我正在尝试从动态加载的页面中抓取数据。为此,我使用无头浏览器 puppeteer
在代码中Puppeteer 可以被视为 headlessBrowserClient 。
主要挑战是在收到所需数据后立即正常关闭浏览器。但是,如果您在评估执行之前完成关闭它-评估执行进度将丢失。
evaluateCustomCode 是一个可以像在Chrome Dev工具中运行它一样调用的函数。
要控制网络请求和puppeteer API的异步流程-我使用了异步发生器,该发生器封装了上述所有逻辑。
问题是我觉得代码有异味,但看不到任何更好的解决方案。
想法?
module.exports = function buildClient (headlessBrowserClient) {
const getPageContent = async (pageUrl, evaluateCustomCode) => {
const request = sendRequest(pageUrl)
const { value: page } = await request.next()
if (page) {
const pageContent = await page.evaluate(evaluateCustomCode)
request.next()
return pageContent
}
}
async function * sendRequest (url) {
const browser = await headlessBrowserClient.launch()
const page = await browser.newPage()
const state = {
req: { url },
}
try {
await page.goto(url)
yield page
} catch (error) {
throw new APIError(error, state)
} finally {
yield browser.close()
}
}
return {
getPageContent,
}
}
答案 0 :(得分:0)
您可以将waitForFunction
或waitFor
和evaluate
与Promise.all
一起使用。无论网站的动态程度如何,您都在等待最后的结果,然后在发生这种情况时关闭浏览器。
由于我无权访问您的动态网址,因此我将使用一些随机变量和延迟作为示例。变量返回真值后,它将解决。
await page.waitForFunction((()=>!!someVariableThatShouldBeTrue);
在评估代码后,如果动态页面实际上在某处创建了选择器?在这种情况下,
await page.waitFor('someSelector')
现在回到您的customCode,让我为您重新命名,
await page.evaluate(customCode)
其中的customCode是将变量someVariableThatShouldBeTrue
设置为true的地方。坦白地说,它可以是任何内容,请求,字符串或其他任何内容。可能性是无限的。
您可以在page.evaluate 中放一个承诺,最近的铬对它们的支持很好。因此,以下内容也将起作用,一旦加载函数/数据,请解决。确保customCode是异步函数或返回promise。
const pageContent = await page.evaluate(CustomCode);
好的,现在我们已经准备好了所有必需品。我对代码进行了一些修改,以免对我:D,
module.exports = function buildClient(headlessBrowserClient) {
return {
getPageContent: async (url, CustomCode) => {
const state = {
req: { url },
};
// so that we can call them on "finally" block
let browser, page;
try {
// launch browser
browser = await headlessBrowserClient.launch()
page = await browser.newPage()
await page.goto(url)
// evaluate and wait for something to happen
// first element returns the pageContent, but whole will resolve if both ends truthy
const [pageContent] = await Promise.all([
await page.evaluate(CustomCode),
await page.waitForFunction((() => !!someVariableThatShouldBeTrue))
])
// Or, You realize you can put a promise inside page.evaluate, recent chromium supports them very well
// const pageContent = await page.evaluate(CustomCode)
return pageContent;
} catch (error) {
throw new APIError(error, state)
} finally {
// NOTE: Maybe we can move them on a different function
await page.close()
await browser.close()
}
}
}
}
您可以根据需要进行更改和调整。我没有测试最终代码(因为我没有APIError,evaluateCustomCode等),但是它应该可以工作。
它没有所有这些生成器和类似的东西。 承诺,这就是处理动态页面的方法:D。
PS:IMO,这样的问题更适合the code review。