木偶操纵者的网页抓取

问题描述 投票:0回答:1

我正在编写一个 html,通过 puppeteer 从互联网上抓取数据,代码如下:

const puppeteer = require('puppeteer');

(async ()=>
{
    const browser = await puppeteer.launch()
    const page = await browser.newPage()

    await page.goto("https://www.info.gov.hk/gia/wr/202404/26.htm")

    const bulletin_urls = await page.$$('div.leftBody');

    for(const bulletin_url of bulletin_urls)
    {
        try
        {
            const bulletin_name = await page.evaluate(el => el.textContent, bulletin_url)
            console.log(bulletin_name)

            const single_url = await page.evaluate(el => el.getAttribute("href"), bulletin_url)
            console.log(single_url) 
        }
        catch(err)
        {
        
        }
    }
    await browser.close()
}) ();

我想抓取所有新闻天气的2条信息:

  1. 公告名称
  2. 超链接的 URL。

我在抓取 1 时成功,在抓取 2 时失败。我得到的只是 null。我尝试将我的代码修改为

const single_url = await page.evaluate(el => el.querySelector(".NEW").getAttribute("href"), bulletin_url)
console.log(single_url)

但是,它只返回第一个网址,但不是全部网址。我该怎么做才能通过一个命令收集超链接的所有 url?任何建议将不胜感激。

javascript web-scraping puppeteer
1个回答
0
投票

您正在选择容器,而不是其中的特定元素。尝试这样做:

const puppeteer = require("puppeteer"); // ^22.7.1

const url = "<Your URL>";

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  await page.setJavaScriptEnabled(false);
  await page.setRequestInterception(true);
  page.on("request", req => {
    if (req.url() === url) {
      req.continue()
    } else {
      req.abort();
    }
  });
  await page.goto(url, {waitUntil: "domcontentloaded"});
  const data = await page.$$eval(".NEW", els => els.map(el => ({
    text: el.textContent,
    href: el.href,
  })));
  console.log(data);
  console.log(data.length); // => 125
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

这里的大部分代码都是禁用 JS 并阻止资源请求,因为数据就在静态 HTML 中,这意味着您不需要 Puppeteer。只需使用 Fetch 和 Cheerio,一个轻量级 HTML 解析器。

const cheerio = require("cheerio"); // ^1.0.0-rc.12

const url = "<Your URL>";

fetch(url)
  .then(res => {
    if (!res.ok) {
      throw Error(res.statusText);
    }

    return res.text();
  })
  .then(html => {
    const $ = cheerio.load(html);
    const data = [...$(".NEW")].map(e => ({
      text: $(e).text(),
      href: $(e).attr("href"),
    }));
    console.log(data);
    console.log(data.length);
  })
  .catch(err => console.error(err));

Puppeteer 未优化(没有任何块,有 JS,没有

{waitUntil: "domcontentloaded"}
:

real 0m4.180s
user 0m0.981s
sys  0m0.198s

Puppeteer 优化(如上图):

real 0m1.181s
user 0m0.642s
sys  0m0.171s

取来并欢呼:

real 0m0.426s
user 0m0.194s
sys  0m0.045s

重要提示:永远不要将

catch (err) {}
与空块一起使用,除非你真的想阻止错误。在本例中,您正在调试脚本,因此您最不想做的就是丢弃重要的调试信息。

此外,避免使用元素句柄。它们冗长、缓慢且容易出错。除了发出信任事件之外,没有必要使用它们,而在网络抓取中通常不需要这些事件,除非填写需要事件处理程序触发的表单。

$$eval
就是要走的路。

© www.soinside.com 2019 - 2024. All rights reserved.