跳转至

使用 Beautiful Soup 提取 HTML 数据

Beautiful Soup 适合从 HTTP 响应中已经存在的 HTML 提取数据。它不会执行页面里的 JavaScript:如果浏览器中的内容由脚本在页面加载后再请求并渲染,requests 得到的初始 HTML 可能没有那部分数据。此时应先在浏览器开发者工具中确认数据来源,并在符合站点规则的前提下选择合适方案。

1. 安装与解析

python -m pip install beautifulsoup4
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. 解析前后的检查

  1. 保存一小段原始 HTML 样本,便于选择器失效时定位问题。
  2. 统计匹配到的卡片数量;突然变为 0 时不要继续写入空数据。
  3. 对每条数据校验必填字段,例如标题、来源 URL。
  4. 保留来源页面 URL 与抓取时间,方便以后复核。

参考

评论