如何合理地将字符串值传入Puppeteer的page.evaluate方法中?
怎么把字符串合理传入Puppeteer的page.evaluate方法?
哎,你这问题其实是踩了Puppeteer上下文隔离的坑啦!page.evaluate里的回调函数是跑在浏览器页面的独立环境里的,和你Node.js代码里的变量(比如那个selector)根本不共享,直接引用肯定找不到。我给你说几个靠谱的解决办法,从简单到复杂,你挑合适的用:
方法一:直接当参数传(最推荐!)
这是官方最推崇的方式,简单直接还高效。你只需要把Node.js侧的selector作为page.evaluate的第二个参数传进去,然后在浏览器侧的回调里接收就行:
crawler.selectorReturner = async function(page, selector) { await page.waitForSelector(selector); // 把selector作为参数传给evaluate的回调 const getSelections = await page.evaluate((targetSelector) => { const resultsobj = { selections: [] }; // 这里直接用传入的targetSelector就行 const selections = Array.from(document.body.querySelectorAll(targetSelector), el => ({ selected: el.selected, text: el.textContent.trim() // 可以根据你的需求加其他属性 })); resultsobj.selections = selections; return resultsobj; }, selector); return getSelections; };
这种方式完全不需要额外的API调用,一步到位,比你原来用exposeFunction的方式简洁多了。
方法二:用exposeFunction(不推荐,除非特殊需求)
你原来尝试用exposeFunction,其实也能成,但纯属绕远路——因为这个API是用来把Node.js的函数暴露给浏览器用的,比如你要在浏览器里调用Node.js的文件读写能力,才用得上。如果只是传个字符串,真没必要。不过还是给你写个正确的版本参考:
crawler.selectorReturner = async function(page, selector) { await page.waitForSelector(selector); // 把获取selector的函数暴露给浏览器,注意这个函数是在Node.js环境跑的,所以能直接用selector await page.exposeFunction('getTargetSelector', () => selector); const getSelections = await page.evaluate(async () => { const resultsobj = { selections: [] }; // 注意:exposeFunction暴露的函数都是异步的,必须await const selector = await getTargetSelector(); const selections = Array.from(document.body.querySelectorAll(selector), el => ({ selected: el.selected, text: el.textContent.trim() })); resultsobj.selections = selections; return resultsobj; }); return getSelections; };
为啥不推荐?因为跨上下文调用会有额外的性能开销,代码还更啰嗦,完全没必要用它传简单字符串。
方法三:注入全局变量(适合多场景复用)
如果你的变量特别多,或者要在多个evaluate调用里复用,可以用page.addScriptTag把变量注入到浏览器的全局window对象里:
crawler.selectorReturner = async function(page, selector) { await page.waitForSelector(selector); // 把selector注入到浏览器全局,用JSON.stringify处理特殊字符避免引号冲突 await page.addScriptTag({ content: `window.targetSelector = ${JSON.stringify(selector)};` }); const getSelections = await page.evaluate(() => { const resultsobj = { selections: [] }; // 直接用全局的window.targetSelector const selections = Array.from(document.body.querySelectorAll(window.targetSelector), el => ({ selected: el.selected, text: el.textContent.trim() })); resultsobj.selections = selections; return resultsobj; }); return getSelections; };
这种方式要注意字符串转义的问题,用JSON.stringify就能避免selector里有引号或者特殊字符导致的语法错误。
总结一下,方法一绝对是你的最优解,代码简洁高效,完全解决你传字符串的需求,别折腾后面两种复杂的方式啦!
内容的提问来源于stack exchange,提问作者user254694
相关产品推荐
相关产品推荐

