如何通过Playwright的CDP会话提取Network面板请求发起链元素?
场景说明
我需要提取网络请求的直接父请求URL,例如在Chromium DevTools的Network面板中,某请求https://uk-script.dotmetrics.net/hit.gif?id=13934&url=https%3A%2F%2Fwww.bbc.com%2Fweather%2F1264527&dom=www.bbc.com&r=1686291576433&pvs=1&pvid=f4aaa4fd-2def-4a70-ab77-c4e11e570388&c=true&tzOffset=-330&doorUrl=http%3a%2f%2fuk-script.dotmetrics.net%2fdoor.js%3fd%3dwww.bbc.com%26t%3dweathergnl的直接父请求是https://uk-script.dotmetrics.net/door.js?d=www.bbc.com&t=weathergnl,这正是我要提取的信息。
尝试的方法
我使用Playwright开启CDP会话拦截网络请求,提取每个请求的Network.Initiator对象,代码如下:
const { chromium } = require('playwright'); (async () => { // Launch browser with headless set to false const browser = await chromium.launch({ headless: false }); let cnt = 1; // Create a new context with CDP enabled const context = await browser.newContext({ devtools: true, }); // Create a new page const page = await context.newPage(); // Enable the CDP session const client = await page.context().newCDPSession(page); // Enable network interception await client.send('Network.enable'); // Listen for network requests client.on('Network.requestWillBeSent', (event) => { console.log("::::::::::Network Call "+ cnt +"::::::::::"); console.log('Request URL: ', event.request.url); console.log('\nInitiator: ', event.initiator); // console.log('\nReferer URL: ', event.request.headers); console.log("\n"); cnt++; }); // Navigate to the website await page.goto('https://www.bbc.com/weather/1264527'); // Perform any further actions using the CDP session if needed // For example, you can use client.send() to interact with the CDP API. // Scroll down the page bit by bit const SCROLL_AMOUNT = 250; // Adjust the scroll amount as needed const pageHeight = await page.evaluate(() => document.documentElement.scrollHeight); let currentScroll = 0; while (currentScroll < pageHeight) { await page.evaluate((scrollAmount) => { window.scrollBy(0, scrollAmount); }, SCROLL_AMOUNT); currentScroll += SCROLL_AMOUNT; await page.waitForTimeout(500); // Adjust the timeout as needed } // Close the browser await browser.close(); })();
遇到的问题
针对同一个请求,通过上述代码获取到的Initiator URL和DevTools窗口中显示的不一致,想知道原因,以及有没有其他方法可以获取请求发起链中显示的准确发起者信息。
原因分析
- 事件触发时机差异:
Network.requestWillBeSent在请求即将发送时触发,此时请求的发起链信息可能还未完全确定。DevTools显示的是请求完成后最终的发起链,信息更完整。 - 发起者信息的粒度问题:如果请求由脚本动态发起,CDP返回的
initiator对象可能只指向当前执行脚本的上下文URL,而非直接发起请求的具体脚本文件。DevTools会追踪到更细粒度的发起脚本。 - Playwright上下文干扰:开启
devtools: true可能会影响CDP会话的信息采集逻辑,导致部分细节丢失。
解决方法
方法1:监听Network.requestWillBeSentExtraInfo事件
该事件提供更详细的发起信息,替换原代码中的Network.requestWillBeSent:
client.on('Network.requestWillBeSentExtraInfo', (event) => { console.log("::::::::::Network Call "+ cnt +"::::::::::"); console.log('Request URL: ', event.requestUrl); console.log('\nInitiator Info: ', event.initiator); console.log("\n"); cnt++; });
方法2:使用Playwright原生request事件
Playwright原生API已封装了发起链信息,无需直接操作CDP,使用更简便:
page.on('request', (request) => { console.log("::::::::::Network Call "+ cnt +"::::::::::"); console.log('Request URL: ', request.url()); console.log('\nInitiator URL: ', request.initiator().url); console.log("\n"); cnt++; });
方法3:获取完整发起链(请求完成后)
通过Network.getRequestInitiatorChain方法,在请求完成后获取完整的发起链:
client.on('Network.requestFinished', async (event) => { const initiatorChain = await client.send('Network.getRequestInitiatorChain', { requestId: event.requestId }); console.log("Request URL: ", event.request.url); console.log("Full Initiator Chain: ", initiatorChain.chain); });
注意事项
- 确保提前调用
await client.send('Network.enable')启用网络域。 - 对于动态加载的内容,可结合
page.waitForLoadState('networkidle')等待页面稳定后再采集信息,避免遗漏。
内容的提问来源于stack exchange,提问作者Manoj Raghavendran

