【问题标题】:How Do I Run page.screenshot Inside page.evaluate in Puppeteer?如何在 Puppeteer 中运行 page.screenshot 内部 page.evaluate?
【发布时间】:2021-06-11 00:54:13
【问题描述】:

我正在创建一个屏幕抓取工具,它需要抓取页面内容并对其进行截图。为此,我正在使用 Puppeteer,但遇到了障碍。当我尝试调用在 page.evaulate 内运行 page.screenshot 的函数时,我收到一个错误,指出该函数未定义。

这是我的代码:

async function getContent(clink, ce, networkidle, host, filepath) {
        let browser = await puppeteer.launch();
        let cpage = await browser.newPage();
        await cpage.goto(clink, { waitUntil: networkidle });
        let content = await cpage.evaluate((clink, ce, networkidle, host, filepath, pubDate) => {
            let results = '';
            let enclurl = clink;
            takeScreenshot(enclurl, filepath, networkidle)
                .then(() => {
                    console.log("Screenshot taken");
                })
                .catch((err) => {
                    console.log("Error occured!");
                    console.dir(err);
                });
            results += '<title><![CDATA[' + 'test' + ']]</title>';
            results += '<description><![CDATA[' + '<img src="' + host + filepath.slice(1) + '">' + document.querySelector(ce).innerHTML + ']]</description>';
            results += '<link>' + clink + '</link>';
            results += '<guid>' + clink + '</guid>';
            results += '<pubDate>' + pubDate + '</pubDate>';
            return results;
        }, clink, ce, networkidle, host, filepath, pubDate);
        await cpage.close();
        await browser.close();
        return content;
    }

该代码应在创建 RSS 格式的 xml 文件之前返回项目。然后,这些文件的 URL 将被添加到 WPRobot 活动中。最终目标将是一个搜索引擎,它使用 Wordpress 来聚合页面的主要内容以及来源的完整屏幕截图。

takeScreenshot函数如下:

async function takeScreenshot(enclurl, filepath, networkidle) {
        let browser = await puppeteer.launch();
        let page = await browser.newPage();
        await page.goto(enclurl, { waitUntil: networkidle });
        let buffer = await page.screenshot({
            path: filepath
        });

        await page.close();
        await browser.close();
    }

在 page.evaluate 之外调用时,截屏效果很好。我得到的确切错误是“takeScreenshot 未定义”。我有另一个函数可以解析 RSS 提要并截取它们的源 URL,但它根本不使用 page.evaluate。

我现在已经在调用 getContent() 之前将 takeScreenshot 调用添加到我的代码的早期部分,但现在似乎 getContent() 总是返回未定义。我的新 getContent() 内容如下:

 async function getContent(clink, ce, networkidle) {
        let browser = await puppeteer.launch();
        let cpage = await browser.newPage();
        await cpage.goto(clink, { waitUntil: networkidle });
        let content = await cpage.evaluate((ce) => {
            let cefc = ce.charAt(0);
            if (cefc != '.') {
                ce = '#' + ce;
            }
            console.log('ce=' + ce);
            let results = document.querySelector(ce).innerHTML;
            return results;
        }, ce);
        await cpage.close();
        await browser.close();
        return content;
    }

我也没有看到 console.log('ce=' + ce) 被写入日志。将 console.log 移出 page.evaluate 循环后,它记录了内容的适当值,即具有指定类的元素的 HTML。尽管返回内容的值仍未定义。

【问题讨论】:

标签: javascript node.js web-scraping puppeteer headless


【解决方案1】:

Page.evaluate 有一种奇怪且不直观的工作方式:

函数的代码(在你的情况下: (clink, ce, networkidle, host, filepath, pubDate) => {...} )不会在你的脚本中执行。该函数在 puppeteer 中序列化,并发送到无头浏览器。

如果你想从评估函数内部调用一个函数,通常(但不是在这种情况下)你可以使用以下技巧之一:How to pass a function in Puppeteers .evaluate() method?

但是在这种情况下......有一个问题! 在 takeScreenshot 内部还有其他不能在 puppeteer 的无头浏览器中使用的函数,即 puppeteer.launch(); 等。这些函数需要很多依赖项(和相同的可执行文件)...并且不能通过。

要执行您需要的操作,请将代码的屏幕截图部分移出评估:

async function getContent(clink, ce, networkidle, host, filepath) {
    let browser = await puppeteer.launch();
    let cpage = await browser.newPage();
    await cpage.goto(clink, { waitUntil: networkidle });
    let content = await cpage.evaluate((clink, ce, networkidle, host, filepath, pubDate) => {
        let results = '';
        let enclurl = clink;

        results += '<title><![CDATA[' + 'test' + ']]</title>';
        results += '<description><![CDATA[' + '<img src="' + host + '{REPL_ME}' + '">' + document.querySelector(ce).innerHTML + ']]</description>';
        results += '<link>' + clink + '</link>';
        results += '<guid>' + clink + '</guid>';
        results += '<pubDate>' + pubDate + '</pubDate>';
        return results;
    }, clink, ce, networkidle, host, filepath, pubDate);

    await takeScreenshot(enclurl, filepath, networkidle);
    content = content.replace('{REPL_ME}', filepath)   

    await cpage.close();
    await browser.close();
    return content;
}

【讨论】:

  • 谢谢,我一直在这样做,所以现在我在我的 main 函数中调用 getContent 之前的 takeScreenshot,并且只使用 RSS 项目中图像 URL 的文件路径。
  • 现在我正在研究一个新问题,涉及在类属性具有多个类(例如 class="text-left p-2" )时使用 querySelector()。到目前为止,我选择具有此类类的元素的努力返回为空。
  • 我英语说得不太好。例如,如果要选择:

    ,则选择器为:".c1, .c2" 如果要选择

    选择器是“.c1.c2”,如果我不明白,对不起!

  • 似乎我谈论我的解决方案工作的太早了。现在截图已经很好了,但是无论类值是什么,getContent() 的结果总是不确定的。我即将用新版本的 getContent() 更新我的问题
  • 您看不到console.log,原因相同:您登录的是无头浏览器,而不是控制台。尝试返回 ce 并从评估函数中打印出来进行调试
猜你喜欢
  • 2021-04-21
  • 2020-05-09
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-25
  • 1970-01-01
相关资源
最近更新 更多