如何从网页中安全提取并解析嵌入的 JavaScript JSON 数据

16次阅读

如何从网页中安全提取并解析嵌入的 JavaScript JSON 数据

本文介绍如何从 html 页面的 `

在现代 前端 渲染的网站中(尤其是使用 Vue、React 或服务端预渲染的站点),关键数据常以 JavaScript 对象形式直接注入 HTML 的

直接对原始字符串做 str.replace(“null”, “None”) 或 str.replace(“false”, “False”) 极其危险:一旦新闻标题中包含 “null”(如 “Version null release”)或 “false”(如 “is false positive”),替换将污染真实业务数据,导致解析失败或逻辑错误。因此,必须采用语义化、上下文感知的提取策略,而非文本层面的模糊替换

✅ 推荐方案:定位 + 截取 + 安全 JSON 化

核心思路分三步:

  1. 定位目标 :用 BeautifulSoup 精确查找含 App = 的脚本;
  2. 提取纯对象字面量内容:跳过 App = 前缀,截取至第一个完整闭合的 }(需处理嵌套);
  3. 转为标准 JSON 并加载:将 JS 风格字面量(true/false/null)安全映射为 JSON 兼容格式。

以下为健壮、可复用的实现:

立即学习Java 免费学习笔记(深入)”;

import requests from bs4 import BeautifulSoup import json import re  def extract_app_json_from_html(html_content: str) -> dict:     """从 HTML 字符串中提取 App 对象并解析为 Python 字典     自动处理嵌套花括号,避免误截断"""     soup = BeautifulSoup(html_content, 'html.parser')     # 查找包含 'App =' 的 script 标签(支持多空格 / 换行)script_tag = soup.find('script', string=re.compile(r'Apps*=s*{'))     if not script_tag:         raise ValueError("未找到包含'App = {'的 script 标签")      script_text = script_tag.string     # 定位 'App =' 后的第一个 '{'     start_idx = re.search(r'Apps*=s*{', script_text).end()      # 手动匹配最外层 {}(处理嵌套)brace_count = 0     end_idx = -1     for i, char in enumerate(script_text[start_idx:], start=start_idx):         if char == '{':             brace_count += 1         elif char == '}':             brace_count -= 1             if brace_count == 0:                 end_idx = i + 1                 break      if end_idx == -1:         raise ValueError("未能匹配到闭合的'}'")      # 提取纯对象字符串(JS 格式)js_obj_str = script_text[start_idx:end_idx].strip()      # 安全转换 JS 字面量为 JSON:仅替换顶层关键字(非字符串内)# 使用正则确保只替换独立单词(前后非字母 / 数字 / 下划线)json_compatible = (         js_obj_str         .replace('true','true')   # JSON 中 true 已兼容,无需改         .replace('false','false') # 同理         .replace('null','null')   # JSON 中 null 已兼容     )     # ⚠️ 注意:标准 JSON 与 JS 在布尔 / 空值上完全一致(true/false/null),无需替换!# 上述 replace 是冗余的;真正需处理的是单引号、末尾逗号等 —— 但 json.loads 不支持。# 因此更稳妥的做法是:用 ast.literal_eval(仅当内容可信时)或调用 JS 引擎(如 PyExecJS)。# 但实践中,该网站输出的 App 对象实际已是严格 JSON 格式(双引号、无尾逗号),# 故直接 json.loads 即可 —— 错误通常源于截取不完整(如多截了 `;` 或少截了 `}`)。try:         return json.loads(json_compatible)     except json.JSONDecodeError as e:         # 提供调试线索         print(f"JSON 解析失败,上下文(前 100 字符):{json_compatible[:100]!r}")         raise e  # 使用示例 url ="https://polymetalinternational.com/en/investors-and-media/news/press-releases/"response = requests.get(url, timeout=10) response.raise_for_status()  app_data = extract_app_json_from_html(response.text) press_releases = app_data["components"]["press-release"]["items"]  print(f" 共获取 {len(press_releases)} 条新闻:") for item in press_releases[:3]:  # 仅打印前 3 条示意     print(f"• {item['name']} ({item['date']}) → {item['link']}")

? 关键注意事项

  • 不要盲目替换 null/false:JSON 规范与 JavaScript 在这些字面量上完全一致,json.loads() 原生支持。报错主因是截取范围错误(如未正确匹配嵌套})或存在非法字符(如单引号、注释)。
  • 优先验证数据源格式:打开网页源码,搜索 App =,确认其内容是否已为合法 JSON(双引号包裹字符串、无尾逗号)。多数企业站会直接输出 JSON 字符串。
  • 处理 编码 与异常:务必设置 requests.get(…, timeout=10) 和 response.raise_for_status(),防止网络超时或 HTTP 错误静默失败。
  • 反爬提示:目标网站可能校验 User-Agent,建议添加请求头:
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"} response = requests.get(url, headers=headers)

此方法兼顾鲁棒性与简洁性,适用于绝大多数将结构化数据内联于 HTML 的场景,是 Web 数据采集中解析嵌入式 JS 对象的推荐实践。

text=ZqhQzanResources