JS新手求助:抓取无结构化食谱网站食材时代码返回异常
解决无结构化食谱网站的食材抓取问题
嘿,我完全懂你现在的头疼——没有清晰类名/ID的无结构化食谱网站,对JS爬虫新手来说简直是绕不开的坎!别慌,咱们一步步拆解问题,把食材数据给揪出来。
首先,先搞清楚你代码返回true/false/undefined的核心原因:你可能用了不匹配的DOM选择器,或者没找准食材所在的文本/列表块,甚至遇到了动态加载的内容。下面是具体的解决步骤:
第一步:手动定位食材在DOM中的规律
先打开浏览器开发者工具(按F12),找到食谱页面里的食材区域,仔细观察:
- 食材是不是在
<ul>/<ol>列表里?哪怕列表本身没有类名,它的父元素或者前面的标题(比如“食材”“Ingredients”)有没有独特的文本特征? - 有没有可能食材是混在段落文本里,用换行、分号或者符号分隔的?
比如很多食谱网站会在食材列表前放一段类似“食材:”的文本,这就是我们可以利用的锚点。
第二步:基于文本特征的DOM查询(核心方案)
因为没有特殊类名,我们可以用XPath匹配包含特定文本的元素,再定位到它后面的食材列表。举个具体的代码例子:
// 1. 用XPath找到包含"食材"(或"Ingredients")的标题元素 const ingredientHeader = document.evaluate( '//*[contains(text(), "食材")]', // 替换成你看到的标题文本,比如英文站用"Ingredients" document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; // 2. 定位标题后面的食材列表(假设是ul/ol,根据实际DOM调整) const ingredientList = ingredientHeader?.nextElementSibling; // 3. 提取列表里的食材文本 if (ingredientList) { const ingredients = Array.from(ingredientList.querySelectorAll('li')) .map(item => item.textContent.trim()); console.log('抓到的食材:', ingredients); } else { console.log('没找到食材列表,请检查DOM结构'); }
如果食材不是在列表里,而是混在段落中,比如用换行分隔,那可以这样提取:
// 找到包含食材的段落元素(可以结合XPath定位) const ingredientPara = document.evaluate( '//p[contains(text(), "食材")]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; if (ingredientPara) { // 按换行分割文本,过滤空行后整理 const ingredients = ingredientPara.textContent.split('\n') .filter(line => line.trim() !== '') .map(line => line.trim().replace(/^\d+\.|\*|\-/, '')); // 去掉开头的序号/符号 console.log('抓到的食材:', ingredients); }
第三步:处理动态加载的内容
如果你的代码直接查DOM返回undefined,很可能食材是页面加载后通过JS动态渲染的。这时候我们可以直接抓取页面的原始HTML,再用DOMParser解析:
async function fetchRecipeIngredients() { try { // 发送请求获取页面HTML(记得替换成目标食谱的URL) const response = await fetch('https://justanotherrecipeblog.tumblr.com/your-recipe-post-url', { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' } }); const html = await response.text(); // 解析HTML为DOM文档 const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); // 重复之前的XPath查询逻辑 const ingredientHeader = doc.evaluate( '//*[contains(text(), "Ingredients")]', doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; if (ingredientHeader) { const list = ingredientHeader.nextElementSibling; const ingredients = Array.from(list.querySelectorAll('li')).map(li => li.textContent.trim()); console.log('抓到的食材:', ingredients); } } catch (error) { console.error('请求出错:', error); } } // 调用函数 fetchRecipeIngredients();
几个重要的提醒
- 先检查目标网站的
robots.txt(比如https://justanotherrecipeblog.tumblr.com/robots.txt),确保允许抓取相关内容,避免合规问题。 - 如果遇到403/请求被拦截,记得在
fetch里设置合理的User-Agent头,模拟普通浏览器请求。 - 不同食谱的DOM结构可能略有差异,需要根据实际页面调整XPath或选择器逻辑——多试几次开发者工具里的元素查询,确认选择器能准确命中目标。
内容的提问来源于stack exchange,提问作者Samuel
相关产品推荐
相关产品推荐

