看别人用异步请求快的飞起, 忍不住手痒尝试了下, 但过程并不美好, 一不小心就是满屏的"红色警告"
不过成功的喜悦总是让人陶醉的, 这也是学习的魅力吧

先看下运行过程

  • 需要插入一个视频, 暂时不知道视频怎么上传
    好像没啥问题, 1秒十章的下载速度, 挺感人了. 异步运行任务每次只添加20个, 最终耗时相比同步运行快了10多倍

再看下运行结果,

有些章节标题漏掉了, 前几章内容变成了最新章节的内容, 应该是正则匹配出了的偏差, 继续往下看

第一章怎么跑到这么靠后的地方了, 原因不明, 继续往下看

第二章是空的,

继续往下检查了一些章节, 好像没啥问题了. 总体上就是第一章前面多了一些章节, 部分章节为空, 其余部分正常. 应该是前面这几个玩意对应的html结构不同导致的

所有章节内容都保存在“小说名.txt”中了, 进行适当的修改, 处理结果保存在"modify.txt"中

快速浏览了整个文件, 章节内容没啥问题, 顺序也对的上

上代码:

# -*- coding:utf8 -*-
# 从https://www.xbiquge.cc/网站下载小说
# https://www.xbiquge.cc/book/9860/
# https://www.xbiquge.cc/book/9860/7063460.html
# catalog目录,chapter章节
# r'[\u4e00-\u9fa5]+' 1到任意多个汉字
# r'\d{1,10}' 章节链接编号,章节链接在类名为box_con的第2个div中
# r'[\u4e00-\u9fa5]+\d{1,4}[\u4e00-\u9fa5]+ [\u4e00-\u9fa5]+' 小说章节名
import requests
import asyncio
import aiohttp
import json
import re
import time
import os
import sys
from bs4 import BeautifulSoup
from docx import Document
from docx.shared import Cmheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'}url = input('please input url:')
if len(url) < 24:# url = 'https://www.xbiquge.cc/book/9860/'#为了测试方便,设置默认地址url = 'https://www.xbiquge.cc/book/14779/'
rootPath = r'C:\Users\QQ\Desktop\ls\py\{}'
url = url.replace(' ','')# name = '我的微信连三界 狼烟新书'#name和saveCatalog()必须要注释掉一个
# name = '一世兵王 我本疯狂新书'
def getCatalog():def saveCatalog():rep = requests.get(url, headers=headers)print(rep.text[:10])rep.encoding = 'gbk'soup = BeautifulSoup(rep.text, 'lxml')  # 解析title = soup.title.contents[0]print(title)global namename = (re.findall('(.+?) ', title))[0] + ' ' + (re.findall('_(.+?)_', title))[0]  # 小说名print(name)mkDir(path=rootPath.format(name))  # 为之后将要保存的文件创建文件夹f1 = r'C:\Users\QQ\Desktop\ls\py\{}\{}.txt'.format(name, '目录')with open(f1, 'w') as f:f.write(rep.text)saveCatalog()  # 只需要运行一次def findAllChapter():f1 = r'C:\Users\QQ\Desktop\ls\py\{}\{}.txt'.format(name, '目录')f2 = r'C:\Users\QQ\Desktop\ls\py\{}\{}.txt'.format(name, '章节链接')with open(f1, 'r') as f:rep = f.read()soup = BeautifulSoup(rep, 'lxml')s = str(soup.find(id='list'))soup = BeautifulSoup(s, 'lxml')ss = soup.findAll('a')[:]print(ss[:10], ss[-10:])global cul, cnl#print(str(s))cul = re.findall(r'\d{6,8}.html', str(s))  # ChapterUrlList# cnl = re.findall(r'第\d{1,4}章 [\u4e00-\u9fa5]+', str(ss))#ChapterNameList,我的微信连三界,漏掉了第373章 B级任务# cnl = re.findall(r'>(第{0,1}\d{1,4}章 .+?)<', str(s))#ChapterNameList,一世兵王,漏掉了010 章 搂腰算非礼吗?# cnl = re.findall(r'>(第{0,1}\d{1,4} {0,1}章 .+?)<', str(s))#ChapterNameList,一世兵王,漏掉了137章无名字cnl = re.findall(r'.html.>(第?\d{0,4} ?章? ?.*?)</a', str(s))  # 只支持数字编号print(len(ss), len(cul), len(cnl))print(cul[:10], cul[-10:], cnl[:10], cnl[-10:])print('len(cul):', len(cul), 'len(cnl):', len(cnl))'''for i in range(0, len(ss)):# 检查正则表达式,检查完后需注释掉c = str(ss[i])cu = re.search(r'\d{7,8}.html', str(c)).group()cn = c[c.index('.html') + 7:-4]if cu != cul[i] or cn != cnl[i]:print(cu, cul[i], cu == cul[i], cn, cnl[i], cn == cnl[i])break'''if len(cul) == len(cnl):with open(f2, 'w') as f:for u, n in zip(cul, cnl):f.write(u + n + '\n')print('All url and name of chapters from source have been saved in this file:{}'.format(f2))else:print('Rules require changes the regular expression')  # 需要修改正则表达式来适应网页的变化# 如果未保存小说目录信息,则获取并保存,反之,开始提取各个章节的信息findAllChapter()def mkDir(path):if not os.path.exists(path):os.makedirs(path)def missingChapter():# 章节序号为中文时不管用,改进方法:增加中文数字转阿拉伯数字模块new = int(re.search(r'\d{1,4}', cnl[-1]).group())# print('newest chapter: ',cnl[-1])nl = [0]  # chapter number listml = []  # missing chapter number listfor i in range(len(cnl)):nl.append(int(re.search(r'\d{1,4}', cnl[i]).group()))d = nl[i] - nl[i - 1] - 1while d > 0:ml.append(nl[i] - d)# print("missing chapters' number:{}!!!".format(ml[-1]),d)d -= 1return nl'''for i in ml:if str(i) in str(cnl):print(i,True)else:print(i,False)'''def saveChapter():f3 = r'C:\Users\QQ\Desktop\ls\py\{}\{}.txt'.format(name, name)# print(list(zip(cul[1900:],cnl[1900:])))lencnl = len(cnl)nlc = modify()  # Number of loaded chapterswith open(f3, 'a') as f:#  for cu, cn in zip(cul[nlc:], cnl[nlc:]):  # 开始位置根据实际情况调整#  此处将同步更改为异步async def getreptext(session, cu, cn, serialcontent):async with session.get(url + cu, headers=headers) as rep:rep = await rep.text(encoding = 'gbk')serialcontent.update(await cache(rep, cu, cn))return serialcontentasync def cache(rep, cu, cn, nlc=nlc):start = time.perf_counter()content = ''for s in rep.splitlines():if 'span' in s:# print('this line contain <span>')continue  # 网页中的这一行存在问题test1 = re.findall(r'&nbsp;&nbsp;&nbsp;&nbsp;(.+)<', s)if test1:content += test1[0] + '\n'if len(content) > 1200:  # 章节字数少于1200则不写入文件content += '\n'print('contents has been writen to cache which from : {} {}'.format(cu, cn))else:print(content)content = '\n'print('There are problems in this chapter : {} {} !!!'.format(cu, cn))#continueend = time.perf_counter()rt = end - start  # run timetrt = rt * (lencnl - nlc) # total remaining timeprint('estimate rest of runtime : {} minutes {} seconds'.format(trt // 60, trt%60))nlc += 1return {cu: content}# 对tasks分组,避免短时间内访问量过大导致爬虫被封pervolume = 20  # workload. setting how much work would be done per iterationsize = lencnl - nlctail = size % pervolumeiterations = size // pervolumeasync def getandcache(cu, cn, serialcontent):async with aiohttp.ClientSession() as session:await getreptext(session, cu, cn, serialcontent)for i in range(iterations):serialcontent = {}tasks = [asyncio.ensure_future(getandcache(cu, cn, serialcontent)) for cu, cn in zip(cul[nlc:nlc+pervolume], cnl[nlc:nlc+pervolume])]loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))for i in cul[nlc: nlc+pervolume]:f.write(serialcontent[i])#print(len(serialcontent), serialcontent)nlc = nlc + pervolume  #del serialcontentpervolume = tailif pervolume:serialcontent = {}tasks = [asyncio.ensure_future(getandcache(cu, cn, serialcontent)) for cu, cn in zip(cul[nlc:nlc + pervolume], cnl[nlc:nlc + pervolume])]loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))for i in cul[nlc: nlc+pervolume]:f.write(serialcontent[i])#print(len(serialcontent), serialcontent)del serialcontentdef runlog():# 记录每次运行时长、运行时间、已保存的章节、缺失章节、增加的章节等信息passdef modify():# 检查文件中是否有广告信息、多余字符、空章节。根据检查结果对saveChapter()进行完善f3 = r'C:\Users\QQ\Desktop\ls\py\{}\{}.txt'.format(name, name)f4 = r'C:\Users\QQ\Desktop\ls\py\{}\{}.txt'.format(name, 'modify')if not os.path.exists(f3):with open(f3, 'w') as f:passprint('saved such file : {}'.format(f3))else:print('no such file : {}'.format(f3))with open(f3, 'r') as f, open(f4, 'w') as fs:cc(f)c = 0li = f.readlines()# print(type(li),len(li))for n, i in enumerate(li):if 'span' in i:continuefs.write(i)if i == '\n' and n < len(li) - 1:c += 1if '第' not in li[n + 1] and '章' not in li[n + 1]:# print(cnl[c])fs.write(cnl[c] + '\n')pass#print('c :', c, 'cnl[c] :', cnl[c], 'cnl[c-1] :', cnl[c - 1])return cdef cc(file):# count charactersf00 = r'C:\Users\QQ\Desktop\ls\py\{}\{}.txt'.format(name, 'other characters')hs0 = {3: '·、【】!¥—~……();‘’:“”《》,。?、',4: ''' `~!@#$%^&*()_+-={}|:%"<>?[]\;',./×'''}hs = {1: 0,  # 中文2: 0,  # english letter3: 0,  # 中文标点符号4: 0,  # english punctuation marks5: 0,  # 数字6: 0,  # 行数7: 0,  # 中文字数占总字符数的比例}string = file.read()with open(f00, 'w') as f:for i in string:if 19968 <= ord(i) <= 40869:hs[1] += 1elif 65 <= ord(i) <= 90 or 97 <= ord(i) <= 122:hs[2] += 1elif i in hs0[3]:hs[3] += 1elif i in hs0[4]:hs[4] += 1elif 48 <= ord(i) <= 57:hs[5] += 1elif i == '\n':hs[6] += 1else:f.write(i)  # 检查是否有其他特殊字符,应该是没有的。如果有,可能乱码了length = len(string)hs[7] = hs[1] / (length + 1)  # len+1避免报错ZeroDivisionError: division by zerofile.seek(0)l = ['中文', 'english letter', '中文标点符号', 'english punctuation marks', '数字', '行数', '中文字数占总字符数的比例']for i in range(7):if i == 6:print('{} : {:.2%}'.format(l[i], hs[i + 1]))else:print('{} : {:.2f}万'.format(l[i], hs[i + 1] / 10000))print('\n总字符数:{:.0f}万.平均每章节{:.0f}字,平均每个段落{:.0f}字\n'.format(length / 10000, length / (len(cnl) + 1), length / (hs[6] + 1)))  # can't division by zerodef main():start = time.perf_counter()getCatalog()# missingChapter()saveChapter()modify()end = time.perf_counter()print('total time consuming : ', (end - start) // 60, 'minutes', (end - start) % 60, 'seconds')
main()

