我正在使用Puppeteer来爬行网页。我想解析页面内的URL,等等。我知道可以将功能移出page.evaluate
,但这是主要问题之外。问题是如何在页面上注入任意脚本,以便可以在page.evaluate
中使用脚本中的变量/函数。
就我而言,我正在使用lil-uri。我基本上是这样的:
var puppeteer = require('puppeteer')
var URL = require('lil-uri')
puppeteer.launch().then(browser => {
browser.newPage().then(page => {
page.goto('https://foo.com').catch(onerror).then(() => {
page.evaluate(fetchLinks).catch(onerror)
})
})
// })
})
function onerror(err) {
console.log('ERRR', err)
}
function fetchLinks() {
var linkEls = document.querySelectorAll('a')
var links = []
for (var i = 0, n = linkEls.length; i < n; i++) {
var el = linkEls[i]
// PARSE URL
var url = parseUrl(el.getAttribute('href'))
links.push(url)
}
return links
function parseUrl(href) {
// REF THE URL LIBRARY
var url = URL(href)
var url2 = url.path()
var query = []
var q = url.query()
if (Object.keys(q).length) {
// query.push(...)
}
if (query.length) {
url2 += '?' + query.join('&')
}
return url2
}
}
这不起作用,因为require('lil-uri')
在Node.js脚本的范围内,而实际上却在page.evaluate
的上下文中使用。
问题是,如何在页面中正确包含parseUrl
和URL
函数,以便可以在page.evaluate
的上下文中使用它们。
此外,如您所见,我将parseUrl
函数放在 中fetchLinks
函数中,这并不理想,因为我无法在我评估的其他函数之间重用它在页面上。我希望能够在window.parseUrl = parseUrl
的上下文中执行类似page.evaluate
的操作,但是我也不知道该怎么做。想知道是否可以展示如何做这两件事:
答案 0 :(得分:1)
您可以使用page.exposeFunction
将Node.js环境中的函数公开给页面本身。引用文档:
该方法在页面的
name
对象上添加了一个名为window
的函数。调用该函数时,该函数在node.js中执行puppeteerFunction
并返回一个Promise,该Promise解析为返回值puppeteerFunction
。
代码示例
下面的代码会将您的函数parseUrl
暴露给页面。然后,您可以从window.parseUrl
内部通过page.evaluate
调用该函数。
const puppeteer = require('puppeteer');
function parseUrl(href) {
// ...
return '...';
}
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.exposeFunction('parseUrl', href => parseUrl(href));
await page.evaluate(async () => {
const url = 'http://...';
const parsedUrl = await window.parseUrl(url);
});
await browser.close();
})();
关于URL解析的附注
这与您的问题没有直接关系,但您不一定需要解析Node.js环境中的URL。为此,有一个JavaScript API URL
,它使您可以像这样在浏览器内部解析URL:
const url = new URL('http://www.example.org/path123');
console.log(url.pathname); // will print: /path123
根据您的用例,您甚至可能不需要公开某个功能,因为您可以在浏览器内部完成该功能。