吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1978|回复: 2
收起左侧

[Python 转载] 新手向的一次完整实战----一键化自动下载整本小说 (二)对bug的修复和代码优化

[复制链接]
we37 发表于 2021-1-13 23:17
本帖最后由 we37 于 2021-1-14 09:41 编辑

  上一个帖子链接:新手向的一次完整实战----一键化自动下载整本小说https://www.52pojie.cn/thread-1348178-1-1.html(出处: 吾爱破解论坛)
  前天写了点一键下载小说的代码,今天早上翻出来随便运行了一下,想试试看有没有什么bug,结果还真有,不多说,直接上张图

1.png
在这里我搜了一个网站没有的书,接下来的反馈是这样,发现了吗,本来我们的逻辑是搜不到之后询问用户是否搜其他的书,但这里变成了回过头再找一遍,做了无用功,而且输入0之后退不出去!!这个bug非常严重,接下来我会梳理一下整个代码,并进行更多的优化,感兴趣的朋友可以自己试试,比较简单,小白向(大佬多指点哈(●’◡’●))。
  首先呢,我们要思考一下是哪个函数出现了问题,既然是在交互上出现了问题,那我们就交互函数这找起。看key这个变量,按道理输入0后,会直接经由sys.exit()退出程序,但是事实上并没有执行这个函数,所以肯定是if判断上出现了问题,那么就只有一个原因了:key==0不成功,想到这,我忽然想到input得到的是一个字符串,恍然大悟,用字符串取和数字相比较,肯定不会成功,所以我们可以直接更改,用字符串去匹配字符串不久成功了嘛。直接 if key==’0’:    测试一下
嘿嘿,成功解决。
2.png
但是还要解决另外一个问题:我们要重新输入书名,以搜索另外的书,先上代码大家看一下:
[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也写在另外的文件里,主文件中引用即可。
3.png
然后要解决的是如何在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这个函数。或者说我们还可以优化一下交互,添加更多的中间信息反馈给我们以便观察代码运行情况……那么做完这些,顺完了所有的逻辑之后,是不是就完成了呀?让我们来运行试试。
4.png
呕吼,没搜到,但实际上笔趣阁是可以搜到这部小说的,还是有问题,问题发生在哪里呢?思考一下,问题原因应该是只有两个,要不就是代码没get到,要不就是获取到但是没有识别出来,再在开发者工具里看一下
5.png
原来是编码问题,问题原因找到了,那我们就要想办法解决,但是两个网站的编码不一样,这该怎么办呢?想办法让程序传回用的是哪个接口,在这里我们用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']


这样我们再测试一下
6.png
芜湖~完成 后记:  写的这个代码基本就告一段落,这个代码的最初目的是练手,但是在优化后还是有它的实际应用价值的,虽然做下载器的意义不大(因为有很多小说下载网站,我们可以直接爬取这些网站来下载整本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('已完成')


免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
a3084476 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ygxgdjj 发表于 2021-1-14 09:21
我遇到了503 ..........爬了50章就爬不了了
 楼主| we37 发表于 2021-1-14 09:44
ygxgdjj 发表于 2021-1-14 09:21
我遇到了503 ..........爬了50章就爬不了了

目标服务器过载了,所以说这个程序用作下载器没有很大的实际意义,练手项目,拿来做在线阅读器还可以
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 21:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表