【源码】协程python小说采集

本文介绍了一个使用Python编写的脚本,利用异步库如aiohttp和aiofiles实现对特定小说网站的章节内容进行并发下载,包括获取目录、解析HTML、异步请求、错误重试和合并章节,以提高下载效率并确保文件完整性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关键组件解释

  1. 导入库: 使用aiohttp进行异步HTTP请求,aiofiles异步文件操作,lxml解析HTML,asyncio管理异步任务。
  2. 准备工作: 定义了目标网站的URL、请求头、字符编码,并检查或创建用于存储下载内容的目录。
  3. 文章列表函数(list_html): 请求小说目录页面,解析HTML以获取小说标题和章节列表。章节URL是通过解析得到的相对链接构造的。
  4. 章节内容函数(res_content): 解析单个章节的页面,获取章节标题和内容。
  5. 异步文本下载(asytxt): 使用aiohttp执行异步GET请求,获取页面内容。
  6. 处理单章节下载(asy_html): 异步请求单个章节的内容,如果下载成功,则保存到指定文件中;如果失败,尝试重新下载或记录下来。
  7. 错误处理和重试(mistake_txt): 在所有章节下载完成后,检查并重新下载之前失败的章节。
  8. 下载并保存内容(downtxt): 使用aiofiles异步写入章节内容到文件中。
  9. 合并小说章节(combine_txt): 将所有下载好的章节文件合并成一个完整的小说文件,并清理临时文件。
  10. 主函数(main): 调用上述函数,使用asyncio运行异步任务,完成整个下载和合并过程。

工作流程

  1. 用户运行脚本,脚本首先获取小说的章节列表和标题。
  2. 通过异步协程并发请求每个章节的内容,大幅提高下载效率。
  3. 每个章节内容被单独保存到临时文件中,如果下载失败,则记录下来以便后续重试。
  4. 下载完成后,脚本尝试重新下载之前失败的章节。
  5. 最后,所有章节被合并成一个完整的小说文件,临时文件被清理。

直接附上源码:

'''
异步协程下载小说
# ---------------------------------------------------------------------------------
# 支持站点:
# 八一中文网(81zw.com)
# 顶点小说(23usp.com)
#笔趣阁(bqg.org,qbiqu.com,52bqg.net等全部站点)
#天籁小说(xs.23sk.com)
# --------------------------------------------------------------------------------
'''
# 2.导入需要用的库文件
import requests,os,time
import asyncio 
import aiohttp,aiofiles
from lxml import etree
# 1.准备网站,headers
URL = "https://www.81zw.com/book/73391/"
gbk = 'utf-8'
headers={
    'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0'
}
if not os.path.exists('./缓存'): #创建缓存文件夹来存放抓取的每一章节文件
        os.mkdir('./缓存')
# 一个返回网页文章列表的函数
def resp_html(url):
    html = requests.get(url,headers=headers)
    html.encoding = gbk
    return html.text
def list_html(url):    
    html = resp_html(url)
    tree=etree.HTML(html)
    zjlist=tree.xpath('//dd//a/@href') #获取每一章的页面相对地址
    title=tree.xpath('//h1/text()')[0] #获取小说名字
    zj_List = []
    for u in zjlist: # 组合链接地址
        zjname=u.split('/')[-1].split('.')[0]
        zj_List.append(int(zjname))
    zj_List.sort() #章节排序
    return title,zj_List
# 多次调用函数
def res_content(html):
    tree=etree.HTML(html)
    title=tree.xpath('//h1/text()')[0] #获取每一章名字
    txtt=tree.xpath('//div[@id="content"]/text()') #获取每一章文本内容
    txt = ""
    for line in txtt: #保存章节内容到文本文件,循环保存每一行
        txt = txt+"\n"+line
    # xs = URL.split('/')[-2] 这个在 “23usp.com” 这个网站有用 .replace("kvpsd https://www.23usp.com/"+xs+"/ 天才一秒记住","")
    #替换掉不用的页面内容
    txt = txt.replace("https://www.81zw.com","").replace("网页版章节内容慢,请下载爱阅小说app阅读最新内容","").replace("网站即将关闭,下载爱阅app免费看最新内容","").replace("免费阅读。","")
    return title,txt
 
