一、引言
这个五一假期自驾回老家乡下,家里没装宽带,用手机热点方式访问网络。这次回去感觉4G信号没有以前好,通过百度查找小说最新更新并打开小说网站很慢,有时要打开好多个网页才能找到可以正常打开的最新更新。为了躲懒,老猿决定利用Python爬虫知识,写个简单应用自己查找小说最新更新并访问最快的网站,花了点时间研究了一下相关报文,经过近一天时间研究和编写,终于搞定,下面就来介绍一下整个过程。
二、关于相关访问请求及应答报文
2.1、百度搜索请求
我们通过百度网页的搜索框进行搜索时,提交的url请求是这样的:
https://www.baidu.com/s?wd=搜索词&pn=10&rn=50
请求的url为https://www.baidu.com/s ,带三个参数:
- wd:搜索的关键词
- pn:当前需要显示搜索结果记录在总搜索结果的序号,如总搜索有300条记录满足要求,现在要求显示第130条记录,则pn参数值设为130即可
- rn:每页显示记录数,缺省为10条,可以自行设定,但如果设定超过50,则会强制显示为每页10条。
2.2、百度返回搜索结果
百度返回的搜索结果有多种方式确定,老猿认为如下方式最简单:
以搜索小说《青萍》为例来看其中的一个返回记录:
<h3 class="t"><a data-click="{
'F':'778317EA',
'F1':'9D73F1E4',
'F2':'4CA6DE6B',
'F3':'54E5243F',
'T':'1620130755',
'y':'FE7FF57A'}"
href="http://www.baidu.com/link?url=9LLa46B6hp69vJdLx6wOGfBpoS7BaRe8zV3oSNj_Vc2AxuU0Tz5Bl7CZlqNPobdw_BElAgaadA_HfCJMtADpyq" rel="external nofollow" target="_blank">
<em>青萍</em>最新章节,<em>青萍</em>免费阅读 - 大神小说网</a>
</h3>
整个搜索返回的结果在一个h3的标签内,返回的搜索结果对应url在a标签内,具体url由href来指定。这里返回的url实际上是一个百度重定向的地址,可以通过打开该url访问对应网站,并通过返回响应消息获取真正网站的URL。
2.3、小说网站关于最新更新的展现及html报文格式
根据老猿分析,约占30%的小说网站关于最新更新章节的展现类似如下:

首先有类似“最新章节”或“最新更新”或“最近更新”等类似提示词,在该提示词后是显示最新章节的章节序号及章节名的一个链接,对应的报文类似如下:
<p>最新章节:<a href="/book/12/12938/358787.html" rel="external nofollow" target="_blank">第729章 就是给你们看看的</a></p>
这个报文的特点是:
“最新章节”的文本信息与小说最新章节的链接在同一个父标签内。另外需要说明的是返回的章节url并不是绝对地址,而是小说网站的相对地址。
老猿对搜索小说查找最新章节都是基于以上格式的,因此实际上程序最终获取的小说网站只占了整个搜索结果的30%左右,不过对于看小说来说已经足够了。
三、实现思路及代码
3.1、根据url获取网站名
def getHostName(url):
httpPost = url[10:]
hostName = url[:10]+httpPost.split('/')[0]
return hostName
3.2、根据百度返回搜索结果地址打开网站获取小说信息
基于2.3部分介绍的小说网站返回内容,我们来根据百度返回搜索结果的URL来打开对应小说网站,并计算从请求发起到响应返回的时间:
def getNoteInfo(url):
"""
打开指定小说网页URL获取最新章节信息
url:百度搜索结果指定的搜索匹配记录的url
返回该URL对应的章节ID、打开耗时、网站真正URL、网站主机名、章节相对url、章节名
"""
head = mkhead()
start = time.time_ns()
req = urllib.request.Request(url=url, headers=head)
try:
resp = urllib.request.urlopen(req,timeout=2)
#根据响应头获取真正的网页URL对应的网站名
hostName = getHostName(resp.url)
text = resp.read()
#网页编码有2种:utf-8和GBK
pageText = text.decode('utf-8')
except UnicodeDecodeError:
pageText = text.decode('GBK')
except Exception as e:
errInf = f"打开网站 {url} 失败,异常原因:\n{e}\n" + '\n' + traceback.format_exc() + '\n'
logPag(errInf, False)
return None
#最新章节的HTML报文类似: '<p>最新章节2"&V&WFW&r#G3rw&"#3b'7^h>[ ~i{bssBj#У^8zczfKz^X{[f&VcGG3rBb3""&V&WFW&r#G3rBb3"'7^h>[ ~i{bj#ЮhYnJX{У&6VУ#N8[#Уh~K{nKyJFJ.hZikiNikz.K^Xz[zyNZ ;>Y([>JNyJK>zzXi J.[ikiNikz.K^XXz[{z8.K^KyNZKK.{niikz.yN;^K[XyNhniikz.KXiYx.y8#УjX[>KFKxikiNikyN[zyNih~z[K{NZIyX[5FikiNikyN[zXh^Z{J.zKK^XNih~zhn{~{XyNyX[>ih~z[ZJ~Z^YZIiJ |