前言
爬虫拿到HTML源码之后,最核心的工作就是提取特定元素、文本、链接 。BeautifulSoup4(bs4)提供多种方式定位页面标签:标签名搜索、class/id匹配、属性过滤、CSS选择器。CSS选择器select()/select_one()日常开发用得最多,写法简洁直观。
前置依赖
pip install beautifulsoup4 lxml
from bs4 import BeautifulSoup
html_sample = """
<div class="table-wrap">
<div class="item row">
<div class="title">标准编号:GB50017‑2003</div>
<div class="type">国家标准</div>
<a href="/detail?id=1001" class="link">查看详情</a>
</div>
<div class="item row">
<div class="title">标准编号:GB50405‑2007</div>
<div class="type">国家标准</div>
<a href="/detail?id=1002" class="link">查看详情</a>
</div>
</div>
"""
# 加载HTML文档
soup = BeautifulSoup(html_sample, "lxml")
1. find():提取第一个匹配的元素
find(标签名, 属性条件),找到第一个匹配节点,找不到返回None,不会抛异常。
# 查找第一个class="item row"的div节点
tag = soup.find("div", class_="item row")
if tag:
print(tag.get_text(strip=True))
# 根据id查找
soup.find(id="table-wrap")
# 根据自定义属性过滤,比如 data‑xxx
soup.find("a", attrs={"class":"link"})
⚠️注意:
class是Python关键字,参数要写class_=,末尾带下划线。
2. find_all():提取全部匹配元素,返回列表
返回所有符合条件节点组成的列表,没有匹配返回空列表[]。
# 获取全部 class="item row" 的div
item_list = soup.find_all("div", class_="item row")
for item in item_list:
text = item.get_text(strip=True)
print(text)
# 限制返回数量,只取前1条
item_list = soup.find_all("div", class_="item row", limit=1)
3. CSS选择器 select / select_one(最推荐)
select_one():匹配第一个 ,无结果返回None
select():匹配全部,返回节点列表
语法和网页CSS完全一致:
-
.类名→ class筛选 -
#id值→ id筛选 -
标签名→ 标签筛选 -
父标签 > 子标签→ 直接子元素 -
父标签 子标签→ 后代所有子元素取第一个 .item.row
first_item = soup.select_one(".item.row")
获取全部 .item.row
all_items = soup.select(".item.row")
获取 .item 下面所有a标签
link_tags = soup.select(".item.row a.link")
直接子元素 >
title_div = soup.select_one(".item.row > .title")
4. 拿到元素之后:提取文本、属性
提取文本
tag = soup.select_one(".title")
# get_text() 推荐,支持参数控制
text1 = tag.get_text(strip=True) # 去除前后换行空格
text2 = tag.get_text(strip=True, separator=" ") # 子节点文本用空格隔开
# 不推荐 .text ,无法做strip处理
# text = tag.text
提取标签属性(href、src、style等)
优先使用.get(),属性不存在返回None,不会抛出KeyError
a_tag = soup.select_one("a.link")
href = a_tag.get("href") # ✅ 安全,无属性返回None
# 不推荐直接下标取值,没有属性直接报错
# href = a_tag["href"]
# 获取全部属性字典
attr = a_tag.attrs
print(attr)
5. 实战示例:循环提取多条列表数据
以上面示例html,循环取出每条的标题、类型、链接:
item_list = soup.select(".item.row")
result = []
for item in item_list:
title = item.select_one(".title").get_text(strip=True)
std_type = item.select_one(".type").get_text(strip=True)
link = item.select_one(".link").get("href")
result.append({
"title": title,
"std_type": std_type,
"link": link
})
print(result)
6. 结合requests网页真实场景
import requests
from bs4 import BeautifulSoup
resp = requests.get("[https://example.com](https://example.com)")
soup = BeautifulSoup(resp.text, "lxml")
# 提取页面所有a标签链接
for a in soup.select("a"):
url = a.get("href")
print(url)
7. 常见踩坑点
-
判空!
find()/select_one()找不到返回None,直接调用.get_text()会报AttributeError。tag = soup.select_one(".not‑exist")
if tag is not None:
txt = tag.get_text(strip=True) -
class有多个值时:
class="item row",bs4中class_="item row"可以匹配;CSS选择器写.item.row(中间无空格)。 -
不要用正则去解析HTML,标签嵌套、属性顺序变化,正则极易失效。
-
select()永远返回列表,哪怕只有一条数据,需要用下标[0]或者改用select_one()。 -
网页残缺、标签不闭合,
lxml解析器容错能力强,优先使用。
find/find_all 和 select/select_one怎么选
| 方式 | 优点 | 缺点 |
|---|---|---|
| find / find_all | 原生API,支持复杂属性过滤 | 写多层嵌套比较繁琐 |
| select / select_one | CSS选择器,写法简短,前端开发易上手 | 部分复杂属性匹配不如find灵活 |
日常爬虫开发优先使用
select、select_one。