selenium的官方不提供获取状态码,Content-Type,以及重定向路径的方法,并且官方说这些功能将来也不会有。java - How to get HTTP Response Code using Selenium WebDriver - Stack Overflow
1.通过requests重新请求一遍url,获取response里面的状态码
2.通过中间代理服务器来获取,比如selenium-wire,selenium-wire里面内建了一个代理服务器,通过代理服务器可以获取各个请求的状态码
3.通过分析chrome的performance log来获取状态码以及重定向路径
1的方法会多访问一遍网络,并且不能支持mata或js的重定向。2的方法只访问一次网络,但同样不能支持mata或js的重定向,只有3的方法只访问一次网络可以获取所有的状态。
并且1的方法在python环境上打开一些https网站还有如下bug
ssl.SSLError: [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1131) · Issue #2653 · urllib3/urllib3 · GitHuby
这个问题的原因是This error comes up when using OpenSSL 3 to connect to a server which does not support it. The solution is to downgrade the cryptography package in python:
python - SSL error unsafe legacy renegotiation disabled - Stack Overflow
要么修改openssl.cnf,要么降级cryptography,要么设置ctx,根据我测试的结果只有修改cnf才能彻底解决,最简单的方法是在cnf后面加上Options = UnsafeLegacyRenegotiation
如果使用docker应该写成这样
RUN echo 'Options = UnsafeLegacyRenegotiation' >> /usr/lib/ssl/openssl.cnf
performanceLog.py
import json
def getHeader(headers,header):
for key,value in headers.items():
if key.lower()==header:
return value
return ''
def getRedirectPath(driver):
redirctPath=[]
requestDic={}
frameId=None
for entry_json in driver.get_log('performance'):
entry = json.loads(entry_json['message'])
# print(entry)
if entry['message']['method'] == 'Network.requestWillBeSent':
if entry['message']['params']['loaderId']==entry['message']['params']['requestId'] and (frameId is None or frameId==entry['message']['params']['frameId']):
# print(entry)
if frameId is None:
frameId=entry['message']['params']['frameId']
if entry['message']['params']['redirectHasExtraInfo']:
# 设置重定向前的状态
item=redirctPath[len(redirctPath)-1]
item['status']=entry['message']['params']['redirectResponse']['status']
item['reason']='location'
item['contentType']=getHeader(entry['message']['params']['redirectResponse']['headers'],'content-type')
item={
'url':entry['message']['params']['request']['url'],
'status':'',
'reason':'',
'contentType':''
}
requestDic[entry['message']['params']['requestId']]=item
redirctPath.append(item)
elif entry['message']['method'] == 'Network.responseReceived' and entry['message']['params']['requestId'] in requestDic:
# print(entry)
item=requestDic[entry['message']['params']['requestId']]
item['status']=entry['message']['params']['response']['status']
item['contentType']=getHeader(entry['message']['params']['response']['headers'],'content-type')
elif entry['message']['method'] == 'Page.frameRequestedNavigation' and frameId==entry['message']['params']['frameId']:
# print(entry)
# 设置重定向前的状态
item=redirctPath[len(redirctPath)-1]
item['reason']=entry['message']['params']['reason']
return redirctPath
后来发现一些网站不能获取contentType,原因是header需要忽略大小写,修改了一下代码。当然你自己修改代码后可以获取任何你需要的header。
各个事件的顺序大致如下
Page.frameStartedLoading
Network.requestWillBeSent
Network.responseReceived
Network.dataReceived
Page.frameNavigated
Network.requestServedFromCache
Network.loadingFinished
Network.resourceChangedPriority
Page.domContentEventFired
Network.loadingFailed
Page.loadEventFired
Page.frameStoppedLoading
注释:Page.frameScheduledNavigation已经被废弃,改成Page.frameRequestedNavigation
官方对performance log的文档很少,在网上也没有找到类似的讨论,上面的代码完全是我根据数据分析出来的,目前看来是对的,如果不对请告诉我。
Chrome DevTools Protocol - version 1-2 - Page domain
测试代码
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import time
from performanceLog import getRedirectPath
caps = DesiredCapabilities.CHROME
caps['goog:loggingPrefs'] = {'performance': 'ALL'}
options = webdriver.ChromeOptions()
# 必须是headless=new,否则download.default_directory不起作用
options.add_argument('--headless=new')
# 下面这些参数是必须的,否则可能出错
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(options=options,desired_capabilities=caps)
driver.get('http://localhost/redirect')
# 必须等待几秒钟,否则metaTagRefresh不起作用
time.sleep(3)
print(getRedirectPath(driver))
driver.quit()
输出结果
[{
'url': 'http://localhost/redirect',
'status': 302,
'reason': 'location',
'contentType': 'text/html; charset=utf-8'
}, {
'url': 'http://localhost/static/index3.html',
'status': 200,
'reason': 'scriptInitiated',
'contentType': 'text/html'
}, {
'url': 'http://localhost/static/redirect.html',
'status': 200,
'reason': 'metaTagRefresh',
'contentType': 'text/html'
}, {
'url': 'http://localhost/',
'status': 200,
'reason': '',
'contentType': 'text/html; charset=utf-8'
}]
可以看出这里面有3种不同类型的重定向,location的通常的重定向,后面2种用requests或者代理都无法获取。
location:response header里面的location重定向
scriptInitiated:js重定向
metaTagRefresh:meta tag重定向