新手向的一次完整实战----一键化自动下载整本小说
今天具体看了一下写爬虫的另外一种手段,lxml+xpath,只会beautifulsoup的我很感兴趣,想着趁这个机会练练手,本来就想找了一个小说网站爬个小说练手,结果在边学边实战的过程中,看到网站的搜索栏突发奇想:是不是可以通过直接调用搜索栏的api(不知道算不算api)实现搜索即下载的自动化,然后经过一下午终于实现了,当然在这个过程中碰到了很多问题,一边学一边写,网上有很多xpath的教程,这里就不多说,代码肯定还有很多可以优化的地方,大佬请多多指点,本文主要介绍我的思路和在编写的过程中遇到的各种问题,小白向(我就是个小白)。整个project的流程大致是这样的:先使用网站的搜索功能检索想要的小说;然后再搜索出来的页面内检索是否有该小说,如果没有,则询问用户是否再次检索;若有则找出该小说对应的url;接下来顺着这个url找到对应小说章节页面,再提取各个章节的url,最后找到章节内容并写入本地文件。
按照这个顺序,第一步先找出网站搜索功能的api,通过Chrome开发者工具(F12)的network发现在搜索栏输入小说名称(这里我就用 吞噬星空 来试验)之后,网站直接发送的doc文件中就包含着检索出的小说名称和地址,说明网站并未通过js文件传输这部分内容,这样就意味着我们很容易就能通过rq.get获取到网页里检索出的结果的name和url,用于对比和下一步操作,但在这之前我们还需要考虑如何做到整个步骤中最重要的一点----自动查找,也就是我们得想办法找到如何调用搜索的方法。最开始我希望能在doc文件中找到蛛丝马迹,但并未如我所愿,最后通过多次搜索,瞅了一眼url,突然发现它变简短了,而且随着搜索名称的变化只有&q=后面的内容在变化,立刻将这个写入代码中
'''
url前后变化
前:https://so.biqusoso.com/s1.php?ie=gbk&siteid=booktxt.net&s=2758772450457967865&q=%CD%CC%CA%C9%D0%C7%BF%D5
后:https://so.biqusoso.com/s1.php?ie=utf-8&siteid=booktxt.net&q=吞噬星空
'''
#search_url就是搜索的接口,直接将要搜索的小说名跟在后面就可以进行搜索
search_url='https://so.biqusoso.com/s1.php?ie=utf-8&&siteid=booktxt.net&q='
这样我们就能通过rq.get(search_url+bookname)的方式进行搜索,然后通过etree和xpath结合使用的方式找到我们想要的内容。
#按书名搜索
def search_bookname(bookname):
search_result_html=rq.get(search_url+bookname)
search_htm=etree.HTML(search_result_html.text)
search_result_url = search_htm.xpath('//span[@class="s2"]/a/@href')#筛选出所有class=s2的span,并找出其下的a标中的href,存入列表search_result_url中
search_result_bookname = search_htm.xpath('//span[@class="s2"]/a/text()')
for i in range(len(search_result_bookname)):#遍历所有的搜索结果
if search_result_bookname==bookname:#判断搜索的小说名与搜索结果是否一致
return search_result_url
else:
return 0
在做完这一步后,我发现了一个问题,那就是在此网页中获取的目标url并不是最终的url,而需要进一步的重定向才能得到真正的url,这并不方便我们后续的使用,因为写爬虫爬取过小说内容的都应该知道,这类网站的href中存的只有网页后缀,还需要在前面补齐章节页面的url才能真正指向我们想要的地址,通过一张图来明白我刚才说的问题
我产生一个想法,用这个中转url加上后缀之后是否能够进入章节内容界面,遗憾的是并不可以,所以如果不能获取到这个真正的url,不能补齐,那就只能手动补齐,也就偏离了我们一键自动化下载的想法,所以这个重定向后的url我们一定要获取到!
经过我翻阅查找后发现urllib.request有一个方法可以获取到,代码如下
rs = urllib.request.urlopen(fake_url)
return str(rs.geturl())
这个代码能将重定向后的url获取到,这样我们就得到了继续向前走的方向
#获取真正的url
def real_url(fake_url):
rs = urllib.request.urlopen(fake_url)
return str(rs.geturl())
跟着上面的流程图走,接下来我们要做的就是获取小说各个章节的url,这里比较简单,就不多做叙述。
#获取小说章节
def book_chapter(book_url):
html=rq.get(book_url,headers=header)
html.encoding='gbk'
chapter=etree.HTML(html.text)
chapter_url=chapter.xpath('//*[@id="list"]/dl/dd/a/@href')#找出所有id=list的节点之下的href放入列表chapter_url中
return chapter_url
然后就是一章一章的将章节内容写入本地txt文件内
#单个章节写入文件内
def singlechapter_write(singlechapter_url):
html=rq.get(singlechapter_url,headers=header)
html.encoding='gbk'
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+'\n')
for i in range(len(data)):
f.write(str(data))
f.write('\n\n\n')
f.close()
因为我在这里选择将整本小说写入一个txt文件内,所以在open内使用'a'即追加写入。
走到这,看了一下整个代码,忽然发现重要的人机交互没有写进去,那就再写一个函数。
#搜索且完成后的交互
def start_search(bookname):
while 1:
book_url = search_bookname(bookname)
if book_url == 0:
print('没搜到,真不好意思')
key = input('还要搜吗,搜就输1,不搜就0')
if key == 0:
sys.exit()
else:
break
return book_url
这样模块化的函数就全部写完了,我们还需要像c语言一样将他们整合使用起来,
if __name__=='__main__':
bookname=input('你要搜什么书呢:')
book_url=start_search(bookname)
book_url=real_url(book_url)
chapter_url=book_chapter(book_url)
for i in range(10): #这里的10次是为了方便测试,所以填写少,如果要下载整本可以写为len(chapter_url)
url=book_url+chapter_url#在这里将章节页面url和后缀合并在一起形成新的url,然后get下来
singlechapter_write(url)
整个编写过程就是这样,不算曲折但也费了我很大精力,可以发现python真的是一个很好使用的语言,后面还有更多的高级操作我也要继续学下去。
另外推荐一个模块my_fake_useragent,使用很方便。
完整源码
import requests as rq
from lxml import etree
import sys
import my_fake_useragent as fu
import urllib.request
agent=fu.UserAgent()
header={
'Referer':'https://www.booktxt.net/',
'User-Agent': agent.random()
}
search_url='https://so.biqusoso.com/s1.php?ie=utf-8&&siteid=booktxt.net&q='
#按照书名搜索
def search_bookname(bookname):
search_result_html=rq.get(search_url+bookname)
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==bookname:
return search_result_url
else:
return 0
#获取真正的url
def real_url(fake_url):
rs = urllib.request.urlopen(fake_url)
return str(rs.geturl())
#获取小说章节
def book_chapter(book_url):
html=rq.get(book_url,headers=header)
html.encoding='gbk'
chapter=etree.HTML(html.text)
chapter_url=chapter.xpath('//*[@id="list"]/dl/dd/a/@href')
return chapter_url
#单个章节写入文件内
def singlechapter_write(singlechapter_url):
html=rq.get(singlechapter_url,headers=header)
html.encoding='gbk'
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+'\n')
for i in range(len(data)):
f.write(str(data))
f.write('\n\n\n')
f.close()
#搜索且完成后的交互
def start_search(bookname):
while 1:
book_url = search_bookname(bookname)
if book_url == 0:
print('没搜到,真不好意思')
key = input('还要搜吗,搜就输1,不搜就0')
if key == 0:
sys.exit()
else:
break
return book_url
if __name__=='__main__':
bookname=input('你要搜什么书呢:')
book_url=start_search(bookname)
book_url=real_url(book_url)
chapter_url=book_chapter(book_url)
for i in range(10):
url=book_url+chapter_url
singlechapter_write(url) C:\Users\G3\Desktop>python C:\Users\G3\Desktop\index.py
Traceback (most recent call last):
File "C:\Users\G3\Desktop\index.py", line 4, in <module>
import my_fake_useragent as fu
ModuleNotFoundError: No module named 'my_fake_useragent'
这个是啥错了? 竹笋家 发表于 2021-1-12 15:57
C:%users\G3\Desktop>python C:%users\G3\Desktop\index.py
Traceback (most recent call last):
File...
你没有装my-fake-UserAgent的模块,使用cmd输入pip install my-fake-useragent来安装。
另外这个代码还有一点bug,我晚上把改完后的发出来 感谢分享,我也有想学的想法 。 不错,感谢分享 刚想学些这方面,感谢楼主分享经验 正好在学习python 哈哈 将来肯定是个高手,我要给大哥点点赞{:1_918:} 高手都是 感谢分享
页:
[1]
2