使用 Beautiful Soup 提取 HTML 数据
Beautiful Soup 适合从 HTTP 响应中已经存在的 HTML 提取数据。它不会执行页面里的 JavaScript:如果浏览器中的内容由脚本在页面加载后再请求并渲染,requests 得到的初始 HTML 可能没有那部分数据。此时应先在浏览器开发者工具中确认数据来源,并在符合站点规则的前提下选择合适方案。
1. 安装与解析
from bs4 import BeautifulSoup
html = """
<article class="post">
<h2><a href="/posts/1">第一篇文章</a></h2>
<p class="summary">一段简介。</p>
</article>
"""
soup = BeautifulSoup(html, "html.parser")
html.parser 是 Python 自带的解析器,足以用于基础学习。页面结构复杂或性能要求更高时,可以在明确依赖后再评估其他解析器。
2. 用 CSS 选择器定位元素
Beautiful Soup 提供 select() 和 select_one():
select(selector)返回所有匹配元素的列表。select_one(selector)只返回第一个匹配元素;没有匹配时返回None。
article = soup.select_one("article.post")
title_link = article.select_one("h2 > a")
title = title_link.get_text(strip=True)
href = title_link.get("href")
summary = article.select_one(".summary").get_text(strip=True)
print(title, href, summary)
常用选择器:
| 选择器 | 含义 |
|---|---|
.summary |
class 包含 summary 的元素 |
#main |
id="main" 的元素 |
article a |
article 内任意层级的链接 |
article > h2 |
article 的直接子元素 h2 |
a[href] |
带有 href 属性的链接 |
优先使用反映页面语义的稳定选择器,例如 article.post h2 > a。不要依赖“第 3 个 div”这类位置选择器;页面插入广告或调整布局时,这类规则很容易失效。
3. 处理缺失字段与相对链接
真实页面不保证每条记录都有摘要或链接。提取时先判断元素是否存在;链接也要把相对地址转换为绝对地址。
from urllib.parse import urljoin
def parse_article(card, page_url: str) -> dict[str, str] | None:
link = card.select_one("h2 > a")
if link is None:
return None
title = link.get_text(" ", strip=True)
href = link.get("href")
if not title or not href:
return None
summary_node = card.select_one(".summary")
summary = summary_node.get_text(" ", strip=True) if summary_node else ""
return {
"title": title,
"url": urljoin(page_url, href),
"summary": summary,
}
get_text(" ", strip=True) 会用空格连接内部文本并清理首尾空白,通常比直接读取 .text 更适合提取标题和摘要。urljoin() 会根据当前页面 URL 正确处理 /path、../path 等相对链接。
4. 解析前后的检查
- 保存一小段原始 HTML 样本,便于选择器失效时定位问题。
- 统计匹配到的卡片数量;突然变为
0时不要继续写入空数据。 - 对每条数据校验必填字段,例如标题、来源 URL。
- 保留来源页面 URL 与抓取时间,方便以后复核。