前段时间,因为暑假休假以及回来后忙于工作,所以CSDN博客一直未更新,大家是不是忘了我了。虽然工作忙,但是每天下班,还是会看一眼CSDN的APP,天天都有新粉丝的加入,我很欣慰,说明我在这个平台所作的努力没有白费,大家的支持就是我继续创作的动力。
这两天稍微空一点,我更新了下最新的爬取《我靠打爆学霸兑换黑科技》小说的爬取代码,这个小说不出意外的话,我会一直更新代码,至于爬取网站来自如下链接:
https://m.shuhaige.net/shu_243116.htmlhttps://m.shuhaige.net/shu_243116.html现在免费的小说网站越来越少,存在的时间也越来越短了,望大家且看且珍惜,至于这个网站的其他小说资源,我没有测试是否可用,有兴趣的朋友,请替换最后的倒数第三句代码的链接,测试下看看。
python
if __name__ == "__main__":
main_url = "https://m.shuhaige.net/243116/"# 如需其他小说,请搜索网站后,替换这句里的链接看看
start = 1 #选择章节数
main(main_url,start)
另外提一句,如果你运行了我的代码,可能发觉很慢,因为我加了每爬取一章有3秒延迟,在代码开头的全局变量里设置,还是那句话,给这些免费的资源网站一点活路,这年头大家都不容易,我今天已完整爬取过一次这篇小说,我会把代码和小说上传到百度云盘方便大家下载,省的大家重复爬取,增加网站负担,同时有些朋友前面的章节已经看过了,所以我加了个可以选择章节的功能,如上代码所示,因为这个小说里有特殊的多余章节,所以可能章节数对不上,但是误差也就两章,最多就是往前多爬两章,大家可以忽略不记,按实际写入即可,如果你比较讲究,可以加上两章,比如现在最新的章节是1189+2=1191,把start = 1191,爬取的就是最后一章了,当然如果碰到爬取的章节,在特殊章节的前面就有可能有问题,大家按程序实际运行的显示章节计算即可。还有一个问题就是如果是选择跳过章节,可能程序开始运行时,需要等待一会,因为程序在爬取page页面,不是python死机,因为这个小说网站不是把所有的章节放一起,而是每页50个章节的链接页面,所以会有这个问题,等以后有机会,我会优化这个问题,另外这个网站好像还有随机的反爬策略,触发条件未知,也可能是人为监控,反正我的代码有从指定章节爬取的功能,不是什么大问题,只要设置好上面的start变量+2,保存代码后,重新运行程序,就能在原始文件上继续写入。
如果你不想学习爬虫代码,你可以不看以下内容,直接使用文末的代码。
我来说说代码的优化问题,首先,因为这个网站的构架比较特殊,如上所说它的各章节链接做成了50个章节一页,所以爬取的页面就比较多,所以我优化了requests和BeautifulSoup的调用,把重复的代码做成一个函数,减少代码量,如下:
python
def request_get(url,headers,soup_select,encode_mode="utf-8"):
res = requests.get(url,headers = headers)
res = res.content.decode(encode_mode)
soup = bs(res,"html.parser")
datas = []
for each_soup_select in soup_select:
datas.append(soup.select(each_soup_select))
return datas
其中url是待爬取网页链接,headers网络请求头,soup_select是网页里BeautifulSoup需要读取爬取的标签头,因为我的程序中有一个函数getNextPageText中,需要连续查询两个不同的标签头,所以我在此函数中,设置了for循环来历遍所有的soup_select数组的元素,比如这样:["div.content p","div.pager a"],并放入BeautufulSoup的解析器中,提取出所需的元素内容。最后的encode_mode="utf-8"默认是"utf-8",代表这个网页的解码方式是"UTF-8",这个在调用函数时,可以自定义为其他,比如"GBK"等,请按你要爬取的网页源代码头自己分析,可以找到网页源代码中如下这样的地方查看:
html
<head><meta charset="UTF-8"></head>
其中的charset后的UTF-8就是解码方式。
此外,我还定义了一个清洗小说文本中的杂乱字符的函数,如这样,可以在replace_text数组中加入自己想去掉的文本,注意加入一个就要在这一行的字符串后加英文逗号。
python
def replaceTxt(text):
replace_text = ['<p>',
'</p>',
'《我靠打爆学霸兑换黑科技》无错的章节将持续在书海阁小说网小说网更新,站内无任何广告,还请大家收藏和推荐书海阁小说网!',
'喜欢我靠打爆学霸兑换黑科技请大家收藏:(m.shuhaige.net)我靠打爆学霸兑换黑科技书海阁小说网更新速度全网最快。',
'本小章还未完,请点击下一页继续阅读后面精彩内容!',
'小主,这个章节后面还有哦,请点击下一页继续阅读,后面更精彩!',
'这章没有结束,请点击下一页继续阅读!'
]
for rep in replace_text:
text = text.replace(rep,"")
return text
因为有些章节有多页(通常是两页),如下图所示:
正常无分页的章节,如下:
而有些章节只显示一页 ,先分析下网页源码,如下:
根据上面分析,所以我编写了一个自迭代函数来处理这种情况,每句代码我都写了注释,如下:
python
def getNextPageText(next_page_tag,headers,name,page_count):
page = next_page_tag[1]# 这里是初次调用或上一次迭代调用本函数的前的request_get返回的datas[1],也就是解析"div.pager a"标签的结果,其中有判断是否有如上图的"下一页"或"下一章"
nextpage_tag = page[2]# 这个就是data[1]中解析"div.pager a"标签的返回结果,也就是a标签数组中的第三个,这个元素可能是"下一页"或"下一章"
if nextpage_tag.text == "下一页":# 单独处理下一页的情况
time.sleep(delay_time-1)# 延时
page_count += 1# 给每章的页数计数,并返回,方便之后在信息中输出
url = "https://m.shuhaige.net" + nextpage_tag['href']# 取出a标签中的href下一页或下一章链接
datas = request_get(url,headers,["div.content p","div.pager a"]) #解析新的分页中的小说内容和下一页/章元素标签
nextpage_text,page_count = getNextPageText(datas,headers,name,page_count)#重复迭代调用此函数,判断是否还有新的分页,比如第三页
text = "\n".join(map(str,datas[0])) #合并读取到的小说<P>元素内容
text = text + nextpage_text #把迭代获取到的新分页内容合并到老分页中
return text,page_count#返回所有分页的小说文本和分页数
return "",page_count#如果没有新的分页或只有下一章,就直接返回空字符
最后,在getText函数中,我没有像以前一样,循环拼接request返回的datas,而是使用了如下代码:
python
text = name + "\n" + "\n".join(map(str,datas[0])) + nextpage_text# nextpage_text处理了一个章节中有多页的情况
这样调用应该更高效,顺便一提,datas是一个tag类型数组,所以要转换成str,需要调用map函数,最后用join链接每行小说文本,并在每行加入一个"\n"软回车。
基本的代码解释到这里,有问题的小伙伴如果有什么不懂,可以私信或者评论区联系我,看到后会回复。
最后放上全部代码和最新小说链接:
python
import requests
from bs4 import BeautifulSoup as bs
import time
delay_time = 3# 延时3秒,防止被和谐,请各位手下留情,人家开网站也不容易,现在能找到一个免费站点不容易
file_name = "11.小说爬取\书海阁\我靠打爆学霸兑换黑科技_20240828.txt"
count = 0
def request_get(url,headers,soup_select,encode_mode="utf-8"):
res = requests.get(url,headers = headers)
res = res.content.decode(encode_mode)
soup = bs(res,"html.parser")
datas = []
for each_soup_select in soup_select:
datas.append(soup.select(each_soup_select))
return datas
def replaceTxt(text):
replace_text = ['<p>',
'</p>',
'《我靠打爆学霸兑换黑科技》无错的章节将持续在书海阁小说网小说网更新,站内无任何广告,还请大家收藏和推荐书海阁小说网!',
'喜欢我靠打爆学霸兑换黑科技请大家收藏:(m.shuhaige.net)我靠打爆学霸兑换黑科技书海阁小说网更新速度全网最快。',
'本小章还未完,请点击下一页继续阅读后面精彩内容!',
'小主,这个章节后面还有哦,请点击下一页继续阅读,后面更精彩!',
'这章没有结束,请点击下一页继续阅读!'
]
for rep in replace_text:
text = text.replace(rep,"")
return text
def getPage(main_url,start):
headers = {
'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'referer':'https://m.shuhaige.net/shu_243116.html',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0'
}
page_urls = request_get(main_url,headers,["span.pagenum select option"])
page_urls = page_urls[0]
sum_text_size = 0
for page_url in page_urls:
p_url = "https://m.shuhaige.net" + page_url['value']
sum_text_size = sum_text_size + getUrl(p_url,headers,start)
return sum_text_size
def getUrl(page_url,headers,start):
urls = request_get(page_url,headers,["ul.read li a"])
urls = urls[0]
sum_page_size = 0
for url in urls:
global count
count += 1
if count >= start:
time.sleep(delay_time) #手工延时,防止被和谐
name = url.text
ul = "https://m.shuhaige.net" + url['href']
start_time = time.time()
text_size,page_count = getText(name,ul)
if page_count > 1:
page_txt = f"共{page_count}页,"
else:
page_txt = ""
print(name,f"已下载完成!(耗时:{time.time() - start_time}秒,{page_txt}字数:{text_size})")
else:
text_size = 0
sum_page_size += text_size
return sum_page_size
def getNextPageText(next_page_tag,headers,name,page_count):
page = next_page_tag[1]
nextpage_tag = page[2]
if nextpage_tag.text == "下一页":
time.sleep(delay_time-1)
page_count += 1
url = "https://m.shuhaige.net" + nextpage_tag['href']
datas = request_get(url,headers,["div.content p","div.pager a"])
nextpage_text,page_count = getNextPageText(datas,headers,name,page_count)
text = "\n".join(map(str,datas[0]))
text = text + nextpage_text
return text,page_count
return "",page_count
def getText(name,url):
headers = {
'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'referer':'https://m.shuhaige.net/shu_243116.html',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0'
}
datas = request_get(url,headers,["div.content p","div.pager a"])
page_count = 1
nextpage_text,page_count = getNextPageText(datas,headers,name,page_count)
text = name + "\n" + "\n".join(map(str,datas[0])) + nextpage_text
text = replaceTxt(text)
with open(file_name,"a+",encoding="utf8") as f:
f.write(text + "\n")
return len(text),page_count
def main(main_url,start=1):
start_time = time.time()
sum_text_size = getPage(main_url,start)
runtime = (time.time()-start_time) / 60
print(f"已全部下载!(共耗时:{runtime:.2f}分钟,总字数:{sum_text_size})")
if __name__ == "__main__":
main_url = "https://m.shuhaige.net/243116/"# 如需其他小说,请搜索网站后,替换这句里的链接看看
start = 1 #选择章节数
main(main_url,start)
小说和代码文件链接如下:
通过百度网盘分享的文件:书海阁
链接:https://pan.baidu.com/s/1kClUGhI1ZBQTN--IkrwznQ?pwd=8888
提取码:8888
--来自百度网盘超级会员V8的分享
谢谢观看,再见!