使用 Fetch 和 Cheerio 清空结果抓取站点

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

出于兴趣,我决定为自己从该网站收集数据(名称、每晚价格、评级),但遇到了误解。我没有得到任何输出。我重写了其他库,但他们说这个更好。

const cheerio = require("cheerio"); 
let fs = require('fs');
const base = "https://ostrovok.ru/hotel/russia/adler/";

(async () => {
  let url = "?page=1";
  const data = [];

  for (let i = 0; i < 176; i++) {
    try {
      console.log(base + url);
      const res = await fetch(base + url);

      if (!res.ok) {
        break;
      }

      const $ = cheerio.load(await res.text());
      const chunk = [...$("")].map(e =>
        $(e).text().trim()
      );
      data.push(chunk);
      url = $("#__next > div > div:nth-child(2) > div > div > div.Layout_content__9ap_g > div:nth-child(3) > div > div.HotelCard_headerArea__hlQPk > div > div.HotelCard_mainInfo__pNKYU > div.HotelCard_wrapTitle__t742O > h2 > a").attr("TEXT");
    }
    catch (err) {
      console.error(err);
      break;
    }
  }

  console.log(JSON.stringify(data, null, 2));

  fs.writeFile('numbers.txt', data.join('\n'), function(err) {
    if (err) {
        console.log(err);
    }
});

})();

我本想看到一个数据列表,但我得到了[]。

javascript node.js web-scraping cheerio
2个回答
1
投票

base + url
始终使用
?page=1
。尝试将索引变量插入:
${base}?page=${i}

.attr("TEXT")
看起来不正确。我假设您希望每个页面上显示全部 20 个酒店名称,因此使用
[...$("...")].map(e => $(e).text())
将每个名称收集为单独的数组元素。

至于选择器,浏览器生成的超长选择器很容易出错。如果该链条中的任何假设发生变化,整个事情就会崩溃。使用

".HotelCard_title__cpfvk"
更安全,这就是识别您想要的元素所需的全部内容,仅此而已。

!res.ok
不足以确定分页何时结束。当结果列表为空时中断。

放在一起:

const cheerio = require("cheerio"); // ^1.0.0-rc.12
const {writeFile} = require("node:fs/promises");

const url = "<Your URL>";

(async () => {
  const data = [];

  for (let i = 1; i <= 1000; i++) {
    const res = await fetch(`${url}?page=${i}`);

    if (!res.ok) {
      break;
    }
    
    const $ = cheerio.load(await res.text());
    const names = [...$(".HotelCard_title__cpfvk")]
      .map(e => $(e).text());

    if (!names.length) {
      break;
    }

    data.push(...names);
  }

  console.log(data);
  await writeFile("numbers.txt", JSON.stringify(data));
})();

这需要一段时间才能运行,因此您可以并行化请求(冒着激怒服务器的风险),或者简单地添加一些日志以确保每个块都正常通过。

披露:我是链接博客文章的作者。


1
投票

您传递了一个空选择器:

$("")

...不会选择任何内容。

您应该指定要选择哪些元素。例如,如果您想要酒店名称,那么也许:

$(".HotelCard_title__cpfvk")

或酒店名称和价格的组合:

$(".HotelCard_title__cpfvk,.HotelCard_ratePriceValue__s3HvW")

请注意,该网站具有国际化功能,因此您可能需要传递参数才能使用您选择的语言。但这取决于第三方网站...

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