几个需要注意的地方

  1. 因为异步运行有随机性, 所以不能将getreptext获取到的文本直接写入"小说名.txt"文件, 否则章节顺序就乱了. 解决方法有很多, 比如
  • 把每个章节分别保存到本地, 全保存下来后, 按顺序读取每个文件并写入"小说名.txt"文件
  • 将章节暂时储存在字典中, 本文定义了一个字典"serialcontent", 每次将pervolume个章节的文本内容更新到serialcontent中, 按章节顺序取出serialcontent的value, 将value写入"小说名.txt"
  • 其实就是对每个getreptext得到的content做一个标记, 有了标记后面的事情就好办了
  1. 异步请求可以一次性访问所有章节链接, 但这样做毫无疑问的是网站会把你封掉
  • 所以需要对访问任务进行分组, 每次并行pervolume个任务, 建议每次20个章
  • 实在有刚需, 可以尝试伪装IP. 伪装IP大概分三类:
  • 服务器知道你伪装了IP, 而且知道你真实的IP
  • 服务器知道你伪装了IP, 不知道你真实的IP
  • 服务器难以辨别你是否伪装了IP

python爬虫 小说下载 异步相关推荐

  1. python爬虫小说下载到txt文档_python 爬取网络小说 清洗 并下载至txt文件

    什么是爬虫 网络爬虫,也叫网络蜘蛛(spider),是一种用来自动浏览万维网的网络机器人.其目的一般为编纂网络索引. 网络搜索引擎等站点通过爬虫软件更新自身的网站内容或其对其他网站的索引.网络爬虫可以 ...

  2. python爬虫 小说下载

    笔趣阁的网页结构比较简单,但也有点乱,需要注意细节. 需要增加运行日志 #-*- coding:utf8 -*- #从https://www.xbiquge.cc/网站下载小说 #https://ww ...

  3. Python爬虫实战——下载小说

    Python爬虫实战--下载小说 前言 第三方库的安装 示例代码 效果演示 结尾 前言 使用requests库下载开源网站的小说 注意:本文仅用于学习交流,禁止用于盈利或侵权行为. 操作系统:wind ...

  4. python爬虫-小说《大江大河》

    python爬虫-小说<大江大河> 最近看了电视剧大江大河电视剧,挺好看的,就在网上找找小说看. 最近看了电视剧大江大河电视剧,挺好看的,就在网上找找小说看. 大江大河小说地址:傲宇中文网 ...

  5. python爬虫下载-python爬虫之下载文件的方式总结以及程序实例

    python爬虫之下载文件的方式以及下载实例 目录 第一种方法:urlretrieve方法下载 第二种方法:request download 第三种方法:视频文件.大型文件下载 实战演示 第一种方法: ...

  6. python 下载文件-python爬虫之下载文件的方式总结以及程序实例

    python爬虫之下载文件的方式以及下载实例 目录 第一种方法:urlretrieve方法下载 第二种方法:request download 第三种方法:视频文件.大型文件下载 实战演示 第一种方法: ...

  7. python爬虫批量下载“简谱”

    python讨论qq群:996113038 导语: 上次发过一篇关于"python打造电子琴"的文章,从阅读量来看,我们公众号的粉丝里面还是有很多对音乐感兴趣的朋友的.于是,今天我 ...

  8. 新一配:perl循环调用python爬虫批量下载喜马拉雅音频

    新一配:perl循环调用python爬虫批量下载喜马拉雅音频 手机下载喜马拉雅音频后,获得的音频文件虽然可以转成mp3格式,但其文件名却是一长串字符串,无法辨别是哪一集,网上找了各种工具,都有局限性, ...

  9. 2021-04-01裁判文书网数据python爬虫更新下载

    长期持续更新数据 2020-11-08裁判文书网数据python爬虫更新下载添加链接描述 截至3月已从数据库中下载1亿1200万条裁判文书数据,有需要数据的伙伴可以(。・∀・)ノ゙嗨前台QQ7900- ...

