最近老爸叫我帮他下载些小说到Kindle上,于是乎,根据他的要求去网上找他想看的书,有些人气的还好,像一些没什么人气的,没有多少站点直接提供txt下载,刚好最近刚学了会儿Python爬虫,就想着学以致用

由于刚学,爬取下来的文本格式不是特别好,还不怎么会直接用代码格式化输出文本,因此后期都是靠wordnotepad++的替换+正则搞定的

本次小记记录爬取两个站点的过程

站点一: requests + bs4 + selenium

小说地址: https://www.rzlib.net/b/107/107563/

首先通过开发者控制台来提取所有的URL,在console中输入这一行代码

urls = $$('a'); for (url in urls) console.log ( urls[url].href ); 
crwal_URL

将上述链接,全部复制到剪切版,然后利用这个在线服务,将所有URL从文本中提取出来 https://it365.gitlab.io/zh-cn/link-finder/?zfindlink

将提取出来的URL去除掉非章节的链接然后保存到一个文本中url.txt,方便接下来的爬取,但是存储下来后发现URL顺序是反的(网站的站长是这样设计的)

因为比较菜~~~我就将文本用word打开来反转顺序

下一步是爬取,可能有的人问就这么个普通的站点需要用selenium来模拟?在初步爬取后发现爬取不完整,通过分析发现站点有一段lazyloadjs,直接requests.get有一部分内容获取不到,所以为了方便就用selenium来进行提取source_code.

selenium文档:https://selenium-python.readthedocs.io/
同时需要下载 chromedriver驱动 : http://chromedriver.storage.googleapis.com/index.html

下面来分析这个站点,打开浏览器开发者工具,找到标题和文章内容所在的tag

标题所在div标签的idchapter_title,内容为chapter_content,所以接下来直接解析这两部分,保存下来即可

import requests
from bs4 import BeautifulSoup
from selenium import webdriver
import time
# URL保存的路径
filename_url = r"E:\work\python_learning\spider\url1.txt"
# 你想要保存的位置   
filename = r"E:\work\python_learning\spider\狂枭在都.txt"
# 将URL读取出来并保存在列表中
with open(filename_url) as f:
    urls = f.readlines()
    f.close()
# executable_path为chromedriver驱动位置
browser = webdriver.Chrome(executable_path=r"E:\work\python_learning\spider\chromedriver_win32\chromedriver.exe")
# 依次爬取各个章节
# rstrip()是去掉列表中URL后面的\n
for url in urls:
    browser.get(url.rstrip())
    # 等待浏览器加载完
    browser.implicitly_wait(3)
    time.sleep(3)
    try:
        # browser.page_source为页面源代码
        soup = BeautifulSoup(browser.page_source, 'html.parser')
        soup1 = soup.find(id = "chapter_title")
        soup2 = soup.find(id = "chapter_content")
        # 这里一定要指定编码,windows默认是以gbk编码打开,会出现问题
        # 'a'为附加模式,可以将新内容附加到文件末尾
        # get_text()可以得到tag中的文本内容,包括子孙节点
        with open(filename, 'a', encoding = "utf-8") as f:
            f.write(soup1.get_text() + "\n")
            f.write(soup2.get_text() + "\n")
            f.close()
    except Exception:
        print("爬取错误,请重试")

爬取出来的文本排版不是特别好,可以使用Word进行快速的排版,比如去掉多余的空位,直接在Word中查找替换即可,如果需要解决换行问题,也是查找替换,查找替换成。^t即可。

站点二:requests + bs4

发现了个提供小说下载功能的网站,小说比较全,直接爬取虽然可以,但是排版以及麻烦度高一些,可以从下载方向突破

站点地址: https://www.daocaorenshuwu.com/

我想下载古龙的小说,搜索过后发现这个网站挺全的

先随便下载一本小说,会跳转到一个需要输入验证码的界面

于是我关注公众号,得到了验证码,打开开发者工具,切换到Network后,再提交验证码,提交后发现了一个post请求,和一个get请求,get就是下载这个小说

这样就好办了,我们只需要构造post请求即可得到我们想要的小说

构造URL: 认证界面URL + &verification=12022018

接下来的关键是如何获得每一本小说的下载认证界面地址,同样是用开发者工具

找到认证界面地址,接下来提取出来就完事了。
和站点一一样我们用同样的方法,把页面中的URL全部提取出来,导入到一个文本中URL.txt ,这个文本记录了每本小说介绍界面的URL。

至于如何获得书的名称也差不多,不过这里我偶然发现了一个快速获得书名的标签,书的图片,用审查元素查看书,会发现书图片的alt就是我们需要的名字

这是分析单个页面的过程

分析完了,接下来就上代码

import requests
from bs4 import BeautifulSoup
import os

# 小说介绍界面的URL
file_url = r"E:\work\python_learning\spider\url1.txt"
with open(file_url, encoding='utf-8') as f:
    urls = f.readlines()
    f.close()

# root为小说下载保存时的根目录
root = r'D:\E-book\literature\'

