requests拿不到正文是因为现代网站内容由javascript动态插入,html源码仅为“空壳”;需通过正则提取window.__initial_state__中的json或用playwright获取渲染后html再解析。

requests 直接解析为什么拿不到正文
因为很多现代网站(比如 Habr、知乎、掘金)的正文不是写在 HTML 源码里的 <p></p> 标签中,而是由 JavaScript 在浏览器里动态插入的。你用 requests.get(url).text 拿到的只是“空壳 HTML”,里面可能只有 <div id="app"></div> 或者一个 <script></script> 标签,真正的文章内容藏在 JSON 里。这时候用 BeautifulSoup 找 <p></p> 或 class="article-body",大概率返回空列表或一堆占位符。
从 window.__INITIAL_STATE__ 提取 Habr 类网站正文
像 Habr 这类使用 SSR + 前端状态管理的站点,会把整篇文章数据序列化后塞进 window.__INITIAL_STATE__ 变量。关键不是解析 DOM,而是从 HTML 字符串里正则捞出那段 JSON:
- 必须用
response.text(不是.content),否则正则匹配会失败 - 正则推荐:
r"window\.__INITIAL_STATE__=({.*?});",加re.DOTALL处理换行 - 提取后
json.loads(),再按实际结构导航,比如initial_state["articlesList"]["articlesList"][article_id]["content"] - 注意:这个路径可能随前端更新变动,建议先打印
initial_state.keys()看顶层结构
Playwright 获取真实渲染后的 HTML 再解析
如果你不确定目标站是否用了类似机制,或者要处理点击、滚动、登录态等复杂交互,Playwright 是更通用的选择。它能真正跑起 Chromium,等 JS 执行完再取 page.content():
- 安装后初始化要加
headless=False调试,确认页面确实加载出了正文 - 别直接
page.content()—— 得等关键元素出现,比如:page.wait_for_selector("article .article-formatted-body", timeout=10000) - 拿到 HTML 后,仍建议用
BeautifulSoup解析,而不是靠page.inner_text(),因为后者会合并所有嵌套文本,丢失段落结构 - 性能开销明显大于 requests,只在必要时启用
BeautifulSoup 提取时 get_text() 的坑
soup.get_text() 看似省事,但容易把标题、侧边栏、广告、脚注全混在一起。尤其当目标区域存在多层嵌套或 <span></span> 包裹时,结果不可控:
- 优先用
find()或select()定位到最外层容器(如article或div.post-content),再对其调用.get_text() - 传参控制格式:
strip=True去首尾空白,separator=" "避免段落粘连 - 如果发现文本重复(比如父 div 和子 p 都有文字),说明没精准定位,得检查 DOM 层级,用浏览器的 “Copy outerHTML” 对照看
- 对富文本含
<strong></strong>、<em></em>的场景,get_text()会丢掉语义,此时应保留标签做轻量清洗
真正麻烦的从来不是“怎么取”,而是“取哪一段”。动态渲染让 HTML 结构和内容分离,必须先判断数据来源是内嵌 JSON、AJAX 接口,还是纯客户端渲染——选错路径,后面所有解析都是无用功。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











