如何用正则从含React生成JSON props的HTML源码中提取指定值?
嘿,这个场景我日常爬取React站点时碰到过好多次,毕竟React经常会把初始配置、页面状态这类数据嵌在页面的JS代码块里,给你几个靠谱的解决思路:
1. 精准正则匹配(快速轻量,适合固定键场景)
如果你的目标键(比如siteName)是固定的,而且值的格式相对简单(没有太多转义字符的话),正则是最快的方式。
比如可以用这个正则来捕获值:
/"siteName":"((?:\\.|[^"\\])+)"/
这个正则能处理值里带转义双引号的情况(比如"siteName":"Example \"Test\" Site"),捕获组里的内容就是你要的Example "Test" Site。
举个Python的例子:
import re html_content = """你的HTML源码内容""" match = re.search(r'"siteName":"((?:\\.|[^"\\])+)"', html_content) if match: site_name = match.group(1).replace('\\"', '"') # 还原转义的双引号 print(site_name)
注意:如果JS里的结构经常变化,或者键名可能有变体,正则的容错性会比较差,这时候就需要用更稳妥的方法。
2. 提取JS代码块后解析为JSON对象
大部分React站点会把初始状态存在全局变量里(比如window.__INITIAL_STATE__、window.__REACT_INITIAL_DATA__这类命名),你可以先定位到对应的JS代码块,提取出JSON部分再解析。
步骤大概是:
- 用HTML解析器(比如Python的BeautifulSoup)找到包含目标数据的
<script>标签 - 用正则从脚本内容里提取出JSON字符串(比如匹配
window.__INITIAL_STATE__ = (.*?);) - 用JSON解析器把字符串转成对象,直接取
siteName属性
比如Python代码示例:
from bs4 import BeautifulSoup import json import re html_content = """你的HTML源码内容""" soup = BeautifulSoup(html_content, 'html.parser') script_tags = soup.find_all('script') for script in script_tags: if script.string and '__INITIAL_STATE__' in script.string: # 提取JSON部分 json_str = re.search(r'window.__INITIAL_STATE__ = (.*?);', script.string).group(1) data = json.loads(json_str) site_name = data.get('siteName') if site_name: print(site_name) break
这个方法比正则稳,因为JSON结构是规范的,只要能准确提取到目标JSON片段,就不会出错。
3. 无头浏览器执行JS直接取值(最可靠,适合复杂场景)
如果目标数据是通过JS动态计算生成的,或者藏在闭包、React组件内部,前面两种方法可能失效。这时候可以用无头浏览器(比如Playwright、Puppeteer)加载页面,直接在页面上下文里执行JS代码获取变量。
举个Playwright的Python示例:
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto("目标页面URL") # 直接在页面里执行JS,获取siteName site_name = page.evaluate('() => window.__INITIAL_STATE__.siteName') print(site_name) browser.close()
这个方法能模拟真实浏览器的渲染过程,不管数据是怎么生成的,只要页面能拿到,你就能拿到。缺点是比前两种方法慢,资源消耗也大,适合复杂场景。
额外小技巧
有些React衍生框架(比如Next.js)会把初始数据放在<script type="application/json" id="__NEXT_DATA__">这类标签里,这种情况直接提取标签内容解析成JSON就行,不用处理JS代码,非常方便。
内容的提问来源于stack exchange,提问作者KriiV

