应优先定位主内容区(如article、main或含post/entry/content的元素),未匹配到再剔除广告区域(如ad、banner、aside等),最后提取文本;避免用正则粗筛,因其无法处理无标签包裹的广告文字。

用 BeautifulSoup 提取正文时怎么跳过广告容器
直接调 soup.get_text() 会把广告文字也混进去,关键不是“删标签”,而是“别选到广告区域”。常见做法是先定位主内容区,再提取——比如找 article、main 或带 class="content" 的元素;如果结构混乱,就得反向排除。
推荐组合策略:
- 优先用
soup.find("article") or soup.find("main") or soup.find(class_=re.compile(r"(post|entry|content)", re.I))
- 没匹配到就 fallback 到 body 下的高密度文本节点(类似 goose 思路),但手动写太重,不建议初筛用
- 明确要排除的广告区域:用
soup.select('div[id*="ad"], div[class*="banner"], aside, footer, .taboola-container, [data-ad]') 批量 .decompose()
- 注意
.decompose() 和 .extract() 区别:前者彻底删除并断开 DOM 引用,后者只移出但保留对象——处理完必须用 .decompose(),否则 get_text() 还可能读到残留文本节点
正则粗筛 HTML 时为什么总漏掉 or 多删广告文字
用 re.sub(r']+>', '', html) 这类简单正则去广告,本质是“无脑删标签”,广告文字本身没标签包裹(比如纯文本广告语、插入在段落中间的推广语)就会原样留下;而某些合法内容含 符号(如数学表达式 x )又会被误切。
真正能靠正则控制广告剔除的,只有两类场景:
- 广告代码有固定特征串,比如
或 ,这时用
re.sub(r'|]*id=["\']ad-[^"\']*["\'][^>]*>.*?
', '', html, flags=re.DOTALL | re.I)- 广告集中在页面底部,且有明显分隔符(如连续 3 个
),可用 html.split('
')[0] 截断
- 其他情况别硬上正则——它不理解 HTML 结构,
<script></script> 里嵌 JS、 里的原始 HTML 都会崩
浏览器控制台临时去广告后,document.body.innerText 还带广告怎么办
因为 innerText 是渲染后文本,广告 DOM 被 remove() 或 display: none 后确实不显示,但有些广告是通过 document.write() 或异步脚本动态注入的,执行 innerText 时它还没加载完。
安全做法是等广告脚本执行完毕再取文本:
- 先手动删掉已知广告容器:
document.querySelectorAll('div[id*="ad"], .taboola, [data-ad]').forEach(el => el.remove())
- 再等 500ms 让异步广告回调跑完:
setTimeout(() => console.log(document.body.innerText), 500)
- 更稳的是监听 DOM 变化:
new MutationObserver(() => console.log(document.body.innerText)).observe(document.body, { childList: true, subtree: true }),但仅限调试,别写进自动化脚本
用 Puppeteer / Playwright 批量处理时,广告清理时机很关键
很多人在 page.goto() 后立刻 page.content(),结果广告还在。根本原因是页面资源(尤其是广告 JS)没加载完,DOM 还没被污染。
正确顺序必须是:
- 用
waitUntil: "networkidle0" 确保网络请求基本结束
- 加一个短延时(如
await page.waitForTimeout(800)),给广告 SDK 留执行窗口
- 再执行清理脚本:
await page.evaluate(() => { /* 移除广告节点 */ })
- 最后才取文本:
await page.evaluate(() => document.body.innerText)
- 注意:不要在
evaluate 里用 document.querySelector 找刚被 remove() 的元素——它已经不存在了,查不到不报错,但后续逻辑可能失效
最易忽略的一点:广告 JS 有时会监听 DOMNodeInserted 并自动重插广告节点,所以单次 remove() 不够,得用 MutationObserver 持续拦截,但这会显著拖慢批量处理速度——权衡之下,800ms 延时 + 一次清理,覆盖 90% 场景已足够。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!