直接用 re.search(r'var data = (.*?);', html) 常失败,因页面js常压缩、含注释、换行或嵌套括号,非贪婪匹配遇首个分号即截断;可靠做法是先正则定位赋值起始(如 window.__initial_state__ = {),再手动扫描并配对 {} 层级提取完整json。

为什么直接用 re.search(r'var data = (.*?);', html) 常常失败?
因为实际页面里根本不存在 var data = ...; 这种干净写法。Script 标签里的 JS 往往是压缩过的、带换行/注释/嵌套括号的,甚至 JSON 被包裹在 IIFE 或对象属性里(比如 window.__INITIAL_STATE__ = {...})。盲目用非贪婪匹配 .*? 会提前截断,遇到第一个 ; 就停,而真正的 JSON 可能跨多行、含字符串里的分号或花括号。
真正可靠的做法是:先定位到目标变量赋值语句的起始位置,再用括号配对逻辑提取完整 JSON 字符串,而不是靠正则“猜”结束位置。
如何用正则定位 + 手动解析括号层级提取完整 JSON?
分两步走:第一步用正则粗筛出可能包含 JSON 的代码段(比如匹配 window.xxxs*=s*{ 或 vars+datas*=s*{),第二步从该 { 开始逐字符扫描,统计 {/} 深度,直到深度归零。
- 别依赖
re.DOTALL和.*?—— 它们无法处理嵌套结构 - 用
re.search(r'(window.__INITIAL_STATE__s*=s*){', html)找到开头,取.end()作为扫描起点 - 扫描时注意:字符串字面量里的
{和}不算层级变化(需跳过引号内内容) - 遇到
'、"、`要进字符串模式,碰到转义符后的引号要忽略
示例关键片段:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
start_match = re.search(r'window.__INITIAL_STATE__s*=s*{', html)
if start_match:
pos = start_match.end()
depth = 1
in_string = None # None, "'", '"', or '`'
while pos 0:
c = html[pos]
if in_string is None:
if c in "'"`":
in_string = c
elif c == '{':
depth += 1
elif c == '}':
depth -= 1
else:
if c == in_string and html[pos-1] != '\':
in_string = None
pos += 1
json_str = html[start_match.end():pos]
提取后 json.loads(json_str) 报 JSONDecodeError: Invalid escape 怎么办?
JS 中允许 u、
、 ,但不严格禁止其他反斜杠(比如 \ 或 x),而 Python 的 json 模块只认标准转义。常见于前端模板拼接或未转义的 HTML 实体中。
- 先尝试
json_str.replace('\', '\\')—— 但太暴力,可能把合法\u变成\\u - 更稳妥的是用
ast.literal_eval()替代json.loads(),它能解析 JS 风格的字符串(如单引号、无引号键),前提是 JSON 结构简单且不含函数/undefined - 或者用
json5.loads()(需pip install json5),它原生支持 JS 式语法、注释、单引号、尾随逗号
如果只是少量非法转义,可预处理:
import re
json_str = re.sub(r'\(?![bfnrtv"'\/]|u[0-9a-fA-F]{4})', r'\\', json_str)
data = json.loads(json_str)
有没有比手写括号解析更省事又不过度依赖第三方库的方法?
有,用 re.findall() 配合 JS 引擎式正则(不推荐)不如用现成的轻量方案:借助 html.parser 先提取所有 <script></script> 文本,再对每个 script 内容做针对性匹配。这样至少规避了 HTML 注释、CDATA 等干扰。
- 别用
BeautifulSoup解析 script 内容 —— 它会把当标签自动修正,破坏原始 JS - 用标准库
HTMLParser子类,在handle_starttag('script')后读取原始文本(需自己维护状态) - 更简单:用
re.findall(r'<script>]*>(.*?)</script>', html, re.DOTALL | re.IGNORECASE)提取 script 内容,再逐个处理 - 提取后加一句
script_text = re.sub(r'//.*?$|/*.*?*/', '', script_text, flags=re.MULTILINE|re.DOTALL)去掉注释,减少干扰
真正难的不是正则本身,而是你得确认那个 JSON 是静态注入的——如果是动态 fetch 或 WebSocket 推送的,正则永远抓不到。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










