HTML 解析器(Soup)¶
零依赖的 HTML 解析器,提供类 BeautifulSoup 的 API —— 仅使用标准库,支持 Python 3.10+。
可替代:
beautifulsoup4(常用子集)
概述¶
Soup 模块基于 html.parser.HTMLParser 构建轻量级 DOM 树。支持 find、find_all、select、select_one、get_text、decompose 和 find_parent —— 涵盖了绝大多数实际网页抓取脚本所使用的 BeautifulSoup 子集。
| 文件 | 描述 | 依赖 |
|---|---|---|
soup.py |
纯 Python 实现 | 无(仅标准库:re、html.parser) |
在你的项目中使用¶
只需将单个 .py 文件复制到你的项目中:
然后直接导入:
使用示例¶
基本解析¶
from soup import Soup
html = "<html><body><p class='msg'>Hello <b>world</b></p></body></html>"
soup = Soup(html)
print(soup.find("p", class_="msg").text)
# Hello world
find 和 find_all¶
html = """
<ul>
<li class="item">Apple</li>
<li class="item">Banana</li>
<li class="item special">Cherry</li>
</ul>
"""
soup = Soup(html)
# 查找第一个匹配
first = soup.find("li")
print(first.text) # Apple
# 查找所有匹配
items = soup.find_all("li", class_="item")
print([i.text for i in items]) # ['Apple', 'Banana', 'Cherry']
CSS 选择器¶
html = """
<div id="main">
<p class="intro">Welcome</p>
<p class="body">Content</p>
</div>
"""
soup = Soup(html)
# 按 ID 选择
main = soup.select_one("#main")
# 按类名选择
intro = soup.select_one(".intro")
print(intro.text) # Welcome
# 按标签选择
paragraphs = soup.select("p")
print(len(paragraphs)) # 2
# 后代选择器
body_p = soup.select_one("div p.body")
print(body_p.text) # Content
# 子选择器
children = soup.select("div > p")
print(len(children)) # 2
# 属性选择器
soup2 = Soup('<a href="/home">Home</a><a href="/about">About</a>')
links = soup2.select("a[href]")
print(len(links)) # 2
# 伪选择器
html3 = """
<ul>
<li class="a">First</li>
<li class="b">Second</li>
<li class="c">Third</li>
</ul>
"""
soup3 = Soup(html3)
# :first-child / :last-child
print(soup3.select_one("li:first-child").text) # First
print(soup3.select_one("li:last-child").text) # Third
# :only-child
soup4 = Soup("<div><span>Alone</span></div>")
print(soup4.select_one("span:only-child").text) # Alone
# :not(selector)
others = soup3.select("li:not(.a)")
print([li.text for li in others]) # ['Second', 'Third']
get_text¶
soup = Soup("<p>Hello <b>world</b></p>")
print(soup.get_text()) # Hello world
print(soup.get_text(separator=" | ")) # Hello | world
print(soup.get_text(strip=True)) # Helloworld
属性访问¶
soup = Soup('<a href="/page" class="nav active" id="link1">Click</a>')
a = soup.find("a")
# 访问属性
print(a["href"]) # /page
print(a["id"]) # link1
print(a.get("class")) # ['nav', 'active']
print(a.get("missing", "default")) # default
树操作¶
from soup import Soup
html = "<ul><li>A</li><li>B</li></ul>"
soup = Soup(html)
ul = soup.find("ul")
# append —— 在末尾添加子节点
new_li = soup.new_tag("li", {"class": "new"})
new_li.children.append("C")
ul.append(new_li)
# insert —— 在指定位置插入子节点
another = soup.new_tag("li")
another.children.append("Z")
ul.insert(0, another)
# extract —— 从父节点移除但保留节点本身
removed = ul.children[1].extract()
# replace_with —— 用另一个节点替换
replacement = soup.new_tag("li")
replacement.children.append("X")
ul.children[0].replace_with(replacement)
# unwrap —— 移除标签但保留子节点
soup2 = Soup("<div><b>bold text</b></div>")
soup2.find("b").unwrap()
print(soup2.get_text()) # bold text
HTML 序列化¶
soup = Soup('<div class="box"><p>Hello <b>world</b></p></div>')
div = soup.find("div")
print(div.to_html())
# <div class="box"><p>Hello <b>world</b></p></div>
# str() 也会生成 HTML
print(str(div))
# <div class="box"><p>Hello <b>world</b></p></div>
属性设置¶
soup = Soup('<a href="/old">Link</a>')
a = soup.find("a")
# 设置属性
a["href"] = "/new"
a["class"] = ["nav", "active"]
# 删除属性
del a["class"]
print(a.to_html()) # <a href="/new">Link</a>
创建新标签¶
soup = Soup("<div></div>")
tag = soup.new_tag("span", {"id": "greeting"})
tag.children.append("Hello!")
soup.find("div").append(tag)
print(soup.find("div").to_html())
# <div><span id="greeting">Hello!</span></div>
decompose(移除元素)¶
html = "<div><p>Keep</p><script>remove me</script></div>"
soup = Soup(html)
for script in soup.find_all("script"):
script.decompose()
print(soup.get_text()) # Keep
find_parent¶
soup = Soup("<div><ul><li>Item</li></ul></div>")
li = soup.find("li")
print(li.find_parent("div").name) # div
print(li.find_parent().name) # ul
支持的 CSS 选择器¶
| 选择器 | 示例 | 描述 |
|---|---|---|
| 标签 | p |
按标签名匹配 |
| 类名 | .intro |
按类名匹配 |
| ID | #main |
按 ID 匹配 |
| 属性 | [href] |
匹配具有某属性的元素 |
| 属性值 | [href="/home"] |
匹配属性值 |
| 后代 | div p |
匹配 div 内的 p |
| 子元素 | div > p |
匹配直接子元素 |
| 复合 | p.intro |
匹配具有 intro 类的 p |
| :first-child | li:first-child |
匹配第一个子元素 |
| :last-child | li:last-child |
匹配最后一个子元素 |
| :only-child | span:only-child |
匹配唯一子元素 |
| :not() | li:not(.active) |
匹配不满足内部选择器的元素 |
API 参考¶
Soup(markup, parser="html.parser")¶
解析 HTML 文档并提供类 BeautifulSoup 的 API。
参数:
| 名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
markup |
str |
-- | 要解析的 HTML 字符串。 |
parser |
str |
"html.parser" |
忽略(仅为 BS4 API 兼容性存在)。 |
Tag 方法¶
| 方法 | 描述 |
|---|---|
find(name, class_, **attrs) |
查找第一个匹配的子元素。 |
find_all(name, class_, **attrs) |
查找所有匹配的子元素。 |
select(selector) |
查找所有匹配 CSS 选择器的元素。 |
select_one(selector) |
查找第一个匹配 CSS 选择器的元素。 |
get_text(separator="", strip=False) |
获取所有文本内容。 |
decompose() |
从父元素中移除此元素。 |
find_parent(name=None) |
查找最近的父元素,可按标签名过滤。 |
get(attr, default=None) |
获取属性值。 |
append(child) |
在末尾添加子节点(Tag 或 str)。 |
insert(index, child) |
在指定位置插入子节点。 |
extract() |
从父节点移除,返回自身(保留子节点)。 |
replace_with(new_node) |
用另一个节点替换此节点。 |
unwrap() |
移除此标签但保留其子节点(重新挂载到父节点)。 |
to_html() |
将此元素序列化为 HTML 字符串。 |
Tag 属性¶
| 属性 | 类型 | 描述 |
|---|---|---|
.text |
str |
所有文本内容(get_text() 的快捷方式)。 |
.name |
str |
标签名(如 "div")。 |
.attrs |
dict |
属性字典。class 存储为列表。 |
.children |
list |
子节点(Tag 或 str)。 |
.parent |
Tag \| None |
父元素。 |
Soup 工厂方法¶
| 方法 | 描述 |
|---|---|
new_tag(name, attrs=None) |
创建一个新的独立 Tag 节点。 |
与 BeautifulSoup 的对比¶
| 特性 | zerodep soup | BeautifulSoup |
|---|---|---|
| 依赖 | 无(仅标准库) | soupsieve,可选 lxml/html5lib |
| 文件数 | 单文件 | 多文件包 |
| 解析器后端 | 仅 html.parser |
html.parser、lxml、html5lib |
| find / find_all | 是 | 是 |
| CSS 选择器 | 标签、类、ID、属性、组合器、伪选择器 | 完整(通过 soupsieve) |
| 树操作(append/insert/extract) | 是 | 是 |
| HTML 序列化(to_html) | 是 | 是(prettify) |
| NavigableString | 否(使用纯 str) |
是 |
| 解析速度(小) | 149 μs | 446 μs(慢 2.99x) |
| 解析速度(大) | 12.7 ms | 37.1 ms(慢 2.93x) |
适用场景(zerodep): 需要基本的 HTML 解析(find、select、get_text),零依赖且高性能。
适用场景(BeautifulSoup): 需要完整 CSS 选择器规范(:nth-child()、:has() 等)、多解析器后端或 NavigableString 功能。
性能测试¶
与 beautifulsoup4 在小、中、大 HTML 文档上进行了基准测试。
详见 Soup 性能测试。