使用Puppeteer与startsWith()时结果不一致的问题排查
问题原因与解决办法
这是个很典型的Puppeteer上下文隔离问题,我来给你捋清楚:
核心原因:上下文隔离
Puppeteer的$$eval()方法里的回调函数是在浏览器的前端上下文环境中执行的,而你定义的root变量是在Node.js的后端上下文里的——这两个环境是完全隔离的,浏览器环境根本访问不到Node.js里的root变量!
所以当你在回调里写link.startsWith(root)时,root实际是undefined,自然过滤不出任何匹配的链接;而直接写字符串'https://tiempone.com'时,这个字符串是直接在浏览器上下文里解析的,能正常匹配到目标链接,所以结果符合预期。
解决办法:传递变量到浏览器上下文
要让浏览器上下文能访问到Node.js里的变量,你可以利用$$eval()的第三个(及以后)参数,把Node端的变量传递过去。修改后的代码如下:
const root = 'https://tiempone.com'; const browser = await puppeteer.launch({headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox']}); const page = await browser.newPage(); await page.goto(root, {waitUntil: 'networkidle0'}); // 将root作为额外参数传入$$eval,让浏览器上下文可以访问到 let urlsToVisit_1 = await page.$$eval( 'a', (links, root) => links.map(link => link.href).filter(link => link.startsWith(root)), root // 这里是传递给回调的参数 ); let urlsToVisit_2 = await page.$$eval('a', links => links.map(link => link.href).filter(link => link.startsWith('https://tiempone.com'))); console.log(urlsToVisit_1); // 现在和urlsToVisit_2结果完全一致 console.log(urlsToVisit_2); // ['https://tiempone.com/', 'https://tiempone.com/projects', ..., 'https://tiempone.com/privacy']
简单来说,page.$$eval(selector, pageFunction[, ...args])里的...args就是专门用来把Node.js环境的变量序列化后传递给浏览器端的pageFunction的,只要是可序列化的变量(字符串、数字、普通对象等)都可以用这种方式传递。
内容的提问来源于stack exchange,提问作者Erik Martín Jordán
相关产品推荐
相关产品推荐

