Cheerio爬取非结构化网页周一至周二菜单数据遇阻求助
解决思路
1. 别迷信Chrome生成的选择器,适配Cheerio的原始HTML解析
Chrome生成的选择器是基于浏览器渲染后的DOM,但Cheerio直接解析的是原始HTML,两者经常有差异:
- 浏览器会自动补全未闭合标签、调整节点顺序,原始HTML里可能完全不是这回事
:nth-child/:nth-of-type这类选择器依赖的是原始HTML里的节点位置,不是渲染后的
实操步骤:
- 把你爬取到的原始HTML(Cheerio加载的那个字符串)粘贴到浏览器的HTML编辑器里,用Chrome生成的周一/周二选择器直接查询,看能不能命中元素。如果查不到,说明得基于原始HTML重新写选择器
- 换更灵活的定位方式,比如通过日期文本找父容器:
// 定位包含"Mandag"的节点,再取对应的菜单内容 const mondayMenu = $('*:contains("Mandag")').siblings('.menu-items').text().trim(); // 或者找父级容器再遍历子节点 const tuesdayContainer = $('div:has(.day-title:contains("Tirsdag"))'); const tuesdayMenuItems = tuesdayContainer.find('.menu-item').map((i, el) => $(el).text().trim()).get();
2. 对比周一/周二和周三/周五的结构差异
既然周三到周五能爬成,把这几天的HTML结构拉出来对比:
- 看容器的类名、ID是不是不一样(比如周三用
day active,周一用day hidden) - 菜单内容的嵌套层级有没有区别(比如周一的菜单在
<p>里,周三在<span>里) - 有没有隐藏节点(比如周一的菜单被
display: none的div包着,但Cheerio照样能解析,只是选择器要对应)
实操步骤:
- 在代码里打印两类容器的HTML,直接看差异:
发现差异后调整选择器即可。console.log('周一容器:', $('你的周一选择器').html()); console.log('周三容器:', $('你的周三选择器').html());
3. 处理动态内容的情况
如果周一/周二的菜单是JS动态加载的,你爬的原始HTML里根本没有这些内容,那Cheerio肯定拿不到。这时候得先让页面渲染完成再取HTML。
实操步骤:
- 用Puppeteer配合Cheerio,先获取渲染后的页面内容:
import puppeteer from 'puppeteer'; import cheerio from 'cheerio'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('目标网站地址', { waitUntil: 'networkidle2' }); // 等页面加载完成 const renderedHtml = await page.content(); const $ = cheerio.load(renderedHtml); // 现在再用选择器爬周一、周二菜单,应该就能拿到了 const mondayMenu = $('你的周一选择器').text().trim(); await browser.close();
4. 用遍历调试定位问题
如果还是找不到原因,直接遍历所有日期容器,打印每个容器的日期名和菜单内容,看哪一步出问题:
$('.day-wrapper').each((index, el) => { const dayName = $(el).find('.day-name').text().trim(); const menuContent = $(el).find('.menu-content').text().trim(); console.log(`${dayName}: ${menuContent || '无内容'}`); });
如果某个日期显示“无内容”,单独打印这个容器的HTML,分析为什么选择器匹配不到。
内容的提问来源于stack exchange,提问作者Tue M
相关产品推荐
相关产品推荐