# 伪造浏览器访问
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36",
         "Referer":"https://www.daocaorenshuwu.com/"}

# 爬取小说主程序
for url in urls: 

    # rstrip()去掉url后面的\n   
    r = requests.get(url = url.rstrip(), headers = headers)
    r.encoding = r.apparent_encoding

    # 用BeautifulSoup解析源码
    soup = BeautifulSoup(r.text, 'html.parser')

    # soup_name是拿来提取书名的
    soup_name = soup

    # 提取小说下载界面认证URL
    soup = soup.find('div', 'col-md-6 text-center')
    soup = soup.find('a')
    soup = soup.get('href')
    download_url = 'https://www.daocaorenshuwu.com' + soup + '&verification=12022018'

    # 提取小说名称用于文件保存 
    soup_name = soup_name.find('a', 'media-right')
    soup_name = soup_name.find('img')
    novel_name = soup_name.get('alt')

    # path为小说完整保存路径
    path = root + novel_name + '.zip'
    print(novel_name + '下载中')

    # 下载小说
    if not os.path.exists(root):
        os.mkdir(root)    
    if not os.path.exists(path):
        res = requests.post(url = download_url, headers = headers)
        if (res.status_code == 200):
            with open(path, 'wb') as f:
                f.write(res.content)
                f.close()

完善

第二日完善了一下代码,其实不需要那样通过控制台提取URL,在审查元素观察了一下,发现目的链接在一个tag中,这样就好搞了,贴上修改后的代码

import requests
from bs4 import BeautifulSoup
import os

#root为小说下载保存时的根目录
root = r'D:\E-book\literature\'

#构造浏览器访问
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36",
         "Referer":"https://www.daocaorenshuwu.com/"}

#通过一本小说的介绍界面,爬取作者所有作品介绍界面
#url = 'https://www.daocaorenshuwu.com/txt/11205.html'  这个请自行替换,网址格式需和这个类似
r = requests.get('https://www.daocaorenshuwu.com/txt/10182.html', headers = headers)
r.encoding = r.apparent_encoding

#用BeautifulSoup解析源码
soup = BeautifulSoup(r.text, 'html.parser')

#提取这个作者所有作品介绍界面URL
soup = soup.find_all('div', 'col-md-6 col-xs-12')

#创建介绍界面URL的一个列表
introduction_url = []

for i in range(len(soup)):
    soup[i] = soup[i].find('a')
    
    #提取出的URL为相对路径
    #将其转换为绝对路径
    part_url = soup[i].get('href')
    complete_url = 'https://www.daocaorenshuwu.com' + part_url
    introduction_url.append(complete_url)

#依次爬取列表中的小说介绍界面
#从而获取下载认证界面地址  
for url in introduction_url:   

    #用rstrip()去掉url后面的\n   
    r = requests.get(url = url.rstrip(), headers = headers)
    r.encoding = r.apparent_encoding
    
    #用BeautifulSoup解析源码
    soup = BeautifulSoup(r.text, 'html.parser')
    
    #soup_name用于提取书名
    soup_name = soup
    
    #提取小说下载界面认证URL
    soup = soup.find('div', 'col-md-6 text-center')
    soup = soup.find('a')
    soup = soup.get('href')
    
    #构造可以通过认证的POST请求
    #可以通过开发者工具得出
    #其实verification后面的数字就是让你关注公众号获取的验证码
    download_url = 'https://www.daocaorenshuwu.com' + soup + '&verification=12022018' 
    
    #提取小说名称用于文件保存 
    soup_name = soup_name.find('a', 'media-right')
    soup_name = soup_name.find('img')
    novel_name = soup_name.get('alt')
    
    #path为小说完整保存路径
    path = root + novel_name + '.zip'
    
    #下载小说
    print(novel_name + '下载中')
    if not os.path.exists(root):
        os.mkdir(root)
    if not os.path.exists(path):
        res = requests.post(url = download_url, headers = headers)
        if (res.status_code == 200):
            with open(path, 'wb') as f:
                f.write(res.content)
                f.close()
                print(novel_name + '下載完成')

总结

今天折腾了一天,总体还是比较开心的,学到了如何爬取Lazyload加载的界面,以及一定的问题分析方法,就比如刚开始爬取时,由于没有考虑 readlines()读取到的URL后面会有一个\n,导致404,最让我抓狂的是,Debug的时候使用status_code没有任何反馈,后来单独分析的时候才有显示404,才发现原因,还要继续努力啊,准备好好学习下正则还有lxml,发现是真的好用!


参考文档:
1. https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/#get-text
2. https://requests.readthedocs.io/zh_CN/latest/
3. https://zhuanlan.zhihu.com/p/39165199
4. https://lxml.de/tutorial.html#the-element-class


BobMaster

天地之间有杆秤,那秤砣是老百姓

发表评论

电子邮件地址不会被公开。 必填项已用*标注

我不是机器人*

珍惜当下,陪伴爱你和你爱的人