Python使用BeautifulSoup抓取和解析網(wǎng)頁數(shù)據(jù)的入門教程
很多 Python 初學(xué)者學(xué)完基礎(chǔ)語法之后,都會(huì)很自然地遇到一個(gè)問題:
我能不能把網(wǎng)頁里的內(nèi)容提取下來,變成自己能處理的數(shù)據(jù)?
比如:
- 抓取文章標(biāo)題
- 提取商品名稱和價(jià)格
- 收集招聘信息
- 批量整理博客鏈接
- 分析網(wǎng)頁中的表格和列表
這時(shí)候,BeautifulSoup 往往就是最適合入門的工具之一。
它不像大型爬蟲框架那樣一上來就有很多工程化概念,也不像正則表達(dá)式那樣容易把人繞暈。它做的事情很純粹:
幫你解析 HTML,然后從中提取你真正需要的數(shù)據(jù)。
如果你現(xiàn)在正在學(xué)習(xí) Python,又想快速上手網(wǎng)頁數(shù)據(jù)抓取,這篇文章會(huì)非常適合你。我們會(huì)從 0 開始講清楚:
- BeautifulSoup 到底是什么
- 它和
requests是什么關(guān)系 - 如何抓取網(wǎng)頁 HTML
- 如何用
find()、find_all()、select()提取數(shù)據(jù) - 如何寫出一個(gè)完整的小型抓取腳本
- 實(shí)際使用時(shí)有哪些坑要提前避開
1. BeautifulSoup 是什么
BeautifulSoup 是一個(gè)專門用來解析 HTML / XML 文檔的 Python 庫。
你可以把它理解成一個(gè)“網(wǎng)頁內(nèi)容整理器”。
網(wǎng)頁源碼本來是一大段字符串,里面夾雜了很多標(biāo)簽,比如:
<div class="article"> <h2>Python 入門</h2> <a href="/post/1" rel="external nofollow" rel="external nofollow" >查看詳情</a> </div>
如果你直接拿字符串去切片、用正則硬摳,代碼會(huì)非常脆弱。
而 BeautifulSoup 會(huì)先把這段 HTML 解析成一個(gè)結(jié)構(gòu)化對(duì)象,然后你就可以像查樹節(jié)點(diǎn)一樣去找:
- 某個(gè)標(biāo)簽
- 某個(gè) class
- 某個(gè) id
- 某個(gè)屬性
- 某個(gè)標(biāo)簽里的文本
這也是它名字里 Soup 的感覺所在。原本很亂的網(wǎng)頁源碼,會(huì)被它整理成更容易“舀出來”的結(jié)構(gòu)。
2. BeautifulSoup 和 requests 是什么關(guān)系
很多新手剛接觸網(wǎng)頁抓取時(shí),最容易混淆這兩個(gè)庫。
其實(shí)職責(zé)非常清楚:
requests:負(fù)責(zé)向網(wǎng)站發(fā)請(qǐng)求,把網(wǎng)頁內(nèi)容下載回來BeautifulSoup:負(fù)責(zé)解析網(wǎng)頁內(nèi)容,并從中提取數(shù)據(jù)
所以它們常常一起出現(xiàn):
- 先用
requests.get()拿到 HTML - 再用
BeautifulSoup()把 HTML 解析成對(duì)象 - 最后用各種選擇器提取需要的數(shù)據(jù)
一句話記?。?/p>
requests 管“拿網(wǎng)頁”,BeautifulSoup 管“拆網(wǎng)頁”。
3. 先安裝依賴
直接安裝 BeautifulSoup:
pip install beautifulsoup4
實(shí)際抓取網(wǎng)頁時(shí),通常還會(huì)配合 requests:
pip install requests beautifulsoup4
如果你后面想讓解析速度更快,也可以安裝 lxml 作為解析器:
pip install lxml
不過對(duì)初學(xué)者來說,先用 Python 自帶的 html.parser 就夠了。
4. BeautifulSoup 最基礎(chǔ)的使用方式
先不要急著抓真實(shí)網(wǎng)站,先用一段本地 HTML 練手,這樣最容易理解。
from bs4 import BeautifulSoup
html = """
<html>
<body>
<h1>Python 學(xué)習(xí)筆記</h1>
<p class="desc">這是一個(gè) BeautifulSoup 示例頁面</p>
<a href="/post/1" rel="external nofollow" rel="external nofollow" >第一篇文章</a>
<a href="/post/2" rel="external nofollow" >第二篇文章</a>
</body>
</html>
"""
soup = BeautifulSoup(html, "html.parser")
print(soup.h1.get_text())
print(soup.find("p", class_="desc").get_text())
這段代碼里最關(guān)鍵的是:
soup = BeautifulSoup(html, "html.parser")
它表示把字符串形式的 HTML 交給 BeautifulSoup 解析。
后面你就可以通過 soup.h1、find()、select() 這些方式去拿數(shù)據(jù)。
5. 先掌握最常用的 4 個(gè)操作
BeautifulSoup 的 API 不算少,但對(duì)于新手來說,真正最常用的就這幾個(gè)。
5.1find():找第一個(gè)匹配標(biāo)簽
title = soup.find("h1")
print(title.get_text())
如果頁面里有多個(gè) h1,它只會(huì)返回第一個(gè)匹配項(xiàng)。
你也可以加屬性條件:
desc = soup.find("p", class_="desc")
注意這里是 class_,后面要加下劃線,因?yàn)?class 是 Python 關(guān)鍵字。
5.2find_all():找所有匹配標(biāo)簽
links = soup.find_all("a")
for link in links:
print(link.get_text(), link.get("href"))
這個(gè)方法非常適合提取列表數(shù)據(jù),比如:
- 所有文章鏈接
- 所有商品卡片
- 所有招聘項(xiàng)
5.3select():用 CSS 選擇器查找
如果你有一點(diǎn)前端基礎(chǔ),會(huì)非常喜歡這個(gè)方法。
items = soup.select("a")
desc = soup.select_one("p.desc")
常見寫法:
div:選所有div.card:選 class 為card的元素#main:選 id 為main的元素.article-list a:選某個(gè)區(qū)域下面的所有鏈接
在很多實(shí)際項(xiàng)目中,select() 和 select_one() 會(huì)比 find() 更順手。
5.4get_text()和get()
提取文本:
text = title.get_text(strip=True)
提取屬性:
href = link.get("href")
這里要區(qū)分清楚:
get_text():拿標(biāo)簽里的文字get("href"):拿標(biāo)簽屬性
6. 寫一個(gè)稍微像樣一點(diǎn)的解析示例
下面這段示例會(huì)更接近真實(shí)項(xiàng)目。
我已經(jīng)把完整代碼整理成單獨(dú)文件,方便你本地直接運(yùn)行:
beautifulsoup_web_scraping_demo.py
代碼如下:
from __future__ import annotations
from bs4 import BeautifulSoup
import requests
SAMPLE_HTML = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>BeautifulSoup Demo</title>
</head>
<body>
<div class="article-list">
<article class="article-card" data-id="101">
<h2 class="title"><a href="/post/101" rel="external nofollow" >Python 抓取網(wǎng)頁入門</a></h2>
<span class="author">小明</span>
<span class="views">1280</span>
</article>
<article class="article-card" data-id="102">
<h2 class="title"><a href="/post/102" rel="external nofollow" >BeautifulSoup 選擇器快速上手</a></h2>
<span class="author">小紅</span>
<span class="views">980</span>
</article>
<article class="article-card" data-id="103">
<h2 class="title"><a href="/post/103" rel="external nofollow" >requests 和 bs4 配合實(shí)戰(zhàn)</a></h2>
<span class="author">小李</span>
<span class="views">1560</span>
</article>
</div>
</body>
</html>
"""
def parse_local_html() -> list[dict[str, str]]:
soup = BeautifulSoup(SAMPLE_HTML, "html.parser")
articles: list[dict[str, str]] = []
for card in soup.select(".article-card"):
link = card.select_one(".title a")
author = card.select_one(".author")
views = card.select_one(".views")
if link is None or author is None or views is None:
continue
articles.append(
{
"id": card.get("data-id", ""),
"title": link.get_text(strip=True),
"url": link.get("href", ""),
"author": author.get_text(strip=True),
"views": views.get_text(strip=True),
}
)
return articles
def fetch_page_title(url: str) -> str:
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0 Safari/537.36"
)
}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
if soup.title and soup.title.string:
return soup.title.string.strip()
return "頁面沒有 title"
def main() -> None:
print("本地 HTML 解析結(jié)果:")
for item in parse_local_html():
print(item)
# 真實(shí)抓取時(shí),把這里替換成你自己的目標(biāo)網(wǎng)頁。
# 使用前請(qǐng)先確認(rèn)目標(biāo)站點(diǎn)允許抓取,并控制請(qǐng)求頻率。
# title = fetch_page_title("https://example.com")
# print("頁面標(biāo)題:", title)
if __name__ == "__main__":
main()
這個(gè)示例故意分成兩部分:
parse_local_html():先教你專注理解 BeautifulSoup 的解析過程fetch_page_title():再告訴你如何把真實(shí)網(wǎng)頁抓下來后交給 BeautifulSoup
學(xué)習(xí)這類東西時(shí),建議你也這么練:
先會(huì)“解析”,再會(huì)“抓取”,最后再把兩者組合。
7. 真實(shí)抓取網(wǎng)頁時(shí)的標(biāo)準(zhǔn)流程
很多網(wǎng)站的抓取邏輯,本質(zhì)上都可以拆成下面這幾步:
第一步:發(fā)送請(qǐng)求
import requests response = requests.get(url, timeout=10) html = response.text
第二步:構(gòu)造 BeautifulSoup 對(duì)象
from bs4 import BeautifulSoup soup = BeautifulSoup(html, "html.parser")
第三步:定位數(shù)據(jù)區(qū)域
cards = soup.select(".article-card")
第四步:從每個(gè)區(qū)域中提取字段
for card in cards:
title = card.select_one(".title").get_text(strip=True)
link = card.select_one("a").get("href")
第五步:整理成結(jié)構(gòu)化數(shù)據(jù)
data.append({
"title": title,
"url": link,
})
所以你會(huì)發(fā)現(xiàn),BeautifulSoup 真正的核心工作其實(shí)是:
先找到“每條數(shù)據(jù)的邊界”,再從每條記錄里拆字段。
這也是寫抓取腳本時(shí)最重要的思維方式。
8. 新手最常見的一個(gè)誤區(qū):一上來就抓整頁
剛開始學(xué)網(wǎng)頁抓取時(shí),很多人喜歡一上來就對(duì)整頁所有標(biāo)簽亂搜。
例如頁面里有幾十個(gè) a 標(biāo)簽,你直接:
soup.find_all("a")
當(dāng)然能找到一堆鏈接,但里面往往混著:
- 導(dǎo)航欄鏈接
- 廣告鏈接
- 相關(guān)文章鏈接
- 正文鏈接
- 頁腳鏈接
最后數(shù)據(jù)會(huì)很亂。
更好的做法是:
- 先找到列表區(qū)域
- 再在這個(gè)區(qū)域里找標(biāo)題、鏈接、作者、時(shí)間
也就是說,盡量從:
soup.select(".article-card")
這種“按塊定位”的方式入手,而不是全頁面亂抓。
9. BeautifulSoup 常見解析器怎么選
創(chuàng)建對(duì)象時(shí)你會(huì)看到第二個(gè)參數(shù):
BeautifulSoup(html, "html.parser")
這里指定的是解析器。
常見的有三個(gè):
html.parser:Python 自帶,安裝最省事lxml:速度通常更快,項(xiàng)目里很常用html5lib:容錯(cuò)更強(qiáng),但一般更重
如果你是初學(xué)者:
- 學(xué)習(xí)階段先用
html.parser - 后面項(xiàng)目里再考慮
lxml
先跑通流程,比一開始糾結(jié)性能更重要。
10. 為什么有時(shí)抓不到你在瀏覽器里看到的內(nèi)容
這是 BeautifulSoup 初學(xué)者非常容易遇到的坑。
你在瀏覽器里明明看到了內(nèi)容,但 requests.get() 拿回來的 HTML 里卻沒有。
原因通常是:
頁面的數(shù)據(jù)是通過 JavaScript 動(dòng)態(tài)加載的。
也就是說,瀏覽器打開頁面后,還會(huì)繼續(xù)執(zhí)行 JS,再向接口請(qǐng)求數(shù)據(jù),最后才把內(nèi)容渲染出來。
而 requests 拿到的只是最初的 HTML。
這時(shí)候 BeautifulSoup 沒問題,問題在于你抓到的輸入源就不完整。
遇到這種情況,常見思路有兩個(gè):
- 直接分析網(wǎng)頁接口,改抓接口返回的 JSON
- 使用 Playwright、Selenium 這類瀏覽器自動(dòng)化工具
所以要記住:
BeautifulSoup 擅長解析 HTML,但它不會(huì)替你執(zhí)行前端 JavaScript。
11. 實(shí)戰(zhàn)中必須注意的幾個(gè)問題
這部分比語法更重要。
11.1 不要把網(wǎng)頁抓取理解成“復(fù)制粘貼源碼”
真正的抓取工作,不是看到標(biāo)簽就抄代碼,而是要先觀察頁面結(jié)構(gòu):
- 每條數(shù)據(jù)的容器是什么
- 標(biāo)題在哪個(gè)標(biāo)簽
- 鏈接在哪個(gè)屬性
- 時(shí)間、作者、價(jià)格是否都在固定位置
只有先分析結(jié)構(gòu),BeautifulSoup 才會(huì)變得好用。
11.2 記得加請(qǐng)求頭
有些網(wǎng)站對(duì)默認(rèn)請(qǐng)求不友好,最簡單的處理就是帶一個(gè)常見瀏覽器的 User-Agent。
headers = {
"User-Agent": "Mozilla/5.0 ..."
}
response = requests.get(url, headers=headers, timeout=10)
11.3 記得處理異常
不要默認(rèn)所有請(qǐng)求都會(huì)成功。
response.raise_for_status()
至少先把 HTTP 錯(cuò)誤暴露出來,否則你很可能在拿一個(gè)報(bào)錯(cuò)頁做解析。
11.4 控制抓取頻率
如果你需要抓取多頁內(nèi)容,不要用極高頻率連續(xù)請(qǐng)求。
這不僅容易失敗,也可能給目標(biāo)網(wǎng)站帶來壓力。
11.5 尊重站點(diǎn)規(guī)則和數(shù)據(jù)邊界
在使用 BeautifulSoup 抓取網(wǎng)頁數(shù)據(jù)前,應(yīng)該先確認(rèn)目標(biāo)站點(diǎn)的使用條款、robots 規(guī)則以及數(shù)據(jù)使用邊界。
技術(shù)上能抓,不代表業(yè)務(wù)上就一定適合抓。
12. BeautifulSoup 適合哪些場景
BeautifulSoup 非常適合下面這些任務(wù):
- 網(wǎng)頁結(jié)構(gòu)比較穩(wěn)定
- 頁面主要內(nèi)容就在 HTML 中
- 抓取規(guī)模不算特別大
- 想快速寫一個(gè)輕量腳本
- 想先驗(yàn)證網(wǎng)頁數(shù)據(jù)能不能提取
比如:
- 文章列表抓取
- 簡單商品頁抓取
- 博客導(dǎo)航頁整理
- 表格數(shù)據(jù)提取
- 頁面鏈接分析
如果你只是想先把網(wǎng)頁數(shù)據(jù)提下來,再做后續(xù)分析處理,BeautifulSoup 是一個(gè)很好的起點(diǎn)。
13. BeautifulSoup 和 Scrapy 該怎么選
這個(gè)問題很多人都會(huì)問。
可以這樣理解:
- BeautifulSoup 更像“解析工具”
- Scrapy 更像“完整爬蟲框架”
如果你的需求是:
- 抓 1 個(gè)到幾十個(gè)頁面
- 快速寫腳本驗(yàn)證
- 重點(diǎn)在網(wǎng)頁解析本身
那么 requests + BeautifulSoup 會(huì)更輕、更直接。
如果你的需求是:
- 大量分頁抓取
- 自動(dòng)跟進(jìn)鏈接
- 需要統(tǒng)一導(dǎo)出、調(diào)度、去重、重試
那通常就要考慮 Scrapy 了。
所以 BeautifulSoup 并不是“低配版 Scrapy”,而是更適合入門和輕量場景的工具。
14. 給初學(xué)者的學(xué)習(xí)建議
如果你想真正學(xué)會(huì) BeautifulSoup,不建議只看語法。
更好的學(xué)習(xí)順序是:
- 先用本地 HTML 字符串練
find()、find_all()、select() - 再學(xué)
requests.get()把網(wǎng)頁拿下來 - 再自己挑一個(gè)結(jié)構(gòu)簡單的頁面練手
- 把提取結(jié)果整理成字典或列表
- 最后導(dǎo)出成 JSON、CSV 或?qū)懭霐?shù)據(jù)庫
你會(huì)發(fā)現(xiàn),真正的能力不是“會(huì)幾個(gè) API”,而是:
看到一個(gè)網(wǎng)頁,能快速判斷該從哪里切入提取數(shù)據(jù)。
15. 總結(jié)
BeautifulSoup 是 Python 生態(tài)里非常經(jīng)典、也非常適合新手入門的網(wǎng)頁解析庫。
它最大的價(jià)值不是“語法多高級(jí)”,而是把網(wǎng)頁提取這件事變得直觀了很多。
記住這套組合最核心的分工:
requests:負(fù)責(zé)抓網(wǎng)頁BeautifulSoup:負(fù)責(zé)拆網(wǎng)頁- 你的 Python 代碼:負(fù)責(zé)整理數(shù)據(jù)和后續(xù)處理
如果你正在學(xué)習(xí) Python,又希望盡快做出一些“能解決實(shí)際問題”的小工具,那么 BeautifulSoup 非常值得你親手寫一遍。
先從本地 HTML 開始,跑通選擇器,再去抓真實(shí)網(wǎng)頁,你的進(jìn)步會(huì)快很多。
以上就是Python使用BeautifulSoup抓取和解析網(wǎng)頁數(shù)據(jù)的入門教程的詳細(xì)內(nèi)容,更多關(guān)于Python BeautifulSoup抓取和解析網(wǎng)頁數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python對(duì)兩個(gè)有序列表進(jìn)行合并和排序的例子
這篇文章主要介紹了Python對(duì)兩個(gè)有序列表進(jìn)行合并和排序的例子,最終代碼經(jīng)過不斷優(yōu)化,小編非常滿意,需要的朋友可以參考下2014-06-06
使用Python算法實(shí)現(xiàn)從字符串中提取重復(fù)子串
在文本處理和數(shù)據(jù)分析中,經(jīng)常需要從字符串中提取重復(fù)出現(xiàn)的子串,本文將解析一個(gè)高效的Python算法,用于從給定字符串中提取長度超過3的重復(fù)子串,需要的朋友可以參考下2025-10-10
淺談對(duì)pytroch中torch.autograd.backward的思考
這篇文章主要介紹了對(duì)pytroch中torch.autograd.backward的思考,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
解決Python獲取字典dict中不存在的值時(shí)出錯(cuò)問題
今天小編就為大家分享一篇解決Python獲取字典dict中不存在的值時(shí)出錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10
python實(shí)現(xiàn)大文本文件分割成多個(gè)小文件
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)大文本文件分割成多個(gè)小文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04

