我决定为了兴趣的缘故,从网站收集数据(名称,每晚价格,评级)为自己,遇到了误解.我什么都没有得到输出.我写了其他图书馆,但他们说这个更好.

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);
    }
});

})();

我本以为会看到一个数据列表,但我得到了[].

推荐答案

base + url总是使用"?page=1".try 在:`${base}?page=${i}`中插入索引变量.

.attr("TEXT")看起来不对我假设您希望在每个页面上所有20个wine 店名称,所以使用[...$("...")].map(e => $(e).text())将每个名称收集为单独的数组元素.

至于 Select 器,long, browser-generated ultra-rigid selectors are prone to error.如果这个链条中的任何假设改变,整个事情就会破裂.使用".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 chunk = [...$(".HotelCard_title__cpfvk")]
      .map(e => $(e).text());

    if (!chunk.length) {
      break;
    }

    data.push(...chunk);
  }

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

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

要获取所需的其他字段,可以按如下方式修改脚本:

const chunk = [...$('[data-testid="serp-hotelcard"]')]
  .map(e => ({
    name: $(e).find('[class*="HotelCard_title"]').text(),
    price: $(e).find('[class*="HotelCard_ratePriceValue"]').text(),
    rating: $(e).find('[class*="TripAdvisor_tripAdvisor_value"]')
      .first()
      .attr("class")
      ?.split(/\s+/)
      .find(e => e.includes("TripAdvisor_tripAdvisor_value"))
      .match(/_value_(\d+)_/)[1]
      .split("")
      .join("."),
  }));

注意,我已经放松了一些 Select 器来使用子字符串,避免了生成的子字符串"cpfvk"变成".HotelCard_title__cpfvk"的情况.

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

Javascript相关问答推荐

获取加载失败:获取[.]添加时try 将文档添加到Firerestore,Nuxt 3

如何修复内容安全策略指令脚本-SRC自身错误?

当promise 在拒绝处理程序被锁定之前被拒绝时,为什么我们会得到未捕获的错误?

我无法在NightWatch.js测试中获取完整的Chrome浏览器控制台日志(log)

Cookie中未保存会话数据

将字符串UTC Date转换为ngx—counting leftTime配置值的数字

在nextjs服务器端api调用中传递认证凭证

函数返回与输入对象具有相同键的对象

切换时排序对象数组,切换不起作用

在带有背景图像和圆形的div中添加长方体阴影时的重影线

获取Uint8ClampedArray中像素数组的宽度/高度

无法使用单击按钮时的useState将数据从一个页面传递到另一个页面

在WordPress中使用带有WPCode的Java代码片段时出现意外令牌错误

使用领域Web SDK的Vite+Vue应用程序中的Web程序集(WASM)错误

在forEach循环中获取目标而不是父对象的属性

TypeError:无法读取未定义的属性(正在读取';宽度';)

如何在Press上重新启动EXPO-AV视频?

重新渲染过多(&Q).REACT限制渲染次数以防止无限循环.使用REACT下拉菜单时

Rails 7:在不使用导入映射的情况下导入Java脚本

如何在Java脚本中添加一个可以在另一个面板中垂直调整大小的面板?