爬取全本小说并保存到本地!

尊重知识产权,建议阅读原版
本文章仅作示例,请勿用作非法用途

该系列的其他篇目:
系列文章完整目录


效果

全本
如你所见,这是一个完整的小说文档。尽管小说原网站并没有提供下载功能,但我们爬虫却做到了!那么,让我们开始吧~

开始

这篇小说从哪里来?互联网上。我们不妨先去看看网页端效果是怎样的。
打开这个网页,你就会看到完整的章节列表、广告和广告……
这广告……
让我们随便打开一章,右键——查看源代码,看看这些东西背后到底是什么。
在这里插入图片描述
在这里插入图片描述
哇,真的是非常的amazing啊,我们居然一下就找到了我们所需要的小说内容!
在这里插入图片描述
但是兄弟,我们怎么才能获得这些源码呢?
走,我们进入下一步!

获取网页源码

上节课 (没看过的请自行查看) 我们讲过,urllib的urlopen函数可以打开一个网页链接,这一次咱们如法炮制,再试一次。

1
2
3
4
def get_html(url):
response = ur.urlopen(url)
html = response.read().decode("utf-8")
return html

然而当你print一下之后,你会发现:

1
print(get_html(URL))

在这里插入图片描述
纳尼?403 Forbidden?服务器把我们拒绝了!
在这里插入图片描述
  没错,有些服务器因为不希望过多的机器访问造成过大的服务器压力,于是会对爬虫采取拒绝访问的措施。你可能就要问了:为什么他会知道我是一只爬虫而不是正常访问呢?
  事实上,当访问上述链接时,我们的请求头会有一个User-Agent告诉服务器我们是谁。比如说当我们使用浏览器访问时,就会有如下UA:
在这里插入图片描述
  而当使用urllib时,默认的UA是:
在这里插入图片描述
  毫无疑问,服务器一看到它,就把我们bang了……
  但方法总比问题多,这个UA事实上是完全可以更换的。于是我们改一改:

1
2
3
4
5
6
7
8
9
def get_html(url):
head = { # 创造请求头
"Referer": url,
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
request = ur.Request(url, headers=head)# 新建一个request,使用我们构造好的head
response = ur.urlopen(request)# 访问
html = response.read().decode("utf-8")
return html

  你是否发现了一个问题:这一次我们的urlopen改为了打开request而不是url直链。我们来看看文档是怎么说的:

urlopen(url, data=None, timeout=<object object at 0x00634920>, *, cafile=None, capath=None, cadefault=False, context=None)
Open the URL url, which can be either a string or a Request object.

  事实上,当你使用字符串作为参数时,python会自动帮你利用url和默认的数据构造一个Request再完成访问。
  那么结果如何呢?我们再跑一次:
在这里插入图片描述
当当当当!搞定~

提取内容

  有了网页,提取内容就不是什么问题了。通过观察我们可以知道,所有小说内容包裹在<div id=”content”>后面。于是,利用正则表达式 【正则表达式是一种根据特定规则提取文本的技术,您可以先学习之后再回到本章节观看】 ,我们可以写出如下代码:

1
2
re_content = re.compile(r'''并刷新页面。</div>.\s+(.+?)<script language="javascript" type="text/javascript" src="https://mgzs.cdn.bcebos.com/txt.js">''', re.S)
content = re.findall(re_content, html)[0]

  再处理一下特殊字符,让文本好看一点,我们最终得到了这样一个函数:

1
2
3
4
5
6
def get_base_content(html):
re_content = re.compile(r'''并刷新页面。</div>.\s+(.+?)<script language="javascript" type="text/javascript" src="https://mgzs.cdn.bcebos.com/txt.js">''', re.S)
content = re.findall(re_content, html)[0]
content = content.replace("<br />", "\n")
content = content.replace("  &nbsp;\n", "")
return content

在这里插入图片描述
完美!

获取标题

  如法炮制,我们把标题也取一下,看着好看一点:

1
2
3
def get_title(html):
re_title = re.compile(r'<h1 class="title">(.+?)</h1>')
return re.search(re_title, html).group(1)

  这样就完成了一章内容的获取。

批量爬取

  爬完了一章,批量爬取就很简单了。
  观察源代码,我们可以看到所有的下一章链接都被明显标出
网页处理
  所以事实上,我们要做的就是:

那么如何判断是不是最后一章呢?我们看一下网页是怎么处理的:
网页处理
  可以看到,网页版对于最后一张的处理是回到主页。所以我们只需要判断获取到的下一页url是不是一个 ./ 就可以确定了。
  上述完整代码如下:

1
2
3
4
BASE_URL = "https://www.e8zw.com/"
def get_next_page(html):
re_next_page = re.compile(r'<a href="(.+?)">下一[章|页]</a>')
return BASE_URL + re.search(re_next_page, html).group(1)
1
2
3
4
5
6
7
8
9
10
if __name__ == '__main__':
# print(get_base_content(get_html(URL)))
html = ""
url = URL
while True:
html = get_html(url)
text = get_base_content(html)
title = get_title(html)
print(f"{title}\n{text}")
url = get_next_page(html)

保存为文本

读取之后,利用简单的文件操作即可保存啦!
完成,撒花!!!

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import re
import urllib.request as ur
BASE_URL = "https://www.e8zw.com/"
URL = "https://www.e8zw.com/book/416/416756/2235794.html"


def get_html(url):
head = { # 创造请求头
"Referer": url,
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
request = ur.Request(url, headers=head) # 新建一个request,使用我们构造好的head
response = ur.urlopen(request) # 访问
html = response.read().decode("utf-8")
return html


def get_base_content(html):
re_content = re.compile(r'''并刷新页面。</div>.\s+(.+?)<script language="javascript" type="text/javascript" src="https://mgzs.cdn.bcebos.com/txt.js">''', re.S)
content = re.findall(re_content, html)[0]
content = content.replace("<br />", "\n")
content = content.replace("  &nbsp;\n", "")
return content


def get_next_page(html):
re_next_page = re.compile(r'<a href="(.+?)">下一[章|页]</a>')
return BASE_URL + re.search(re_next_page, html).group(1)


def get_title(html):
re_title = re.compile(r'<h1 class="title">(.+?)</h1>')
return re.search(re_title, html).group(1)


if __name__ == '__main__':
html = ""
url = URL
# 保存的文件路径,请修改为你自己的路径
f = open("D:/projects/something_download/神澜奇遇——海龙珠.txt", "w+", encoding="utf-8")
while True:
# 获取每一章html源码
html = get_html(url)
# 获取内容
text = get_base_content(html)
# 获取标题
title = get_title(html)
print(f"{title}\n{text}")
# 获取下一页的链接
url = get_next_page(html)
f.write(f"{title}{text}\n\n")
if url.endswith("./"):
print("全本写入完毕!")
break

f.close()

其他

  • 你可以试着用这样的方法保存其他小说,然后不出意外,有些地方的小说你一定会遇到问题(譬如访问不了,网页中没有内容等等)。我们之后再扯
  • 因网页结构变动等原因,此项目源码可能无法直接运行,如遇此情况请自行酌情修改!!!

后续