使用Fetch与Cheerio爬取ostrovok.ru网站返回空结果问题
问题分析与修复方案
你的代码输出空数组主要有以下几个关键问题:
核心问题点
- 空选择器导致无数据提取:
$("")是无效选择器,自然无法获取任何元素,chunk始终为空数组。 - 分页逻辑完全错误:你用来获取下一页URL的选择器指向的是酒店标题的
<a>标签,而且attr("TEXT")是错误用法(应该用text()或attr("href")),导致url很快变为undefined,后续请求失效。 - 缺少请求头被反爬拦截:直接用
fetch请求可能被网站识别为爬虫,返回空内容或错误状态码。 - 未实现目标数据提取逻辑:代码里没有编写提取酒店名称、价格、评分的逻辑。
修复后的代码
const cheerio = require("cheerio"); let fs = require('fs').promises; // 使用promise版本适配async/await const base = "https://ostrovok.ru/hotel/russia/adler/"; // 模拟浏览器请求头,避免被反爬拦截 const fetchOptions = { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" } }; // 延迟函数,控制请求频率 const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); (async () => { let currentPage = 1; const data = []; const maxPages = 5; // 先测试少量页面,避免请求过多被封 while (currentPage <= maxPages) { try { const url = `${base}?page=${currentPage}`; console.log(`正在爬取第${currentPage}页: ${url}`); const res = await fetch(url, fetchOptions); if (!res.ok) { console.log(`请求失败,状态码: ${res.status}`); break; } const html = await res.text(); const $ = cheerio.load(html); // 遍历所有酒店卡片,提取目标数据 $(".HotelCard_root__r0TF9").each((_, el) => { const $card = $(el); const hotelName = $card.find(".HotelCard_name__2Wid6").text().trim(); const price = $card.find(".PriceInfo_price__3b9i2").text().trim(); const rating = $card.find(".Rating_value__1S13c").text().trim(); if (hotelName) { // 确保有有效数据才加入数组 data.push({ hotelName, price, rating }); } }); // 检查是否存在下一页 const hasNextPage = !!$(".Pagination_next__3Z8fQ").length; if (!hasNextPage) break; currentPage++; await delay(1000); // 每页间隔1秒,降低被封风险 } catch (err) { console.error(`爬取第${currentPage}页出错:`, err); break; } } console.log("爬取结果:", JSON.stringify(data, null, 2)); await fs.writeFile('hotels.json', JSON.stringify(data, null, 2)); console.log("数据已保存到hotels.json"); })();
关键修复说明
- 添加请求头:通过
User-Agent模拟浏览器请求,避免被网站直接拦截。 - 正确的选择器:使用
.HotelCard_root__r0TF9定位酒店卡片,再从卡片内提取名称、价格、评分的具体元素(若网站DOM更新,需重新调整选择器)。 - 合理分页逻辑:通过页码递增请求,同时检查下一页按钮状态,避免无效循环。
- 请求延迟:添加
delay函数控制请求频率,降低被反爬机制封禁的概率。 - 使用Promise版fs:避免回调嵌套,适配async/await语法。
注意事项
- 网站DOM结构可能随时更新,若后续爬取失效,需重新检查页面元素的类名或选择器。
- 不要设置过高的
maxPages或过短的请求间隔,避免对目标网站造成服务器压力,同时防止IP被封禁。
内容的提问来源于stack exchange,提问作者N9ter
相关产品推荐
相关产品推荐

