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的分享

谢谢观看,再见!

相关推荐
Swift社区2 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht2 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht2 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20242 小时前
Swift 数组
开发语言
stm 学习ing3 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc4 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe5 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin5 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python