无法即时刮取和打印链接

时间:2018-10-08 21:23:11

标签: node.js web-scraping promise puppeteer

我已经在node.js中编写了一个脚本,以从网页中抓取links的不同标题。当我执行以下脚本时,将在控制台中打印undefined而不是我后面的links。我定义的选择器是准确的。

我不希望将links放入数组并返回结果;相反,我希望即时打印它们。由于我刚接触node.jspuppeteer来编写脚本,因此我无法弄清自己犯的错误。

这是我的脚本(Link to that site):

const puppeteer = require('puppeteer');
function run () {
    return new Promise(async (resolve, reject) => {
        try {
            const browser = await puppeteer.launch();
            const page = await browser.newPage();
            await page.goto("https://stackoverflow.com/questions/tagged/web-scraping");
            let url = await page.evaluate(() => {
                let items = document.querySelectorAll('a.question-hyperlink');
                items.forEach((item) => {
                    //would like to keep the following line intact 
                    console.log(item.getAttribute('href'));
                });
            })
            browser.close();
            return resolve(url);
        } catch (e) {
            return reject(e);
        }
    })
}
run().then(console.log).catch(console.error);
  

如果我考虑声明一个空数组results并在其中存储已抓取的链接并最终返回results,则以下脚本可以正常工作,但我不希望这样。我想坚持我上面尝试的方式,就像即时打印结果一样。

const puppeteer = require('puppeteer');
function run () {
    return new Promise(async (resolve, reject) => {
        try {
            const browser = await puppeteer.launch();
            const page = await browser.newPage();
            await page.goto("https://stackoverflow.com/questions/tagged/web-scraping");
            let urls = await page.evaluate(() => {
                let results = [];
                let items = document.querySelectorAll('a.question-hyperlink');
                items.forEach((item) => {
                    results.push({
                        url:  item.getAttribute('href'),
                    });
                });
                return results;
            })
            browser.close();
            return resolve(urls);
        } catch (e) {
            return reject(e);
        }
    })
}
run().then(console.log).catch(console.error);

再一次:我的问题是如何在不将其存储在数组中的情况下即时打印类似console.log(item.getAttribute('href'));的链接?

3 个答案:

答案 0 :(得分:2)

要在console.log()中运行evaluate(),只需在定义页面的位置复制下面的行

page.on('console', obj => console.log(obj._text));

所以现在整个片段现在都是这样

const puppeteer = require('puppeteer');
function run () {
    return new Promise(async (resolve, reject) => {
        try {
            const browser = await puppeteer.launch();
            const page = await browser.newPage();
            page.on('console', obj => console.log(obj._text));
            await page.goto("https://stackoverflow.com/questions/tagged/web-scraping");
            let url = await page.evaluate(() => {
                let items = document.querySelectorAll('a.question-hyperlink');
                items.forEach((item) => {
                    //would like to keep the following line intact 
                    console.log(item.getAttribute('href'));
                });
            })
            browser.close();
            return resolve(url);
        } catch (e) {
            return reject(e);
        }
    })
}
run().then(console.log).catch(console.error);

希望获得帮助

答案 1 :(得分:1)

该库看起来有点尴尬,但在github- https://github.com/GoogleChrome/puppeteer/issues/628

上找到了从该线程获取href的正确方法。

我要使用的工作代码是使用await page.$$eval

async function getStackoverflowLinks(){
  return new Promise(async(resolve, reject)=>{
    console.log(`going to launch chromium via puppeteer`)
    const browser = await puppeteer.launch()
    console.log(`creating page/tab`)
    const page = await browser.newPage()
    await page.goto('https://stackoverflow.com/questions/tagged/web-scraping')
    console.log("fetched SO web-scraping, now parsing link href")

    let matches = await page.$$eval('a.question-hyperlink', hrefs=>hrefs.map((a)=>{
      return a.href
    })) // $$eval and map version, $$eval returns an array
    console.log("matches = ", matches.length)

    await browser.close()
    resolve(matches)
  })
}

getStackoverflowLinks()
.then(hrefs=>{
  console.log("hrefs: ", hrefs)
}) 

答案 2 :(得分:0)

注意事项

  • async函数将返回诺言。
  • new Promise也将返回承诺。

请注意,您只需使用.console事件即可即时打印它们。用法

page.on("console", msg => console.log(msg.text()));
await page.evaluate(async => {
  console.log("I will be printed on node console too")
})

已在this answer上讨论了高级用法。