直接用get_text()无法准确提取正文,因其仅递归提取所有文本节点、不判断语义权重,会混入导航栏、页脚、广告等噪声,正文占比常低于40%;需结合文本密度、DOM结构特征或专用库(如newspaper3k、trafilatura)实现精准提取。

直接用 BeautifulSoup.get_text() 无法准确提取正文,它会把导航栏、页脚、广告位全吞进去。真正能区分“正文”和“噪声”的,得靠基于文本密度或 DOM 结构特征的算法,不是简单删标签。
为什么 get_text() 不够用
它只是递归遍历所有文本节点,不判断语义权重。一个新闻页面里,<header> 里的网站名、<nav> 里的菜单项、<aside> 里的推荐链接,都会被原样保留。实测中,soup.get_text() 输出的纯文本里,正文占比常低于 40%。
- 不跳过
<script>和<style>内容(除非手动调用decompose()) - 对
<meta>、<title>等非可视内容无过滤逻辑 - 无法识别“连续多段
<p>”这种正文信号,也无视段落长度分布
newspaper3k 是最省心的 Python 正文提取方案
它内部做了三件事:先用 CSS 选择器预筛常见正文容器(如 article、.post-content),再计算每个区块的文本密度(字符数 / HTML 标签数),最后合并高密度相邻段落。对中文新闻、博客、论坛帖支持良好,且自带 URL 自动去重和编码自动探测。
- 安装:
pip install newspaper3k - 基础用法:
from newspaper import Article; a = Article(url); a.download(); a.parse(); print(a.text) - 本地 HTML 文件处理:
a.set_html(open("page.html").read()); a.parse() - 注意:首次运行会下载 NLP 模型,需联网;中文需额外
pip install jieba
需要更高精度或定制规则?用 trafilatura 替代
相比 newspaper3k,trafilatura 更轻量(纯 Python,无外部模型)、更可控(支持 XPath 白名单、自定义噪声标签剔除)、更适合批量任务。它默认启用“boilerplate removal”,即通过统计特征剥离模板区域。
立即学习“前端免费学习笔记(深入)”;
- 安装:
pip install trafilatura - 提取本地文件:
trafilatura.extract(open("page.html").read(), include_comments=False, no_fallback=True) - 关键参数:
include_tables=False可跳过表格(常含噪声),favor_precision=True倾向少提但准 - 陷阱:对 JavaScript 渲染后的内容无效,必须传入已渲染完成的 HTML 字符串
Java 项目里别硬套 Spire.Doc — 它本质是 Word 解析器
用 Spire.Doc 加载 HTML 是个 hack:它把 <h1> 当标题、<p> 当段落映射进自己的文档模型,再调用 Document.getText()。这在结构规整的静态页面上能凑合,但遇到 <div class="content"> 这类无语义容器时,会把整个 div 当作一个段落,中间的换行、列表、图片 alt 文本全糊成一团。
- 真实场景问题:
getText()返回值里常混入页眉页脚文字、重复的导航文案、甚至 base64 图片字符串(若 src 被解析为内联文本) - 替代方案:Java 生态更稳的是
jsoup+ 自定义选择器,例如doc.select("article > p, .post-content > p").text() - 如果必须用 Spire.Doc,请在 load 前先用正则粗筛:
html.replaceAll("<script[^>]*>[\s\S]*?</script>", "")
真正难的不是“怎么删标签”,而是“怎么定义什么是正文”。哪怕同一个页面,运营位和用户评论都可能是目标正文——这时候得靠业务规则前置,而不是依赖通用算法。工具只是执行者,判断权得交还给具体场景。



















