Selenium配置token访站报DevTools WebSocket会话创建错误
触发Unexpected error creating WebSocket DevTools session报错的核心原因有三个,按出现概率从高到低排列:
- WebDriver与本地浏览器版本不匹配:Selenium的网络拦截能力完全依赖CDP(Chrome DevTools Protocol)的WebSocket连接,CDP对版本一致性要求极高,浏览器和Driver的大版本号不一致就会直接建连失败。
- 代码执行顺序错误:
network.StartMonitoring()是异步初始化操作,原代码在启动监控后立刻触发页面导航,再等待监控任务完成,会导致CDP会话还没完成初始化就收到网络事件,触发建连冲突。 - Selenium版本过低:4.0正式版之前的预览版本内置的CDP客户端存在WebSocket握手bug,部分场景下会无故建连失败。
对应修复步骤:
- 先对齐浏览器与Driver版本:将Chrome/Edge升级到最新正式版,下载对应大版本的Driver文件,保证两者大版本号完全一致。
- 调整代码执行顺序,必须等CDP监控会话完全建立后再触发页面导航,修正后的代码如下:
public void Setup() { _driver = Driver.GenerateWebDriver(browser, "local"); var network = _driver.Manage().Network; network.AddRequestHandler(new NetworkRequestHandler { RequestMatcher = data => data.Url.Contains("www-stage.ibselectronics.com"), RequestTransformer = data => { if (data.Method == "POST") { var plainTextBytes = Encoding.UTF8.GetBytes(data.PostData); data.PostData = Convert.ToBase64String(plainTextBytes); } data.Headers["CF-Access-Client-Id"] = "token here.access"; data.Headers["CF-Access-Client-Secret"] = "token here"; return data; }, }); // 等待监控初始化完成再跳转 network.StartMonitoring().Wait(); _driver.Navigate().GoToUrl(baseUrl); }
- 若仍报错,直接将
Selenium.WebDriver、Selenium.Support依赖包升级到4.6以上稳定版,新版本已修复绝大多数CDP连接相关的已知bug。
如果上述修复无效,或者不想依赖Selenium内置的网络拦截能力,可以用以下三种更稳定的方案实现带认证头的站点访问:
方案1:直接调用CDP接口设置全局请求头
不需要启动全量网络监控,直接通过CDP会话给所有请求附加固定头,资源占用更低、连接稳定性更高:
public void Setup() { _driver = Driver.GenerateWebDriver(browser, "local"); var devToolsSession = ((IDevTools)_driver).GetDevToolsSession(); // 启用Network域 devToolsSession.SendCommand("Network.enable", new Dictionary<string, object>()); // 设置全局附加请求头 devToolsSession.SendCommand("Network.setExtraHTTPHeaders", new Dictionary<string, object> { ["headers"] = new Dictionary<string, string> { {"CF-Access-Client-Id", "token here.access"}, {"CF-Access-Client-Secret", "token here"} } }); // 需要处理POST数据Base64转码时,额外订阅Network.requestWillBeSent事件即可 _driver.Navigate().GoToUrl(baseUrl); }
方案2:本地代理层注入请求头
启动Driver时指定本地代理端口,在代理层统一处理请求头注入、POST数据转码逻辑,完全不依赖浏览器侧的CDP能力,兼容性覆盖所有浏览器版本,不会出现DevTools相关报错。
启动Driver时添加代理参数示例:
var chromeOptions = new ChromeOptions(); // 替换为实际启动的本地代理监听地址 chromeOptions.AddArgument("--proxy-server=http://127.0.0.1:8888"); _driver = new ChromeDriver(chromeOptions);
代理层只需要实现两个逻辑:匹配到目标域名的请求时追加两个CF Access认证头;遇到发往目标域名的POST请求时,将请求体转码为Base64后再转发即可。
方案3:提前换取认证Cookie访问
Cloudflare Access支持直接用Client Id/Secret调用认证接口换取CF_Authorization Cookie,拿到Cookie后通过Selenium的Cookie操作接口写入目标域名下,后续访问不需要每次请求都携带认证头,也不需要拦截请求,性能最优。
注意:原逻辑中无差别将所有POST请求的PostData转为Base64的逻辑存在风险,如果目标接口的
Content-Type为application/json或application/x-www-form-urlencoded,服务端默认不会按Base64格式解析请求体,会直接报参数错误,建议提前和服务端确认编码要求再做转换。
内容的提问来源于stack exchange,提问作者Kier Harny Rosalejos