async def asytxt(url):
    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=20,ssl=False)) as session :    
        async with session.get(url,headers=headers) as resp:
            resp.encoding = gbk
            html =await resp.text()
            code = resp.status        
    return html,code
# 写一个异步返回text
async def asy_html(nums):
    if os.path.exists('./缓存/{}.txt'.format(nums)): #这里是查重,如果有重复文件就不继续采集了
        print(nums,"------ 已采集,执行下一个")        
    else:  
        url = URL+str(nums)+".html"  # 组合网址
        html,code = await asytxt(url)
    
        if code == 200:
            title,txt = res_content(html)
            await downtxt(nums,title,txt)
        else:
            await asyncio.sleep(1) # 停顿3秒再执行一次
            html,code = await asytxt(url)
            if code == 200:
                title,txt = res_content(html)
                await downtxt(nums,title,txt)
            else: # 采集不成功,就记录下来
                with open('mistake.txt','a',encoding='utf-8') as f:
                    f.write(str(nums)+'\n')
                print(url+' '*10+'-----已记录------第二次下载失败!') 
            #采集错误的记录在这个文件里
# 这个函数把错误网址在合并文件前再重新采集一遍
def mistake_txt():
    if not os.path.exists('./mistake.txt'): #检测错误存储文件,要不没有错误时会报错 
        print("------漂亮------完美------\n -----你的程序没有出错!-----")
    else:
        with open('mistake.txt','r+',encoding='utf-8') as f:
            ff = f.readlines()
            ii = len(ff)
            print("共{}条数据。".format(ii))
            print("-"*30)
            if ii>0:
                i = 1
                for line in ff:
                    print("下面采集第 {} 条数据。".format(i))
                    lines = URL+str(line)+".html"
                    text = resp_html(lines)
                    title,txt = res_content(text)
                    with open('./缓存/{}.txt'.format(line),'w',encoding='UTF-8') as f:
                        f.write('\n'+title+'\n\n'+txt) #保存章节名字到文本文件
                    print(title+' '*10+'下载成功')
                    time.sleep(1)
                print("错误网址已经采集完毕!")
            else:
                print("你没有、没有出错网址!")
    print("*"*30)
 
# 写一个下载文章的函数
async def downtxt(nums,file_name,txt):
    async with aiofiles.open('./缓存/{}.txt'.format(nums),'w',encoding='UTF-8') as f:
        await f.write(file_name+"\n\n"+txt+"\n\n")
        print(file_name +"-"*20+" 已下载完成")
# 写一个合并小说的函数
def combine_txt(title,nums):  #合并所有章节文件函数
    with open('./小说/{}.txt'.format(title),'a',encoding='utf-8') as f:
        for txt in nums: #循环打开缓存中每一章的内容保存到新的文件中
            path='./缓存/{}.txt'.format(txt)  #设置存放路径
            content=open(path,'r',encoding='utf-8').read() #打开每章节文件
            f.write(content)
            os.remove(path) # 删除缓存的txt文件,调试时可以注释掉
        print("已保存 <<"+title+">> 的所有章节!请开心阅读!")
# 写一个主函数
def main():  
    title,zjlist= list_html(URL) # 返回小说名称和文章列表
    try:
        F = False       
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        tasks = [asy_html(nums) for nums in zjlist]
        loop.run_until_complete(asyncio.wait(tasks)) # 激活协程
        loop.close()
        print("*"*30+"")
        #这里检查掉下的错误网址
        mistake_txt()
    except Exception as e :
        F = True
        print(e)
        print("出现错误时就不执行合并")  
    if F == False :
            # 这里合并文件
        combine_txt(title,zjlist)  
    else:
        print("未采集完毕,没有执行查重和合并,请重新运行一次!")
if __name__ == "__main__" :
    start = time.time()
    main()
    end = time.time()
    print(end - start, 's')
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农之家★资源共享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值