最近在搞一个自制的参考文献归档,查阅记录的工具。需要刚好需要用到Python爬虫,爬取Paper和References。国内由于谷歌学术无法访问,所以怕一个国内的网站,但基本就是一个国内的代理服务器,文献是结果一样的。不过在爬取过程过遇到了网址跳转的问题。
前情提要
在使用requests库爬取关键词时,返回来的并不是用浏览器获得的结果。
例如我们一般爬取搜索页面,输入参数seismic,通过Fiddler抓包发现网址是
http://so.hiqq.com.cn/scholar_complete.php?q=seismic&hl=zh-CN&as_sdt=0%2C5&btnG=
但是我们调用Requests库去Get请求,返回的不是想要的内容,而是
HTTP/1.1 302 Moved Temporarily
Server: nginx
Date: Thu, 13 Feb 2020 13:54:15 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Location: https://zz.glgoo.top/scholar?hl=zh-CN&as_sdt=0%2C5&q=seismic&btnG=
响应报文里没有响应体,头文件里就是一个类似重定向的东西,告诉我们,去这个Location找。这个倒是不难,直接再Request这个Location
过程
结果几次的摸索,算是搞清楚了整个过程,我以最一般的情况来说(不带有任何Cookies)。
一、给出Location
猜测是这个代理服务器存在很多个,域名基本都是
xxx..glgoo.top/scholar
xxx.gufenxueshu.com/scholar
每次都需要在kuaisou里给你一个门的钥匙。我们直接将此次Location的变量传给下一个Request
二、传递security_id
对xxx.fufenxueshu.com/scholar的访问,首先会检测有没有Cookies,主要有4个参数
UM_distinctid
_gads
security_session_verify
security_session_mid_verify
前两个浏览器里可以直接获取,后两个,如果没有携带,则会分两次传递过来。本次Request获得了security_id,通过定义一个函数来获取参数并传递给下一个请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| def getssv(self,str): strurl = self.geturl(0,str) headers={ 'Referer': 'http://so.hiqq.com.cn', 'Accept-Language': 'zh-CN,zh;q=0.9', "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", "Host": "xs.gufenxueshu.com", "Connection": "keep-alive", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Upgrade-Insecure-Requests": "1", "Sec-Fetch-User": "?1", "Sec-Fetch-Site": "cross-site", "Sec-Fetch-Mode": "navigate" } r1 = requests.get(strurl,headers = headers,verify=False) ssv = r1.headers['Set-Cookie'] ssv = re.findall(r'security_session_verify=''.{32}',ssv)[0] return ssv
|
三、获取security_mid_id
原理同上,不过这次请求需要带上上次给的ssv,同时,访问的站点后缀发生了一些变化。由于上次传来的响应体还包含了一个YunSuoAutoJump
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate, post-check=0, pre-check=0"/><meta http-equiv="Connection" content="Close"/> <script type="text/javascript"> function stringToHex(str){ var val=""; for(var i = 0; i < str.length; i++){ if(val == "") val = str.charCodeAt(i).toString(16); else val += str.charCodeAt(i).toString(16); } return val; } function YunSuoAutoJump(){ var width =screen.width; var height=screen.height; var screendate = width + "," + height; var curlocation = window.location.href; if(-1 == curlocation.indexOf("security_verify_")) { document.cookie="srcurl=" + stringToHex(window.location.href) + ";path=/;"; } self.location = "/scholar?hl=zh-CN&as_sdt=0,5&q=seismic&btnG=&oq=seismic&security_verify_data=" + stringToHex(screendate); }</script> <script>setTimeout("YunSuoAutoJump()", 50);</script></head></html>
|
分析不难发现,其实就是获取屏幕的分辨率(除去下方的任务栏),例如‘(1920,1080)’再通过一个函数,转化为十进制,我们写一个这样的转化函数。
1 2 3 4 5 6
| def stringToHex(self,string): length = len(string) hex_string = str() for i in range(length): hex_string += hex(ord(string[i]))[2:] return hex_string
|
后面只需要在原来的url后面加上
&security_verify_data=stringToHex(1920,1080)
就可以获得ssmv了。
四、获取真正目标html
在拿到两个认证Id后,添加到Cookies里,再回过头来访问原始url就可以正常拿到想要的html了。
简化
## 认证上的简化
其实在后续的请求中,发现有时候不需要ssv和ssmv也可以拿到完整的html,为了保持程序的完整性和速度,我们加一个判断就可以了,如果获取的ssv为空,就直接把这个request丢到beautifulsoup里,不用再去获取ssv和ssmv了。
Location上的简化
另外,其实在第一步获取Location时也可以省略,直接拿一个固定的服务器,不过这样的结果就是,如果这个服务器访问量太大,需要你刷新一下或者排队,也就是再次request一次。两种方法无非就是
- 固定花费一定时间来到一个通畅的道路
- 不花费时间来到一个固定的道路,不过这个道路可能会堵塞
预告
其实本次爬虫主要是为了一个自制的文献管理和记录的工具,后续我们会继续在PyQt中使用到这个类,完成我们对Paper和References的获取与记录。
Comments