导语

正好最近收到我之前在某论坛上发表的爬取小说网站的文章回帖,便乘着有闲暇时间重写一遍,贴上分析过程发表于此,技术尚浅,大佬勿喷,如有问题欢迎留言评论。


正文

变量声明

这里博主采用的是面向对象的编程方法。
首先,我们先定义__init__方法,声明接下来我们可能用到的一些基本的变量。

在此,我们初始化novel_data、headers两个字典,并声明字符串start_url。其中字典novel_data用于收集存储爬取到的小说名称/作者/小说地址等信息,start_url为爬虫最开始工作的网址。

def __init__(self):
    self.novel_data = {}
    self.start_url = "https://www.ddxsku.com/full.html"
    self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"}

小说信息采集

初始化基本的变量之后,我们开始对小说网站进行分析,大致思路是先收集小说基本信息(小说名称/作者/地址等),然后在根据返回的数据爬取小说内容,最后保存即可。

通过观察网站 https://www.ddxsku.com/full.html 我们发现小说还挺多,网页不止一页,于是想要爬取全站的完结小说的话,我们首先需要获取网站的页数,然后打入循环,循环遍历每一页收集小说数据。
1.png

通过查看元素我们发现,定义页数的元素是唯一的(Html中 ID属性值一般情况是唯一的),我们只需要通过简单的re正则表达式匹配提取即可。

start_response = requests.get(self.start_url, headers=self.headers)
total_page = re.search(r'<em id="pagestats">1/(.+)</em>', start_response.text).group(1)

当然了,在开始页数匹配之前别忘了打开网页。但是光有页数可不行(不然咋打开网址呢?),之后我们得分析url的变化规律。

通过简单的几页网址分析我们不难发现,除了参数page其余都是不变的,而page正是页数,所以我们现在定义一个列表来存储每一页小说导航的地址。

novel_navigation_urls = [fr"http://www.ddxsku.com/modules/article/articlelist.php?fullflag=1&page={i}" for i in range(1, int(total_page)+1)]

这里博主使用的是列表推导式,这里不展开多讲,有兴趣可以自行百度。当然了,你也可以直接使用for循环页数,将页数放到网址字符串相应的位置即可。

for page in range(1, int(total_page)+1):
    url = fr"http://www.ddxsku.com/modules/article/articlelist.php?fullflag=1&page={page}"
    pass

于此,我们便到了最令人激动的环节,逐页的打开小说导航网址,并收集小说基本内容。
2.png

通过分析刚才打开的网页元素我们发现,小说的目录页网址是存储在一个有规律的元素下的,博主依旧打算使用正则表达式来匹配。

novel_index_urls = re.findall(r'<td class="L"><a href="(.+)" title=".+" target="_blank">.+</a></td>', novel_navigation_response.text)

收集到小说目录页的网址,我们再逐项打开小说目录页网址,小说的名称/作者/每一章的标题与网址即可。

for novel_index_url in novel_index_urls:
    novel_index_response = requests.get(novel_index_url, headers=self.headers)
    novel_index_response.encoding = "utf-8"

    novel_name = re.search(fr'.+<a href="http://www.ddxsku.com/xiaoshuo/\d+\.html">(.+)</a>.+', novel_index_response.text).group(1)
    novel_author = re.search(r'<dd><h3>作者:(.+)</h3><br>.+</h3></dd>', novel_index_response.text).group(1)
    self.novel_data = {novel_name: [("novel_author", novel_author)]}
    print("Collecting novel:  《%s》--%s" % (novel_name, novel_author))

    index_soup = BeautifulSoup(novel_index_response.text, "html.parser")
    novel_text_urls = index_soup.find_all("td", class_="L")
    for each in novel_text_urls:
        chapters_title = each.text
        chapters_url = each.a["href"]
        self.novel_data[novel_name].append((chapters_title, chapters_url))
    sleep(1)

收集的方法与代码跟之前的大同小异,大多的都是使用的正则表达式匹配的(这足以见得正则表达式对于一个合格的爬客是多那么的重要!)。

独有收集小说章节标题与网址不同,博主这里使用的是bs4库中的Beautifulsoup进行匹配章节标题与网址的。这同样见得,Beautifulsoup也是作为一个合格的爬客所必须掌握的。这里不做多讲,具体用法,请自行百度(或许博主有时间会写一篇关于这两门技术的文章)。

至于novel_index_response.encoding = "utf-8"的作用,则是将网页转换为utf-8编码,避免带来由于编码带来的不变要的麻烦(我们需要提取的存在中文字符)

并且博主是将收集到的数据以字典的形式存储小说信息,以小说名称为健,小说作者与章节名称/网址作为值,其中章节名称与网址以元组(章节名称,章节地址)的形式存储。

小说内容下载

通过上面的代码,我们已经能够收集小说的一些基本信息,接下来我们就只需要下载保存即可。

为了方便查看我们下载到的小说,我们需要更改一下Python的运行工作路径。

for name in self.novel_data:
    count = 0
    print("Downloading:  《%s》" % name, end="\n"*2)

    work_path = r"C:/Users/Administrator/Desktop/NovelCopy/%s-%s" % (name, self.novel_data[name][0][1])
    if not os.path.exists(work_path):
        os.makedirs(work_path)
        os.chdir(work_path)
    else:
        os.chdir(work_path)

对于self.novel_dataname[1],不知道客官可否还记得之前我们的小说数据保存形式

{"name": [("novel_author", novel_author), (chapters_title, chapters_url)]}

所以你大概知道self.novel_dataname[1]索引的结果是什么了吧?

需要下载小说自然的,我们需要获取小说的文本内容。通过查看元素我们知道,文本内容都是保存在ID为contents的元素下的,为了能够简单快速的获取文本,博主这里选择的是使用Beautifulsoup来定位元素,然后通过方法 text 获取文本即可。

而至于count的作用,则是方便小说文本文件的排序(鬼晓得有没有懒作者连第几章都不写呢?)
3.png

for chapter_data in self.novel_data[name][1:]:
    count += 1
    chapter_response = requests.get(chapter_data[1], headers=self.headers)
    chapter_response.encoding = "utf-8"

    chapter_soup = BeautifulSoup(chapter_response.text, "html.parser")
    chapter_text = chapter_soup.find("dd", id="contents")
    with open("%d-%s.txt" % (count, chapter_data[0]), "w", encoding="utf-8") as f:
        f.write(chapter_text.text)

结语

至此,一篇简单的小说网站爬取分析就算是结束了。由于博主之前少有写关于过程分析的,都是闷声自己干,所以写得不好之处还望见谅,有建议请在下方评论区留言,当然有问题也欢迎留言哦!

完整代码

点击下方模块即可跳转到 项目代码 界面

Last modification:May 2, 2021
如果觉得我的文章对你有用,请随意赞赏