Python爬虫系列-爬取小说20240823更新(Request方法的优化)

前段时间,因为暑假休假以及回来后忙于工作,所以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的分享

谢谢观看,再见!

相关推荐
铁打的阿秀10 分钟前
okhttp 报java.lang.IllegalStateException: closed
java·开发语言·okhttp
Zhen (Evan) Wang15 分钟前
What is new in C# 7,8,9,10
开发语言·c#
有杨既安然25 分钟前
Python数据分析与可视化基础教程
开发语言·python·信息可视化·数据分析·excel
van叶~26 分钟前
移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——12.二叉树(习题)
开发语言·c++·算法
丨我是张先生丨32 分钟前
Python 导出Excel
python·excel
`北极星34 分钟前
应用程序已被 Java 安全阻止:Java 安全中的添加的例外站点如何对所有用户生效
java·开发语言·安全
蒙娜丽宁36 分钟前
深入解析Go语言的类型方法、接口与反射
java·开发语言·golang·go
会敲代码的小张37 分钟前
设计模式-外观模式
java·开发语言·后端·设计模式·外观模式
计算机学姐39 分钟前
基于python+django+vue的视频点播管理系统
vue.js·python·mysql·django·pip·web3.py·ipython
AI让世界更懂你42 分钟前
漫谈设计模式 [9]:外观模式
python·设计模式·外观模式