最佳实践
下面给你一份 Beautiful Soup 最佳实践(专业级),涵盖:初始化、查询模式、性能优化、健壮性、反爬处理、编码、错误处理、与 requests/并发的配合、真实爬虫场景等。
内容是高质量、可直接用于生产环境的 最佳实践合集。
lxml 速度最快、容错最强,生产环境必须用它。
soup = BeautifulSoup(html, "lxml")
不要使用默认的 html.parser —— 太慢、容错差。
CSS 选择器可读性更强,更灵活。
title = soup.select_one(".post-title").text.strip()
items = soup.select("ul.list > li")
网页中很多文本有换行、空格。
text = element.get_text(strip=True)
strip=True 是 BeautifulSoup 最佳方式。
避免 KeyError,提高容错性。
link = a.get("href")
减少噪声,使解析更准确。
for tag in soup(["script", "style"]):
tag.decompose()
避免匹配到太多不必要的节点。
soup.select(".item .title")
soup.find("a", class_="download")
更快更干净:
soup.select(".price")
避免这样写:
soup.find_all("span", {"class": "price"})
items = [li.get_text(strip=True) for li in soup.select("ul li")]
避免手写复杂逻辑。
rows = [
[cell.get_text(strip=True) for cell in row.select("td, th")]
for row in soup.select("table tr")
]
[
{
"title": item.select_one(".title") and item.select_one(".title").text.strip(),
"link": item.select_one("a") and item.select_one("a").get("href"),
}
for item in soup.select(".item")
]
会扫描整个 DOM,非常慢。
比如 .decompose() 取代 .extract()。
减少搜索范围。
headers = {
"User-Agent": "Mozilla/5.0 ..."
}
requests.get(url, headers=headers)
如果页面超过 20MB(超多网页列表),应:
- 分块处理
- 分页获取
- 用 lxml.etree.iterparse()(事件流解析器)
import requests
from bs4 import BeautifulSoup
def fetch(url):
r = requests.get(url, timeout=10)
r.raise_for_status()
r.encoding = r.apparent_encoding
return BeautifulSoup(r.text, "lxml")
避免常见问题:
- 未设置 timeout -> 卡死
- 未 raise_for_status -> 误解析错误页面
- 未处理编码 -> 乱码
必须判断:
title_tag = soup.select_one(".title")
title = title_tag.get_text(strip=True) if title_tag else None
title = (soup.select_one(".title") or {}).get("href")
import time
time.sleep(1 + random.random()) # 随机延时
真实爬虫必须要加!
soup.find_all(string=lambda t: "关键字" in t)
若发现:
- HTML 内容是空的
- 数据通过 JS 渲染
要么:
- 抓取 API 接口
- 使用 Playwright / Selenium
你可以建立一个标准模板:
def parse_item(item):
return {
"title": item.select_one(".title").get_text(strip=True),
"link": item.select_one("a").get("href"),
"price": item.select_one(".price").get_text(strip=True)
if item.select_one(".price") else None
}
def crawl(url):
soup = fetch(url)
return [parse_item(item) for item in soup.select(".item")]
crawler/
├── fetch.py # requests 封装
├── parse.py # beautiful soup 解析
├── models.py # 数据结构
├── pipeline.py # 存储到 DB / 文件
├── utils.py
└── run.py
如爬取新闻标题+链接:
import requests
from bs4 import BeautifulSoup
def fetch(url):
r = requests.get(url, timeout=8)
r.raise_for_status()
return BeautifulSoup(r.text, "lxml")
soup = fetch("https://news.ycombinator.com")
data = [
{
"title": a.get_text(strip=True),
"link": a.get("href")
}
for a in soup.select(".titleline > a")
]
print(data)