优先用 json.loads() 解析完整 js 变量赋值(如 var data = {...}),而非正则硬匹配;若必须用正则,应先用 beautifulsoup 定位 script 标签缩小范围,再结合 re.dotall 提取并用 json.loads() 校验。

re.findall 提取 JSON 字符串时匹配不到内容
常见现象是 re.findall(r'\{.*?\}', text) 返回空列表,或只拿到半个 JSON——因为正则默认不跨行、不处理嵌套、遇到换行或注释就断掉。
根本原因:JSON 的 {} 是嵌套结构,而 .*? 无法正确平衡括号层级;且原始 HTML 或 JS 中的 JSON 往往混着换行、缩进、单双引号混用,甚至有 JS 注释(// 或 /* */),正则一碰就失效。
- 优先改用
json.loads()解析完整 JS 变量赋值(如var data = { ... };),而不是硬啃 JSON 片段 - 若必须用正则,把模式升级为
r'(\{(?:[^{}]|(?R))*\})'——但 Pythonre不支持(?R)递归,得换regex模块(非标准库) - 更稳妥的做法:先用
re.search(r'var\s+\w+\s*=\s*(\{.*?\});', text, re.DOTALL)拿到赋值右侧,再用json.loads()解析,re.DOTALL确保.匹配换行
re.search 和 re.findall 在提取 JSON 场景下的行为差异
re.search 找第一个匹配就停,re.findall 贪心扫全文本——但两者在 JSON 提取中都容易“切错位置”,尤其当页面含多个 {} 块(比如多个 script 标签或内联配置)。
典型坑:用 re.findall(r'\{.*?\}', text) 会把 { "a": 1 } 和后面紧挨着的 { "b": 2 } 中间的 } 和 { 错配成 }`{,导致截断。
立即学习“Python免费学习笔记(深入)”;
-
re.search更适合提取单个主数据块(如页面唯一的window.__INITIAL_STATE__ = {...}) -
re.findall仅适用于明确分隔、无嵌套的简单键值对(如{"id":"123"}连续出现且彼此独立),且需加re.DOTALL - 永远检查返回结果是否为合法 JSON:用
json.loads(result[0])尝试解析,捕获json.JSONDecodeError——别信正则“看起来像”
从 script 标签里提取 JSON 最少出错的写法
真实爬虫里,JSON 几乎都藏在 <script></script> 标签中,直接对整个 HTML 用正则扫效率低、误伤多。应先定位 script,再针对性提取。
关键不是“怎么写正则”,而是“怎么缩小搜索范围”:用 BeautifulSoup 定位 <script></script>,再对 .string 内容做正则,比全文扫可靠十倍。
- 选 script:用
soup.find('script', string=re.compile(r'window\.__DATA__')),比re.search(r'<script>.*?window\.__DATA__.*?</script>', html)更准 - 提取后先清理:去掉开头的
var data =、结尾的分号、JS 注释(用re.sub(r'//.*|/\*.*?\*/', '', js_text)) - 避免用
re.match:它只从开头匹配,而 script 里 JSON 往往在中间;一律用re.search或re.findall
为什么不用 json.loads 直接解析反而要写正则
不是不想用,是经常不能用:目标字段可能在 JS 变量赋值里(const config = {...}),或被包裹在函数调用中(init({ ... })),或和 HTML 混在一起没单独标签——这时候 json.loads 会直接报 Expecting property name enclosed in double quotes。
真正该警惕的是“以为正则能替代解析器”:正则只能帮你把疑似 JSON 的字符串抠出来,绝不能代替 json.loads 做校验和结构化。漏掉这步,后续字段访问(如 data['user']['id'])必崩。
- 抠出来之后必须
json.loads(),哪怕只是try: json.loads(s) except: continue - 双引号问题:JS 允许单引号或无引号 key,但 JSON 不允许;正则抠出来的字符串大概率不合法,得先用
ast.literal_eval()(仅限简单情况)或预处理修复 - 编码问题:script 标签内容可能是 UTF-8 但没声明,
.string取出来是乱码——确保 BeautifulSoup 解析时指定了from_encoding='utf-8'
事情说清了就结束。正则只是撬棍,JSON 解析才是锤子;撬不开的时候,别硬砸,先看看有没有更结实的支点。










