本帖最后由 we37 于 2021-1-14 09:41 编辑
上一个帖子链接:新手向的一次完整实战----一键化自动下载整本小说https://www.52pojie.cn/thread-1348178-1-1.html(出处: 吾爱破解论坛)
前天写了点一键下载小说的代码,今天早上翻出来随便运行了一下,想试试看有没有什么bug,结果还真有,不多说,直接上张图
在这里我搜了一个网站没有的书,接下来的反馈是这样,发现了吗,本来我们的逻辑是搜不到之后询问用户是否搜其他的书,但这里变成了回过头再找一遍,做了无用功,而且输入0之后退不出去!!这个bug非常严重,接下来我会梳理一下整个代码,并进行更多的优化,感兴趣的朋友可以自己试试,比较简单,小白向(大佬多指点哈(●’◡’●))。
首先呢,我们要思考一下是哪个函数出现了问题,既然是在交互上出现了问题,那我们就交互函数这找起。看key这个变量,按道理输入0后,会直接经由sys.exit()退出程序,但是事实上并没有执行这个函数,所以肯定是if判断上出现了问题,那么就只有一个原因了:key==0不成功,想到这,我忽然想到input得到的是一个字符串,恍然大悟,用字符串取和数字相比较,肯定不会成功,所以我们可以直接更改,用字符串去匹配字符串不久成功了嘛。直接 if key==’0’: 测试一下
嘿嘿,成功解决。
但是还要解决另外一个问题:我们要重新输入书名,以搜索另外的书,先上代码大家看一下:
[Python] 纯文本查看 复制代码 #搜索且完成后的交互
def start_search(bookname):
while 1:
book_url= api_search(bookname)
if book_url == 0:
print('没搜到,真不好意思')
key = input('还要搜吗,还搜就输1,不搜就0:')
if key != '0': #判断key是否不等于’0’,如果还搜(key=’1’)则执行下面三行
bookname=input('你要搜什么书呢:')
book_url= start_search(bookname) #函数嵌套
return book_url
else:
return 0
else:
return book_url
api_search(bookname) 这个函数和之前的search_bookname(bookname)的意思差不多,可以先这样理解,后面也会讲我的思路。这里用了一个函数嵌套,嵌套完成交互查找任务,不了解函数嵌套的可以自己搜资料查一查,这里就不说了,通过这个函数嵌套,就可以实现我们想要的多次查找功能了。接下来我又想到,既然这个小说都搜不到,那么说明这个书源不太行,做一件事情要把它做好做完整,写代码也是一样,这个问题得修复,不行的书源我们可以替换或者添加,这里我选择了添加,因为两个或者多个书源能提高成功率(毕竟盗版网站总有搜不到的小说)。我选择了笔趣阁这个网站,老牌盗版懂得都懂,直接像上次一样寻找他的搜索url, 很明显的searchkey提醒我们直接再后面接小说名字就行。还是和之前一样,打开开发者工具,根据路径写xpath一步到位(在这里不得不说xpath真的比beautifulsoup好用的多,虽然正则更好用,但它难学啊!)
[Python] 纯文本查看 复制代码 def search_bookname_biquge(bookname):
search_url='http://www.biquge.info/modules/article/search.php?searchkey='
search_result_html=rq.get(search_url+bookname,headers=header)
search_htm=etree.HTML(search_result_html.text)
search_result_url = search_htm.xpath('//td[@class="odd"]/a/@href')
search_result_bookname = search_htm.xpath('//td[@class="odd"]/a/text()')
for i in range(len(search_result_bookname)):
if search_result_bookname[i]==bookname:
result='http://www.biquge.info'+search_result_url[i]
return result
else:
return 0
写if判断的时候发现笔趣阁给的href里的url只是个后缀,那我们就在他前面手动加上一个域名前缀然后返回(这个在之前已经讲过了),这样这个接口就完成了。写完之后发现已经有两个接口了,看着有点杂乱,索性再同一文件夹下另外建一个py文件专门用来存放这些接口,也方便后续接着添加更多的接口,然后在main文件中import回来(文件、变量起名什么的最烦了)。。。因为接口函数中需要用到header,我们也就把header也写在另外的文件里,主文件中引用即可。
然后要解决的是如何在main文件中如何引用这些接口的问题: 先将两个接口函数放在一个列表里方便循环调用,然后再写一个循环调用接口进行查找的函数api_search(bookname),也就是上面的交互中出现的函数:
[Python] 纯文本查看 复制代码
header=搜索接口.header #调用接口文件中的header
search=[搜索接口.search_bookname_dingdian,搜索接口.search_bookname_biquge]
#多个小说网站api接口循环查找
def api_search(bookname):
for i in range(len(search)):
search_result_url=search[i](bookname)
if search_result_url==0:
continue
else:
return search_result_url
else:
return 0
这里应该都看得懂,我就不解释了。写完后还有一些小细节我们要修改,比如说两个小说站点的章节和章节内容页面的布置是否一致呢?这会导致我们所写xpath的改变,但是看过之后发现,两个网站布置其实都一样(盗版网站千篇一律),所以我们的 #获取小说章节 #单个章节写入文件内 这两个函数中的xpath都不需要改变。也比如说笔趣阁提供的搜索接口反馈的小说章节url是否是真实url,在程序使用笔趣阁接口时需不需要调用real_url这个函数。或者说我们还可以优化一下交互,添加更多的中间信息反馈给我们以便观察代码运行情况……那么做完这些,顺完了所有的逻辑之后,是不是就完成了呀?让我们来运行试试。
呕吼,没搜到,但实际上笔趣阁是可以搜到这部小说的,还是有问题,问题发生在哪里呢?思考一下,问题原因应该是只有两个,要不就是代码没get到,要不就是获取到但是没有识别出来,再在开发者工具里看一下
原来是编码问题,问题原因找到了,那我们就要想办法解决,但是两个网站的编码不一样,这该怎么办呢?想办法让程序传回用的是哪个接口,在这里我们用api_search(bookname)函数里的i变量来传回使用的变量,即:
[Python] 纯文本查看 复制代码 #多个小说网站api接口循环查找
def api_search(bookname):
for i in range(len(search)):
search_result_url=search[i](bookname)
if search_result_url==0:
continue
else:
return search_result_url,I #在这里添加i,来传使用的第几个变量
else:
return 0
跟着这个思路,我们也改一下其他函数里的传入传出变量,再添加一个列表变量
[Python] 纯文本查看 复制代码 encode=['gbk','utf-8']
这样我们再测试一下
芜湖~完成 后记: 写的这个代码基本就告一段落,这个代码的最初目的是练手,但是在优化后还是有它的实际应用价值的,虽然做下载器的意义不大(因为有很多小说下载网站,我们可以直接爬取这些网站来下载整本txt,用这个速度太慢而且只有单线程),但是在经过修改比如重写main函数,再写一个GUI面板,实现pc端的在线小说阅读器……有很多思路等着我们去拓宽完整代码附下:
搜索接口
[Python] 纯文本查看 复制代码 import requests as rq
from lxml import etree
import my_fake_useragent as fu
def Uagent():
agent=fu.UserAgent()
return agent.random()
header={
'Referer':'https://www.booktxt.net/',
'User-Agent':Uagent()
}
#按照书名搜索--顶点小说接口
def search_bookname_dingdian(bookname):
search_url = 'https://so.biqusoso.com/s1.php?ie=utf-8&&siteid=booktxt.net&q='
search_result_html=rq.get(search_url+bookname,headers=header)
search_result_html.encoding = 'gbk'
search_htm=etree.HTML(search_result_html.text)
search_result_url = search_htm.xpath('//span[@class="s2"]/a/@href')
search_result_bookname = search_htm.xpath('//span[@class="s2"]/a/text()')
for i in range(len(search_result_bookname)):
if search_result_bookname[i]==bookname:
return search_result_url[i]
return 0
#按照书名搜索--笔趣阁小说接口
def search_bookname_biquge(bookname):
search_url='http://www.biquge.info/modules/article/search.php?searchkey='
search_result_html=rq.get(search_url+bookname,headers=header)
search_result_html.encoding='utf-8'
search_htm=etree.HTML(search_result_html.text)
search_result_url = search_htm.xpath('//td[@class="odd"]/a/@href')
search_result_bookname = search_htm.xpath('//td[@class="odd"]/a/text()')
for i in range(len(search_result_bookname)):
if search_result_bookname[i]==bookname:
result='http://www.biquge.info'+search_result_url[i]
return result
else:
return 0
一键下载小说main文件:
[Python] 纯文本查看 复制代码 import requests as rq
from lxml import etree
import sys
import urllib.request
import 搜索接口
header=搜索接口.header
search=[搜索接口.search_bookname_dingdian,搜索接口.search_bookname_biquge]
encode=['gbk','utf-8']
#搜索且完成后的交互
def start_search(bookname):
while 1:
book_url,u= api_search(bookname) #u是判断使用哪种编码的变量
if book_url == 0:
print('没搜到,真不好意思')
key = input('还要搜吗,还搜就输1,不搜就0:')
if key != '0':
bookname=input('你要搜什么书呢:')
book_url,u = start_search(bookname)
return book_url,u
else:
return 0
else:
return book_url,u
#多个小说网站api接口循环查找
def api_search(bookname):
for i in range(len(search)):
search_result_url=search[i](bookname)
if search_result_url==0:
continue
else:
return search_result_url,i
else:
return 0
#获取真正的url
def real_url(fake_url):
rs = urllib.request.urlopen(fake_url)
return str(rs.geturl())
#获取小说章节
def book_chapter(book_url,u):
html=rq.get(book_url,headers=header)
html.encoding=encode
chapter=etree.HTML(html.text)
chapter_url=chapter.xpath('//div[@id="list"]/dl/dd/a/@href')
return chapter_url
#单个章节写入文件内
def singlechapter_write(singlechapter_url,u):
html=rq.get(singlechapter_url,headers=header)
html.encoding=encode
htm=etree.HTML(html.text)
data=htm.xpath('//*[@id="content"]/text()')
singlechapter_name=htm.xpath('//*[@class="bookname"]/h1/text()')
f=open('1.txt','a',encoding='utf-8')
f.write(' '+singlechapter_name[0]+'\n')
for i in range(len(data)):
f.write(str(data[i])+'\n')
f.write('\n\n\n')
f.close()
if __name__=='__main__':
bookname=input('你要搜什么书呢:')
book_url,u=start_search(bookname)
if book_url== 0:
sys.exit()
print('已找到,正在写入')
try:
if u==0: #判断原url是否是真实url
book_url=real_url(book_url)
chapter_url=book_chapter(book_url,u)
for i in range(10):
singlechapter_url=book_url+chapter_url[i]
singlechapter_write(singlechapter_url,u)
finally:
print('已完成')
|