最新文章

  1. java filefilter递归_Java中的递归+文件过滤器
  2. [RHEL5企业级Linux服务攻略]--第2季 Samba服务全攻略答疑贴
  3. 【IT资讯】Linus Torvalds:我们都老了,但Linux维护真的很难找
  4. 2020 CSP-S 游记
  5. python去停用词用nltk_【NLTK】安装和使用NLTK分词和去停词
  6. 1.5编程基础之循环控制 24 正常血压
  7. Redis小记——数据结构
  8. 配置 MAC地址表实现绑定和过滤
  9. 算法笔记_面试题_2.移动零(将数组的的0元素移到末尾)
  10. 使用oracle修改/etc/passwd /etc/passwd默认SHELL被修改后,无法...
  11. 第三方登录 steam_如何在Steam中激活第三方游戏代码
  12. 联想昭阳E46A笔记本的一个问题
  13. 100-1000的水仙花数 有哪些?
  14. C语言中格式输出二进制的两种方法
  15. [LibreOJ 3124]【CTS2019】氪金手游【容斥原理】【概率】【树形DP】
  16. 安卓视线可锁定首行和首列的表格视图
  17. 如何使用TF卡和阿里云盘给surface pro扩容
  18. 树莓派:10行代码体验红外检测
  19. u盘名称霸气_皓影改装点点滴滴之记忆U盘详细解说!
  20. sql注入基础(一)

热门文章

  1. javascript技巧——消灭星星
  2. 数字孪生应用相关调研:智慧城市中的车联网(十余篇论文简介)
  3. 从这些相声艺人身上,我们能学到什么?
  4. centOs6.5版Linux系统中搭建Samba服务(附搭建Samba设置及相关配置参考)
  5. lanm中cdn之varnish服务的搭建
  6. 2022年高处安装、维护、拆除考试技巧及高处安装、维护、拆除作业考试题库
  7. 论“面向对象”之下的爱情观
  8. Bzoj1479: [Nerrc1997]Puncher打孔机
  9. 成功解决~请使用IE浏览器(兼容模式)
  10. RabbitMq的安装与配置(Windows平台下)