Python BeautifulSoup JSON单页爬取正常,同域相似页面失败求助
解决NutritionData网站爬取JSON解析跨页面失败的问题
我帮你分析下问题所在,以及怎么修改脚本适配两个页面:
问题根源
你的脚本在第一个页面能运行,但第二个页面报错,核心问题出在JSON字符串的截取和格式化环节:
- 你依赖
fillSpanValues和分号位置来截断字符串,第二个页面里这个变量的出现位置或者脚本结构可能有细微差异,导致截取后的JSON内容不完整或者带了多余的无效字符。 - 你用来给键名加引号的正则
re.sub(r'([{,:])(\w+)([},:])', r'\1"\2"\3', jsonStr)不够精准,可能误处理了某些内容,或者漏掉了一些需要加引号的键,最终生成的JSON格式不合法,触发了解析错误。
优化后的脚本
下面是调整后的代码,能同时适配两个页面:
import requests from bs4 import BeautifulSoup import json import re # 可以替换成任意目标页面URL target_url = "https://nutritiondata.self.com/facts/vegetables-and-vegetable-products/2383/2" page = requests.get(target_url) soup = BeautifulSoup(page.content, 'html.parser') scripts = soup.find_all("script") for script in scripts: script_text = script.text if 'foodNutrients = ' in script_text: # 用正则精准提取foodNutrients对应的完整JSON对象 match = re.search(r'foodNutrients = (\{.*?\});', script_text, re.DOTALL) if match: raw_json_str = match.group(1) # 给没有引号的键名加上双引号,只匹配对象内的键名 valid_json = re.sub(r'(?<=[{,])\s*(\w+)\s*:', r'"\1":', raw_json_str) try: jsonObj = json.loads(valid_json) # 计算每份重量的逻辑保持不变 g_per_serv = int(jsonObj['FOODSERVING_WEIGHT_1'].split('(')[-1].split('g')[0]) for k, v in jsonObj.items(): if k == 'NUTRIENT_0': conv_v = (float(v)*g_per_serv)/100 print(f'{k} : {round(float(v))} (per 100 grams) | {round(float(conv_v))} (per serving {jsonObj["FOODSERVING_WEIGHT_1"]})') except json.JSONDecodeError as e: print(f"JSON解析错误位置: {e}") # 打印前1000字符方便排查问题 print("待解析的JSON片段:", valid_json[:1000])
关键改进点
- 精准提取JSON内容:用
re.search(r'foodNutrients = (\{.*?\});', script_text, re.DOTALL)直接匹配从{到}的完整对象,不再依赖fillSpanValues这类不稳定的标识,re.DOTALL确保能匹配多行的JSON结构。 - 安全的键名格式化:新的正则
(?<=[{,])\s*(\w+)\s*:只针对对象中{或,之后的键名添加引号,不会误处理字符串值里的内容,避免破坏JSON格式。 - 异常捕获与排查:加入try-except块,当解析失败时能打印错误位置和片段,方便你快速定位问题。
额外排查建议
如果后续遇到其他页面仍有问题,可以打印raw_json_str和valid_json的内容,对比差异,看看是否存在:
- 值里包含未转义的引号或特殊字符
- 键名包含下划线以外的特殊符号
- 脚本里有注释或多余的无效字符
针对性调整正则或添加额外的字符串清理逻辑即可。
内容的提问来源于stack exchange,提问作者Runner Bean
相关产品推荐
相关产品推荐

