使用Python爬取喜欢的小说后在本地存储为txt文件
最近在看小说时因为是在手机上在线观看,百度了半天也没找到可以下载的txt全集,实在没办法只好借助python来帮我完成需求!
准备爬取的小说名称
萧阳叶云舒
目标小说网站 https://www.ly990.com/
准备工具:pycharm
chrome 谷歌浏览器(其它浏览器亦可,谷歌最佳)
python3.7 环境
依赖第三方包: requests BeautifulSoup
如果没有requests包
pip install requests
pip install beautifulsoup4
第一步:先获取所有章节以及所有章节的链接
按F12即可出现开发者界面
第二步:刷新界面后,点击Network
![图2
第二步:在浏览器捕捉的众多需求中,除去js,图片等就找到了目标请求,如下图所示
![图3
可以看到该请求响应的html文件中 可以拿到所有 章节,以及每个章节的子链接
现在问题是我们想要的章节都是在html文件中,如何处理呢?没关系,python中的强大的解析库
BeautifulSoup为我们指明了方向
有关BeautifulSoup不懂的同学可以参考下这篇博文链接
https://blog.csdn.net/qq_36119192/article/details/82952442
我们点开其中一个章节并再次捕捉请求,就可以获得他的请求url:https://www.ly990.com/7_7349/10769168.html;
发现是get请求,该url可以发现是 https://www.ly990.com 和 /7_7349/10769168.html 拼接而成,而/7_7349/10769168.html 就是我们第二步中图片所示的html网页中的信息。我们还可以看看get请求请求头中所需要的信息,其中的path,user-agent:信息需要关注
![图4-1
接下来整理思路 我们只要获取所有的章节以及所有章节的子链接,然后我们循环去请求每个章节(请求头中需要设置path,user-agent等),然后以追加的方式进行写入不就可以实现完整小说的爬取了么?
话不多收直接上代码
代码如下
# coding
:
utf
-
8
import
re
import
os
import
time
import
random
import
requests
from
bs4
import
BeautifulSoup
def
get_contents
(
)
:
# 请求头
headers
=
{
'authority'
:
'www.ly990.com'
,
'method'
:
'GET'
,
'path'
:
'/7_7349/'
,
'scheme'
:
'https'
,
'accept'
:
'text/html,application/xhtml+xml,application/xml;q=0.9,'
'image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3'
,
'accept-encoding'
:
'gzip, deflate, br'
,
'accept-language'
:
'zh-CN,zh;q=0.9'
,
'referer'
:
'https://www.ly990.com/7_7349/5975618.html'
,
'upgrade-insecure-requests'
:
'1'
,
'user-agent'
:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
}
fix_url
=
"https://www.ly990.com"
url
=
fix_url
+
"/7_7349/"
mysession
=
requests
.
session
(
)
# 获取带有所有章节目录的响应
response
=
mysession
.
get
(
url
=
url
,
headers
=
headers
)
# 用BeautifulSoup库解析html文件,获取soup对象
soup
=
BeautifulSoup
(
response
.
content
.
decode
(
encoding
=
"gbk"
)
,
"lxml"
)
# soup对象根据
"dd"
标签即可获取所有的章节
res_list
=
soup
.
select
(
"dd"
)
return
res_list
if
__name__
==
'__main__'
:
# 调用获取所有章节
res_list
=
get_contents
(
)
# user
-
agent列表
user_agent_list
=
[
'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'
,
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3696.400 SLBrowser/10.0.3740.400'
,
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763'
]
# 请求每一章节时所使用的请求头
headers
=
{
'authority'
:
'www.ly990.com'
,
'method'
:
'GET'
,
'path'
:
'/7_7349/'
,
'scheme'
:
'https'
,
'accept'
:
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,'
'image/apng,*/*;q=0.8,application/signed-exchange;v=b3'
,
'accept-encoding'
:
'gzip, deflate, br'
,
'accept-language'
:
'zh-CN,zh;q=0.9'
,
'upgrade-insecure-requests'
:
'1'
,
'user-agent'
:
""
}
# 从章节列表中循环每一个章节进行请求
for
item
in
res_list
:
# soup中的string属性可以直接获取a标签文字
content_name
=
item
.
a
.
string
# soup中的href属性可以直接获取a标签包含的链接
href_content
=
item
.
a
[
'href'
]
# 更新请求头中的path字段信息
headers
.
update
(
{
'path'
:
href_content
}
)
# 简单的反爬虫策略,每次随机选择一个user
-
agent来作为请求头,让服务器以为是不同的浏览器
headers
.
update
(
{
'user-agent'
:
random
.
choice
(
user_agent_list
)
}
)
try
:
# 组合形成最终的章节url
print
(
"url is %s"
%
(
"https://www.ly990.com"
+
href_content
)
)
# 发送
get
请求 超市时间设为
20
秒
resopnse
=
requests
.
get
(
url
=
(
"https://www.ly990.com"
+
href_content
)
,
headers
=
headers
,
timeout
=
20
)
except Exception
as
e
:
print
(
e
)
# 发生超时后随即选择user
-
agent再次发送请求
headers
.
update
(
{
'user-agent'
:
random
.
choice
(
user_agent_list
)
}
)
print
(
"发生异常,url是%s"
%
(
"https://www.ly990.com"
+
href_content
)
)
# 间隔
5
-
8
秒后发送请求(避免被服务器发现请求过于频繁)
time
.
sleep
(
random
.
choice
(
[
5
,
6
,
7
,
8
]
)
)
resopnse
=
requests
.
get
(
url
=
(
"https://www.ly990.com"
+
href_content
)
,
headers
=
headers
,
timeout
=
20
)
# 判断请求状态
if
resopnse
.
status_code not
in
[
200
,
"200"
]
:
print
(
"the status is %s"
%
resopnse
.
status_code
)
# 如果状态不是
200
说明请求过于频繁应休息
5
分钟后再开始
time
.
sleep
(
300
)
# 下面的
try
--
except是为了再次请求之前请求失败的url(状态码不是
200
)
try
:
print
(
"url is %s"
%
(
"https://www.ly990.com"
+
href_content
)
)
resopnse
=
requests
.
get
(
url
=
(
"https://www.ly990.com"
+
href_content
)
,
headers
=
headers
,
timeout
=
20
)
except Exception
as
e
:
headers
.
update
(
{
'user-agent'
:
random
.
choice
(
user_agent_list
)
}
)
print
(
"发生异常,url是%s"
%
(
"https://www.ly990.com"
+
href_content
)
)
time
.
sleep
(
random
.
choice
(
range
(
10
,
30
)
)
)
resopnse
=
requests
.
get
(
url
=
(
"https://www.ly990.com"
+
href_content
)
,
headers
=
headers
,
timeout
=
20
)
# 对于正常拿到章节内容后的响应利用BeautifulSoup获取soup对象
one_soup
=
BeautifulSoup
(
resopnse
.
content
.
decode
(
encoding
=
"gbk"
)
,
"lxml"
)
# 利用find_all方法获取文字内容并将一些不需要的字符进行置空处理
real_content
=
str
(
one_soup
.
find_all
(
attrs
=
{
'id'
:
'content'
}
)
[
0
]
)
.
replace
(
" "
,
""
)
.
replace
(
"
"
,
""
)
.
\
replace
(
r
'
'
,
""
)
.
replace
(
"
,
""
)
.
replace
(
"
"
,
""
)
.
replace
(
r
"</p>"
,
""
)
# 在本地打开test2
.
txt文件来存储爬取到的小说内容
with
open
(
"test2.txt"
,
"a+"
,
encoding
=
'utf-8'
)
as
f
:
# 写入章节目录
f
.
write
(
str
(
"\n"
+
content_name
+
"\n"
)
)
# 写入章节内容
f
.
write
(
real_content
.
replace
(
"\n"
,
""
)
)
# 为了避免请求过于频繁 尽可能降低请求频率
time
.
sleep
(
random
.
choice
(
range
(
10
,
50
)
)
)
运行效果
查看爬取到的test2.txt文件
查看test2.txt文件内容