如何从嵌套JSON-LD结构中查找@type为Recipe的食谱数据
最优实现方案(递归遍历+提前终止)
核心逻辑是设计一个通用递归遍历函数,支持同时处理任意嵌套层级的列表、字典结构,遍历过程中主动匹配"@type": "Recipe"的字典节点,匹配到就直接存入结果集合,不需要继续向下遍历该节点的子元素,减少无效计算。
实现步骤
- 第一步:先提取页面中所有
<script type="application/ld+json"标签的文本内容,用JSON解析工具转成原生的字典/列表对象,注意要处理部分站点存在的JSON转义异常、尾随逗号问题,可以用json5库替代标准JSON库提高兼容性。 - 第二步:编写递归遍历函数,逻辑如下:
- 如果当前遍历对象是字典:首先检查是否存在键
"@type"且值为"Recipe",如果符合直接返回该字典作为结果;如果不符合,就遍历所有字典值,逐个传入递归函数,收集返回的有效结果。 - 如果当前遍历对象是列表:遍历列表中每个元素,逐个传入递归函数,收集返回的有效结果。
- 如果当前是字符串、数字等基础类型,直接返回空结果。
- 如果当前遍历对象是字典:首先检查是否存在键
- 第三步:收集所有匹配到的Recipe字典,按需提取
recipeIngredient(食材清单)、recipeInstructions(制作步骤)字段即可。
代码示例(Python版本)
import json5 from lxml import etree def find_recipe_nodes(obj): recipe_list = [] if isinstance(obj, dict): # 先判断当前字典是不是目标Recipe节点 if obj.get("@type") == "Recipe": recipe_list.append(obj) # 不需要继续遍历子节点,直接返回 return recipe_list # 兼容@type为数组的场景,可替换上面的判断逻辑 # if "Recipe" in obj.get("@type", []): # recipe_list.append(obj) # return recipe_list # 不是目标节点的话遍历所有值递归 for v in obj.values(): recipe_list.extend(find_recipe_nodes(v)) elif isinstance(obj, list): # 遍历列表所有元素递归 for item in obj: recipe_list.extend(find_recipe_nodes(item)) # 基础类型直接返回空 return recipe_list # 调用示例 html = etree.HTML(你的网页源码字符串) ld_json_text = html.xpath('//script[@type="application/ld+json"]/text()')[0] # 用json5解析兼容不规范的JSON格式 ld_data = json5.loads(ld_json_text) # 查找所有Recipe节点 all_recipes = find_recipe_nodes(ld_data) # 提取需要的字段做后续处理 for recipe in all_recipes: ingredients = recipe.get("recipeIngredient", []) steps = recipe.get("recipeInstructions", [])
可选优化项
- 可以给递归函数加深度限制,避免极端情况下的栈溢出,一般ld+json嵌套深度不会超过10层,设置20层的上限完全够用。
- 如果确定页面只有1个食谱,可以在匹配到第一个Recipe节点后直接终止整个遍历,进一步提升效率。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

