最直接方式是用 find_all("p") 提取段落节点再调用 get_text(strip=true, separator="\n");遇非标准容器需用 select() 精准定位;务必判空并避免用 .text。

用 find_all("p") 提取所有段落节点,再调用 get_text()
这是最直接、最常用的方式。不要跳过节点定位这步——直接对整个 soup 调用 get_text() 会把标题、导航栏、脚注全混在一起,失去段落边界语义。
常见错误是拿到 find_all() 返回的 ResultSet 后,误以为它是字符串列表,直接 print(p) 或拼接。它其实是 Tag 对象列表,必须逐个调用文本提取方法。
-
strip=True必须加,否则段落首尾常带换行和空格,尤其从网页源码读取时 -
separator="\n"推荐加,让不同<p></p>之间有明确换行,避免粘连成一行 - 若页面含
<div class="paragraph"> 这类非标准段落容器,不能只靠 <code>"p",得配合select()或find_all(class_="paragraph")用
select()+ CSS 选择器精准定位特定段落当目标段落藏在复杂结构里(比如
<article><section><p></p></section></article>),或需排除广告、侧边栏等干扰块时,select()比find_all()更可靠。例如,某教育平台的正文段落统一加了
class="lesson-content__text",但页面还有十几个其他<p></p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill4293" title="Doc To HTML"><img src="https://img.php.cn/upload/skill/000/000/081/178998486916110.jpg" alt="Doc To HTML" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill4293" title="Doc To HTML" class="overflowclass">Doc To HTML</a> <p class="overflowclass">使用 MinerU 文档处理引擎将 Word 文档(.doc、.docx)转换为保留结构和格式的干净 HTML。</p> </div> <a rel="nofollow" href="/xiazai/skill4293" title="Doc To HTML" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>标签。硬写find_all("p")会拉进大量噪音。- 写法:
soup.select("article .lesson-content__text p"),注意空格表示后代关系 - 如果选中的
p里嵌套了<strong></strong>或<em></em>,get_text(strip=True)仍能干净提取,不用手动递归 - 避免用过于脆弱的选择器,比如
p:nth-of-type(3)—— 页面一改版就失效
处理空段落、换行混乱与嵌套标签干扰
get_text()默认保留所有内部空白,而真实 HTML 常含大量<br>、<div> 换行、多层 <code><span></span>套娃。不干预的话,输出可能是:这是一段文字。\n\n\n \n 再一段
这不是 bug,是默认行为。要得到可读结果,得主动控制:
-
strip=True清首尾空白,但中间的多个换行/空格不会合并 - 想压缩中间空白,得自己后处理:
re.sub(r"\s+", " ", text).strip() - 若段落内含
<img>或<script></script>,它们没文本内容,get_text()自动跳过,无需额外过滤 - 遇到
<p><br>文本</p>这种结构,get_text()仍返回“文本”,<br>被忽略 —— 这是预期行为,不是漏了
别用
.text属性替代.get_text()tag.text看似更短,但它没有参数、不支持清洗、无法设分隔符,且遇到嵌套标签时行为不稳定(比如<p>A<span>B</span>C</p>可能返回"ABC"或"A B C",取决于解析器)。生产环境一律用get_text()。另一个坑:有人对
None结果直接调用.get_text(),比如soup.find("p", id="missing").get_text(),会抛AttributeError。务必先判空:p_tag = soup.find("p", id="intro"); text = p_tag.get_text(strip=True) if p_tag else ""- 或者用
select_one(),它返回None而非空列表,判空更直观
真正难的不是提取动作本身,而是判断哪一层该停——是取整个
<main></main>下所有p,还是只取某个<div class="content"> 内的?这得看 HTML 实际结构,不能靠通用规则猜。</div> - 写法:










