应先用 BeautifulSoup 定位主内容容器(如 main/article 或含 content/post 类的 div),再清除 script/style/nav 等干扰标签,最后用 .children + NavigableString 精准提取裸露文本,并调用 get_text(separator="\n", strip=True)。

用 BeautifulSoup 定位标签再调 get_text()
直接对整个 HTML 调 soup.get_text() 会混入导航、页脚、广告等干扰内容。真正要做的,是先缩小范围——比如只取 <main></main>、<article></article> 或带 content/post 类的容器,再在其内部提取文本。
常见写法:soup.find("main") or soup.find("article") or soup.find("div", class_=re.compile(r"content|post|entry"))。如果结构固定(如教育平台的 div.dnevnik-lesson__task),直接用 soup.select("div.dnevnik-lesson__task") 更稳。
提取时务必加参数:.get_text(separator="\n", strip=True)。其中 separator="\n" 让段落间有换行,strip=True 去首尾空格,比默认行为干净得多。
提取前必须 decompose() 干扰标签
即使选对了父容器,里面仍可能藏着 <script></script>、<style></style>、<nav></nav>、<footer></footer> 这类非文本节点,它们的 get_text() 结果会污染输出。
立即学习“前端免费学习笔记(深入)”;
正确做法是提取前批量清除:
for tag in soup(["script", "style", "nav", "footer", "header", "aside"]): tag.decompose()- 不要用
extract(),它返回被移除节点,内存开销大;decompose()彻底销毁节点对象,更轻量 -
<i></i>、<svg></svg>等无文本图标标签不影响get_text(),无需额外处理
想只取某标签“直接子文本”,别用 .text
比如这个结构:<div class="A">
<h1>不要这段</h1>只要这段。</div>,目标是“只要这段。”,而非 h1 里的内容。
soup.find("div", class_="A").text 会把所有子节点文本拼成一串,结果是“不要这段只要这段。”,完全错误。
正确方式是遍历 .children 并过滤出 bs4.element.NavigableString 类型:
texts = [child.strip() for child in soup.find("div", class_="A").children if isinstance(child, bs4.element.NavigableString) and child.strip()]- 这样能跳过
h1、ul等子元素,只拿到父标签下裸露的文本节点 - 注意:
.text是快捷但粗糙的接口,.children+ 类型判断才是精准控制的关键
正则提取仅限简单、可控场景
正则处理 HTML 是高风险操作。它无法可靠应对嵌套(<div><p>x</p></div>)、注释(<!-- -->)、CDATA 或属性中含 > 的情况(如 <tag attr="a>b"></tag>)。
仅在以下情况可考虑正则:
- 输入格式严格统一(如自动生成的报表 HTML)
- 只需提取单层标签内纯文本,且确认无嵌套、无属性、无换行(否则需加
re.DOTALL) - 模式必须非贪婪:
r"<p>(.*?)</p>",避免跨标签吞掉中间内容 - 若要泛化标签名,用反向引用:
r"(.*?)\1>",但依然不防属性干扰
绝大多数真实网页(微信公众号、CMS 输出、动态渲染页)都该用解析器,不是正则。容易忽略的一点是:很多“正文”根本没包在 p 里,而是散落在 div、section 中——这时候靠正则写死 p 就直接漏掉了。



















