先说说事情的起因。最近在调试百度搜索的自动化脚本,但是使用最普通的Python脚本(没有配置任何headers信息)发送搜索请求没有办法返回搜索页面。因此无论使用BeautifulSoap或者lxml都无法从结果中使用xpath抽取需要的字段。多次尝试后会出现302跳转验证码的情况,并且由于Requests脚本是下载的是静态数据,所以直接显示“百度安全验证”信息。但是在本地浏览器中却可以正常访问搜索百度。在这种情况下,通过以下步骤,分析浏览器访问百度的数据包,并用Python实现自动搜索脚本。
#1. 禁用浏览器JS
使用不同的浏览器,禁用JS的方法不同。参考这篇文章中的方法可以在Firefox中禁用Javascript。本文在Mac上使用Edge浏览器(本人使用Safari浏览正常页面,使用Firefox设置了Proxy访问特殊页面,使用Chrome调试动态页面),可以通过以下两种方式禁用javascript:
- 如果已经启动了Edge浏览器,在地址栏中中输入
edge://settings/content/javascript
- 如果通过命令行启动Edge,使用启动参数禁用JS
/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev --proxy-server=127.0.0.1:8080 --ignore-certificate-errors -disable-javascript
- 如果通过开发者工具,打开开发者工具,使用快捷键(Command+Shift+P),并键入Javascript能出现Disable Javascript的选项。
除了以上方法还有其他方式就不一一举例了。
#2. 安装和启动Mitmproxy
其实使用浏览器自带的开发者工具就可以很方便的分析网络请求,但是Mitmproxy提供了更加强大的功能。具体参考官方文档。随后通过以下命令安装和启动Mitmproxy:
brew install mitmproxy # 安装
mitmweb # 启动web版本,proxy地址为127.0.0.1:8080,在浏览器中访问用户界面http://127.0.0.1:8081/
#3. 在设置了proxy的浏览器中访问百度
因为使用Python的Requests包访问的是静态页面,所以在调试过程中禁用浏览器的JS功能。以下是请求头信息:
GET https://www.baidu.com/ HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.0.0
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.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
以下是响应头信息:
HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0x8b064ee900001798
Connection: keep-alive
Content-Encoding: gzip
Content-Security-Policy: frame-ancestors 'self' https://chat.baidu.com http://mirror-chat.baidu.com https://fj-chat.baidu.com https://hba-chat.baidu.com https://hbe-chat.baidu.com https://njjs-chat.baidu.com https://nj-chat.baidu.com https://hna-chat.baidu.com https://hnb-chat.baidu.com http://debug.baidu-int.com;
Content-Type: text/html; charset=utf-8
Date: Wed, 19 Jul 2023 03:31:30 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Server: BWS/1.1
Set-Cookie: BAIDUID=83894E635C596442D9073B6617A44F41:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=83894E635C596442D9073B6617A44F41; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1689737490; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=83894E635C5964423356F21A6FF6122B:FG=1; max-age=31536000; expires=Thu, 18-Jul-24 03:31:30 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Set-Cookie: BDSVRTM=28; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=36558_38642_38831_39027_39024_38943_38881_38954_38984_38962_38801_38825_39085_26350_39095_39100; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1689737490235426023410017781183766599576
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
#4. 保存和使用Cookies
从以上返回的HTTP Headers中发现很多Set-Cookie信息。这部分信息需要带到后面的请求中。通过以上的请求头和响应头分析,使用Python模拟浏览器访问静态页面。
baiduHeaders = {
"Host": "www.baidu.com",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.0.0",
"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.7",
"Sec-Fetch-Site": "none",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-User": "?1",
"Sec-Fetch-Dest": "document",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"
}
url = "https://baidu.com/"
response = requests.get(url, headers=baiduHeaders, verify=False, allow_redirects=False)
cookies = response.cookies
print(response.text)
#5. Transfer-Encoding chunked 产生的乱码问题
运行以上代码,很可能会遇到打印出乱码的问题。主要是因为百度返回的是数据块(并且可能压缩过),在响应http头中发现Transfer-Encoding为chunked。如果要避免百度返回这种数据,那么在请求头中不要设置Accept-Encoding即可。
#6. 奇怪的重定向
上文中我们已经禁用了Javascript,并且返回的响应代码为200(如果是301或者302就是http重定向)。但是在后续的调试中发现百度页面会重定向到了新的URL。(后续在搜索的过程中也遇到过很多次,搜索结果已经返回,但是在浏览器中会刷新2次显示新的页面和URL)
最后在百度返回的内容中发现原因:
<noscript><meta http-equiv="refresh" content="0; url=http://www.baidu.com/baidu.html?from=noscript" /></noscript>
#7. 模拟禁用JS的浏览器进行百度搜索
到目前为止,我们已经获了解了请求头的配置方法,cookie的配置方法和百度搜索在静态页面下的运行流程。这样我们就可以在Python中进行模拟:
baiduHeaders = {
"Host": "www.baidu.com",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.0.0",
"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.7",
"Sec-Fetch-Site": "none",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-User": "?1",
"Sec-Fetch-Dest": "document",
# "Accept-Encoding": "gzip, deflate, br", #先不要使用该参数避免Transfer-Encoding为chunked返回数据
"Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"
}
url = "https://baidu.com/"
response = requests.get(url, headers=baiduHeaders, verify=False, allow_redirects=False)
cookies = response.cookies #保存cookies
baiduHeaders["Referer"] = "http://www.baidu.com/baidu.html?from=noscript"
url = "http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=腾讯"
response = requests.get(url, headers=baiduHeaders, verify=False, allow_redirects=False, cookies=cookies)
print(response.text)
扫码联系船长