Selenium结合CDP实现浏览器高级自动化:网络监控与请求拦截实战

发布时间:2026/7/1 23:54:57

Selenium结合CDP实现浏览器高级自动化:网络监控与请求拦截实战 1. 项目概述当Selenium遇上CDP浏览器自动化的新篇章如果你用过Selenium做网页自动化不管是爬数据还是做测试肯定遇到过一些头疼的“天花板”。比如你想实时监控页面加载时每个网络请求的详情和性能指标或者想拦截并修改某个特定的Ajax请求又或者想模拟更底层的浏览器行为如触摸事件、地理位置。用传统的Selenium WebDriver API这些需求要么实现起来异常繁琐要么干脆不支持。这时候一个更强大的工具就该登场了——Chrome DevTools Protocol也就是我们常说的CDP。这个项目就是带你用Python和Selenium通过CDP协议这把“瑞士军刀”捅破那层天花板。我们不止于简单的点击和输入而是要深入浏览器内核实现诸如网络请求监控、性能分析、请求拦截与修改等高级自动化操作。我会手把手带你搭建环境解析CDP的核心概念并最终实现一个实用的“网络日志监控器”让你能清晰看到页面加载过程中的每一个请求、响应状态、耗时和资源大小。这对于前端性能调试、自动化测试断言、甚至是特定场景的数据采集都极具价值。无论你是已经熟悉Selenium基础想进一步提升技能的开发者还是对浏览器底层工作原理充满好奇的技术爱好者这篇实战指南都将为你打开一扇新的大门。2. 环境准备与核心工具链解析工欲善其事必先利其器。在开始CDP实战之前我们需要一个稳固且版本匹配的基础环境。这里面的坑我踩过不少总结下来就一句话版本兼容性是成功的第一步。2.1 Python与Selenium的版本抉择首先确保你有一个Python 3.7或更高版本的环境。我强烈推荐使用Python 3.8它在异步支持和稳定性上表现更好。安装Selenium很简单用pip即可pip install selenium。但请注意不要盲目安装最新版。截至我撰写本文时Selenium 4.x系列是主流且对CDP支持最完善的版本。你可以使用pip install selenium4.10.0来安装一个经过大量项目验证的稳定版本。Selenium 4的一个重大改进就是原生集成了对CDP的支持通过driver.execute_cdp_cmd()方法可以直接调用这比早期版本需要额外依赖websocket库连接要方便得多。2.2 浏览器与驱动程序的精准匹配这是最容易出错的环节。我们的目标是Chrome/Chromium内核的浏览器因为CDP是Chrome DevTools的协议。你需要准备两样东西Chrome浏览器和对应的ChromeDriver。浏览器版本建议使用Chrome稳定版Stable Channel。你可以通过访问chrome://version/查看完整版本号例如115.0.5790.170。驱动版本ChromeDriver的版本必须与你的Chrome浏览器主版本号完全一致例如Chrome版本是115.0.5790.170那么ChromeDriver也必须选择115.x.x.x版本。你可以从淘宝的NPM镜像站https://npm.taobao.org/mirrors/chromedriver/或官方站点下载对应版本。注意很多教程会教你用webdriver-manager这个库自动管理驱动。对于常规自动化这很方便。但在涉及CDP的高级场景尤其是需要稳定复现某些行为时我建议手动指定明确版本的驱动和浏览器。自动管理有时会下载到版本稍有不匹配的测试版Canary驱动可能导致CDP命令执行不稳定。配置路径将下载的chromedriver可执行文件放在系统PATH路径下或者在代码中指定其绝对路径。2.3 验证基础环境让我们写一个最简单的脚本确保Selenium和驱动能正常工作同时为后续的CDP调用铺路。from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options # 1. 配置浏览器选项 chrome_options Options() # 建议在调试阶段先禁用GPU加速和沙箱减少潜在干扰 chrome_options.add_argument(--disable-gpu) chrome_options.add_argument(--no-sandbox) # 仅限Linux环境或某些Docker环境需要 # 为了CDP我们通常需要以远程调试模式启动但Selenium 4的CDP支持不需要手动开启此端口 # 2. 指定ChromeDriver路径如果不在PATH中 service Service(executable_path/path/to/your/chromedriver) # 替换为你的实际路径 # 3. 创建驱动实例 driver webdriver.Chrome(serviceservice, optionschrome_options) try: # 打开一个测试页面 driver.get(https://httpbin.org/get) print(f页面标题: {driver.title}) print(f浏览器版本: {driver.capabilities[browserVersion]}) print(fChromeDriver版本: {driver.capabilities[chrome][chromedriverVersion].split( )[0]}) finally: # 确保关闭浏览器 driver.quit()运行这个脚本如果成功打开浏览器并打印出版本信息说明你的基础Selenium环境已经就绪。打印出的两个版本号应该主版本一致这是后续CDP工作稳定的基石。3. CDP协议核心概念与Selenium对接原理在开始写代码之前我们有必要花点时间理解CDP到底是什么以及Selenium是如何与它“对话”的。这能让你在遇到问题时知道该从哪里排查。3.1 CDP是什么不是GB28181的CDP首先明确一个概念这里讨论的CDPChrome DevTools Protocol是谷歌Chrome浏览器开发者工具所使用的底层通信协议。它是一个基于WebSocket或旧版本的HTTP的JSON-RPC协议允许外部工具如我们的Python脚本对浏览器实例进行深度检测、调试和控制。这和你可能在网络视频监控领域听过的GB28181标准中的“CDP”设备控制协议完全不是一回事两者只是缩写巧合。CDP的能力极其强大几乎涵盖了你在Chrome DevTools面板里能看到和操作的一切网络域监控请求/响应、拦截修改、获取加载时间线。页面域导航、截图、获取DOM树、执行JavaScript。运行时域执行JS表达式、监听控制台输出、操作远程对象。性能域获取性能指标FPS、内存、CPU。浏览器域控制浏览器窗口、标签页。输入域模拟键盘、鼠标、触摸等硬件输入事件。3.2 Selenium与CDP的握手过程在Selenium 4之前想要使用CDP你需要自己用websockets库去连接浏览器启动时开放的远程调试端口如--remote-debugging-port9222然后手动发送JSON-RPC命令流程相当复杂。Selenium 4 带来了革命性的简化。当你通过webdriver.Chrome()创建驱动对象时Selenium底层会自动与ChromeDriver通信而ChromeDriver则作为中间人通过CDP与真实的Chrome浏览器实例对话。Selenium为我们提供了一个极其简洁的接口driver.execute_cdp_cmd(cmd, cmd_args)。cmd字符串表示CDP的命令格式为“域.方法”。例如Network.enable表示启用网络域监听Page.captureScreenshot表示截取页面截图。cmd_args字典表示传递给该命令的参数。当你在Python中调用driver.execute_cdp_cmd(‘Network.enable’, {})时这个调用会沿着Python脚本 - Selenium客户端 - ChromeDriver - CDP - Chrome浏览器这个链路传递最终在浏览器内核中生效。3.3 理解“域”和“事件”CDP的API组织方式主要围绕“域”和“事件”。域一组相关功能的集合如Network、Page、Runtime。命令属于某个域的方法你可以主动调用它来执行某个操作如Network.setCacheDisabled。事件属于某个域的通知当浏览器中发生某事时如网络请求发起Network.requestWillBeSentCDP会主动推送事件消息。我们需要事先“订阅”或“监听”这些事件。在Selenium中监听事件的方式是使用driver.add_cdp_listener(‘事件名’, 回调函数)。例如监听网络请求事件def on_request_will_be_sent(event): print(f请求URL: {event[params][request][url]}) driver.add_cdp_listener(Network.requestWillBeSent, on_request_will_be_sent) driver.execute_cdp_cmd(Network.enable, {})当浏览器发起任何网络请求时on_request_will_be_sent函数就会被调用并接收到包含请求详情的事件参数。掌握了这些核心概念你就知道了我们所有高级自动化操作的“力量源泉”来自哪里。接下来我们就进入最激动人心的实战环节。4. 网络日志监控实战从零构建监控器网络监控是CDP最经典的应用场景之一。我们将构建一个功能完整的网络日志监控器它能捕获页面加载过程中所有网络活动的详细信息并以结构化的方式呈现出来。这对于分析页面性能瓶颈、检查第三方资源加载、验证API调用是否正确等场景非常有用。4.1 设计监控器架构与数据模型在编码前先规划一下我们的监控器需要做什么启用CDP网络域告诉浏览器开始发送网络事件。监听关键网络事件主要监听四个事件Network.requestWillBeSent请求即将发送时触发。记录请求ID、URL、方法、请求头、发起者信息调用栈。Network.responseReceived收到响应头时触发。记录响应状态码、响应头、资源类型如Document, Stylesheet, XHR。Network.loadingFinished请求完成加载时触发。记录请求ID和传输的数据大小encodedDataLength。Network.loadingFailed请求加载失败时触发。记录失败原因和错误信息。关联请求与响应通过浏览器为每个网络请求分配的唯一的requestId将请求、响应、完成/失败事件关联起来。存储与展示数据将关联好的完整网络请求信息存储起来并在所有监控结束后或实时地打印或保存到文件。我们需要一个数据结构来存储每个请求的完整生命周期信息。Python字典是个好选择用requestId作为键。class NetworkLogMonitor: def __init__(self, driver): self.driver driver self.network_entries {} # 存储所有网络请求{requestId: entry} # 初始化监听器 self._attach_listeners() def _attach_listeners(self): # 监听请求发送事件 self.driver.add_cdp_listener(Network.requestWillBeSent, self._on_request_will_be_sent) # 监听响应接收事件 self.driver.add_cdp_listener(Network.responseReceived, self._on_response_received) # 监听加载完成事件 self.driver.add_cdp_listener(Network.loadingFinished, self._on_loading_finished) # 监听加载失败事件 self.driver.add_cdp_listener(Network.loadingFailed, self._on_loading_failed) def start(self): 开始监控网络活动 # 启用Network域并可以设置一些参数比如maxTotalBufferSize最大缓冲区大小 self.driver.execute_cdp_cmd(Network.enable, { # maxTotalBufferSize: 100 * 1024 * 1024, # 100MB缓冲区可选 # maxResourceBufferSize: 50 * 1024 * 1024, # 每个资源50MB可选 }) print(网络监控已启动。) def stop(self): 停止监控网络活动 self.driver.execute_cdp_cmd(Network.disable, {}) print(网络监控已停止。) def get_entries(self): 获取所有捕获的网络条目 return self.network_entries4.2 实现核心事件监听与数据关联现在我们来填充那几个事件监听方法。这是整个监控器的核心逻辑。def _on_request_will_be_sent(self, event): 处理请求发送事件 params event[params] request_id params[requestId] request_info params[request] # 初始化一个条目 self.network_entries[request_id] { requestId: request_id, url: request_info[url], method: request_info[method], requestHeaders: request_info.get(headers, {}), timestamp: params.get(timestamp, time.time()), # 使用事件时间戳 wallTime: params.get(wallTime), # 系统墙钟时间 initiator: params.get(initiator, {}), # 请求发起者类型、调用栈 type: params.get(type), # 资源类型此时可能还不确定 response: None, # 预留响应字段 transferSize: None, # 预留传输大小字段 error: None # 预留错误字段 } # 可选打印实时日志 # print(f[请求] {request_info[method]} {request_info[url]}) def _on_response_received(self, event): 处理响应接收事件 params event[params] request_id params[requestId] response params[response] if request_id in self.network_entries: entry self.network_entries[request_id] entry[response] { status: response[status], statusText: response[statusText], headers: response.get(headers, {}), mimeType: response.get(mimeType, ), remoteIPAddress: response.get(remoteIPAddress), remotePort: response.get(remotePort), } # 更新资源类型从响应中获取的更准确 entry[type] params.get(type, entry.get(type)) # 打印实时日志 print(f[响应] {response[status]} {entry[url]}) def _on_loading_finished(self, event): 处理加载完成事件 params event[params] request_id params[requestId] encoded_data_length params.get(encodedDataLength, 0) # 网络传输的数据大小 if request_id in self.network_entries: self.network_entries[request_id][transferSize] encoded_data_length self.network_entries[request_id][finished] True def _on_loading_failed(self, event): 处理加载失败事件 params event[params] request_id params[requestId] error_text params.get(errorText, Unknown error) if request_id in self.network_entries: self.network_entries[request_id][error] error_text self.network_entries[request_id][finished] True print(f[失败] {error_text} - {self.network_entries[request_id][url]})实操心得事件触发的顺序通常是requestWillBeSent-responseReceived- (loadingFinished或loadingFailed)。但网络是并发的不同请求的事件会交错到达。因此我们的代码必须基于requestId来关联而不能假设事件的全局顺序。encodedDataLength字段非常有用它代表了实际通过网络传输的响应体数据大小压缩后对于性能分析是关键指标。4.3 整合与运行监控一个真实页面让我们把上面的类整合起来并写一个主程序来监控一个复杂页面的网络活动比如电商网站首页。import time import json from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options # 假设上面的 NetworkLogMonitor 类定义在一个单独的模块或写在此处 # class NetworkLogMonitor: ... def main(): # 1. 初始化浏览器驱动 chrome_options Options() # 为了看到效果我们可以让浏览器窗口正常显示 # chrome_options.add_argument(--headless) # 如果需要无头模式取消注释 chrome_options.add_argument(--disable-gpu) chrome_options.add_argument(--no-sandbox) service Service(executable_path/path/to/chromedriver) # 请修改路径 driver webdriver.Chrome(serviceservice, optionschrome_options) # 2. 创建网络监控器实例 monitor NetworkLogMonitor(driver) try: # 3. 启动网络监控 monitor.start() # 4. 导航到一个复杂页面 target_url https://www.example.com # 替换为你想要监控的网站 print(f正在访问: {target_url}) driver.get(target_url) # 等待页面主要内容和异步请求加载完成 time.sleep(5) # 根据网络情况调整更佳实践是使用WebDriverWait等待特定元素 # 5. 停止监控可选也可以一直监控直到浏览器关闭 monitor.stop() # 6. 获取并分析结果 entries monitor.get_entries() print(f\n共捕获 {len(entries)} 个网络请求。) # 按资源类型统计 type_stats {} for req_id, entry in entries.items(): req_type entry.get(type, Other) type_stats[req_type] type_stats.get(req_type, 0) 1 print(\n资源类型统计:) for req_type, count in type_stats.items(): print(f {req_type}: {count}) # 找出加载最慢的请求基于时间戳简单计算实际更复杂 if entries: # 这里我们简单地以请求开始时间排序实际耗时需要结合多个事件计算 sorted_entries sorted(entries.values(), keylambda x: x[timestamp]) print(f\n第一个请求: {sorted_entries[0][url]}) print(f最后一个请求: {sorted_entries[-1][url]}) # 7. 将详细日志保存为JSON文件便于后续分析 output_file network_log.json # 由于字典的键可能是对象需要转换为可序列化的格式 serializable_entries {} for req_id, entry in entries.items(): # 简单处理确保所有值都是可JSON序列化的 serializable_entries[req_id] entry with open(output_file, w, encodingutf-8) as f: json.dump(serializable_entries, f, indent2, ensure_asciiFalse) print(f\n详细网络日志已保存至: {output_file}) # 8. 在控制台打印几个示例请求的详细信息 print(\n--- 示例请求详情 ---) sample_count 0 for req_id, entry in entries.items(): if sample_count 3: # 只打印前3个 break if entry.get(response): print(fURL: {entry[url]}) print(f 方法: {entry[method]}, 状态码: {entry[response][status]}) print(f 类型: {entry.get(type)}, 大小: {entry.get(transferSize, N/A)} bytes) print(- * 40) sample_count 1 finally: # 确保关闭浏览器 driver.quit() if __name__ __main__: main()运行这个脚本你会看到控制台输出捕获的请求数量、类型统计并生成一个包含所有请求详情的network_log.json文件。通过这个文件你可以进行深入分析比如找出未压缩的大资源、失败请求、慢速API等。5. 进阶技巧请求拦截、性能抓取与无头模式掌握了基础监控我们可以玩点更“高级”的。CDP的魅力远不止于监控。5.1 拦截并修改网络请求想象一个场景在自动化测试中你想模拟一个API接口返回错误状态码或者修改某个JS文件的响应内容。CDP的Network域提供了Network.setRequestInterception和Network.continueInterceptedRequest等命令来实现。原理启用请求拦截后浏览器在发送每个匹配规则的请求前会暂停并触发Network.requestIntercepted事件。我们的脚本可以在这个事件中决定是放行原请求、修改后放行还是直接返回一个模拟的响应。def setup_request_interception(driver): 设置请求拦截修改特定URL的请求头或响应 # 1. 启用Network域和请求拦截 driver.execute_cdp_cmd(Network.enable, {}) # 2. 设置拦截模式可以指定URL模式 driver.execute_cdp_cmd(Network.setRequestInterception, { patterns: [ {urlPattern: *://api.example.com/*, resourceType: XHR, interceptionStage: Request}, # 拦截所有XHR类型的来自api.example.com的请求 ] }) # 3. 监听拦截事件 def on_request_intercepted(event): params event[params] interception_id params[interceptionId] request params[request] print(f拦截到请求: {request[url]}) # 示例1修改请求头例如添加一个自定义Token modified_headers request.get(headers, {}) modified_headers[X-Custom-Token] MySecretToken123 # 示例2直接返回一个模拟的响应例如让某个API返回404 # if block_this in request[url]: # mock_response { # errorReason: BlockedByClient, # # 或者使用 rawResponse 字段返回一个完整的HTTP响应字节 # } # driver.execute_cdp_cmd(Network.continueInterceptedRequest, { # interceptionId: interception_id, # errorReason: BlockedByClient # }) # return # 继续请求并带上修改后的头 driver.execute_cdp_cmd(Network.continueInterceptedRequest, { interceptionId: interception_id, headers: modified_headers }) driver.add_cdp_listener(Network.requestIntercepted, on_request_intercepted)注意事项请求拦截功能非常强大但使用不当会严重影响页面加载性能。务必精确指定需要拦截的URL模式避免拦截过多无关请求。此外修改响应体rawResponse需要将HTTP响应包括状态行、头、体编码成base64格式操作较为复杂需谨慎处理。5.2 抓取性能时间线数据除了网络CDP的Performance域可以获取到类似Chrome DevTools中Performance面板记录的详细时间线数据包含布局、绘制、脚本执行等耗时。def capture_performance_timeline(driver, output_fileperformance_timeline.json): 捕获页面性能时间线数据 # 1. 启用Performance域 driver.execute_cdp_cmd(Performance.enable, {}) # 2. 开始记录时间线 driver.execute_cdp_cmd(Performance.setTimeDomain, {timeDomain: timeTicks}) # 可以设置缓冲区大小 # driver.execute_cdp_cmd(Performance.setBufferSize, {bufferSize: 10000}) # 3. 执行你想要测量的页面操作例如导航或点击 driver.get(https://www.example.com) time.sleep(3) # 等待加载 # 4. 停止记录并获取数据 # 获取指标 metrics driver.execute_cdp_cmd(Performance.getMetrics, {}) print(性能指标:, metrics) # 获取时间线数据包含详细事件 timeline_response driver.execute_cdp_cmd(Performance.getBufferedEntries, {}) # 这个命令可能返回空取决于浏览器实现。更可靠的方式是监听 Performance.entryAdded 事件 # 5. 禁用Performance域 driver.execute_cdp_cmd(Performance.disable, {}) # 将数据保存到文件 if timeline_response and entries in timeline_response: with open(output_file, w) as f: json.dump(timeline_response[entries], f, indent2) print(f性能时间线数据已保存至 {output_file})5.3 在无头模式下的稳定运行对于服务器环境或需要并行执行大量任务的场景无头模式是标配。使用CDP时无头模式需要注意一些细节。from selenium.webdriver.chrome.options import Options chrome_options Options() chrome_options.add_argument(--headlessnew) # Selenium 4.8 推荐使用 new chrome_options.add_argument(--disable-gpu) chrome_options.add_argument(--no-sandbox) chrome_options.add_argument(--disable-dev-shm-usage) # 解决共享内存问题对Docker/Linux环境很重要 chrome_options.add_argument(--window-size1920,1080) # 设置窗口大小某些页面布局依赖于此 # 对于CDP无头模式本身不影响其功能。 # 但有些网站会检测无头浏览器你可以通过CDP注入JS来修改navigator.webdriver等属性需注意相关合规要求。 # 示例覆盖webdriver属性仅用于教育目的请遵守网站使用条款 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); })6. 常见问题排查与实战心得在实际项目中融合CDP你肯定会遇到各种稀奇古怪的问题。我把一些典型的坑和解决方案整理如下希望能帮你节省大量调试时间。6.1 CDP命令执行失败或无效问题现象调用driver.execute_cdp_cmd后没有效果或者抛出异常。可能原因1浏览器/驱动版本不匹配。这是最常见的原因。请严格按照第2部分的要求确保Chrome和ChromeDriver主版本号一致。可能原因2命令或参数格式错误。CDP命令和参数名对大小写敏感且结构必须完全正确。最好的参考是Chrome DevTools Protocol官方文档可在Chrome源码仓库或DevTools网站找到。使用driver.execute_cdp_cmd(‘Domain.command’, {})格式。可能原因3未先启用对应的域。在执行某个域的命令如Network.enable前需要先确保该域可用。但Selenium 4中execute_cdp_cmd会自动处理会话对于大多数命令你不需要显式“启用”除了像网络拦截这种需要持续监听事件的功能。排查技巧先尝试执行一个最简单的、肯定能成功的命令来测试通道是否畅通例如driver.execute_cdp_cmd(‘Browser.getVersion’, {})。如果这个都失败基本就是环境或驱动问题。6.2 网络事件监听不到或不全问题现象Network.requestWillBeSent事件没触发或者只捕获到部分请求。可能原因1监听器注册时机太晚。必须在页面开始加载之前就执行Network.enable和add_cdp_listener。最佳实践是在driver.get(url)之前完成所有监听器的设置。可能原因2页面包含iframe或Service Worker。来自不同源跨域iframe的请求或者Service Worker发出的请求可能不会触发默认的监听。CDP的Network域可以配置Network.setRequestInterception来捕获更多类型的请求但这会更复杂。可能原因3请求是浏览器缓存。如果资源直接从磁盘或内存缓存加载可能不会触发网络事件。你可以在启用监控时禁用缓存driver.execute_cdp_cmd(‘Network.setCacheDisabled’, {‘cacheDisabled’: True})。实操心得为了确保捕获到所有导航请求包括重定向建议的代码顺序是创建driver。立即创建并启动监控器执行Network.enable。然后才执行driver.get(url)。6.3 性能开销与稳定性问题描述启用大量CDP监听尤其是网络全量监控会显著增加浏览器内存占用并可能降低自动化执行速度在长时间运行的脚本中可能导致浏览器不稳定。优化建议1按需监听。不要一股脑启用所有域的所有事件。仔细分析需求只监听必要的事件。例如如果只关心XHR请求可以在Network.enable后通过事件参数过滤。优化建议2及时清理。对于长时间运行的脚本如果某个阶段的监控不再需要调用Network.disable()来停止事件流释放资源。优化建议3增加缓冲区。如果预期有大量网络请求可以在Network.enable时适当增加maxTotalBufferSize和maxResourceBufferSize参数防止事件丢失。但注意这会增加内存消耗。稳定性技巧在try...except...finally块中包裹核心逻辑并在finally中确保调用driver.quit()。对于无头模式可以考虑定期重启浏览器实例来清理状态。6.4 异步事件处理的坑问题现象事件回调函数里操作了浏览器DOM或执行了Selenium命令导致线程死锁或意外错误。根本原因CDP事件回调通常运行在Selenium/ChromeDriver管理的某个内部线程中与主脚本线程不同。在此回调中直接调用driver.find_element或执行JS可能会引发并发问题。解决方案在事件回调中尽量避免执行复杂的、与浏览器状态交互的操作。如果必须操作可以考虑将需要处理的数据放入一个线程安全的队列如queue.Queue然后在主线程中从队列取出并处理。或者使用driver.execute_script执行一些只读的、轻量的JS查询。import queue data_queue queue.Queue() def on_request_will_be_sent(event): url event[params][request][url] # 不直接操作driver而是将数据放入队列 data_queue.put((request, url)) # 在主线程循环中处理队列 while True: try: item_type, data data_queue.get(timeout1.0) if item_type request: print(f在主线程中处理请求URL: {data}) # 这里可以安全地使用driver # driver.find_element(...) except queue.Empty: break将这些排查技巧和经验融入你的开发习惯能极大提升使用CDP进行浏览器自动化开发的效率和成功率。记住CDP是一把锋利的剑它让你能深入到浏览器的每一个角落但同时也要求你对浏览器的工作原理有更深入的理解。从简单的网络监控开始逐步尝试拦截、性能分析、模拟输入等高级功能你会发现自己对Web自动化的掌控力达到了一个全新的层次。

相关新闻