Python?HTML解析:BeautifulSoup,Lxml,XPath使用教程
想要精找到數(shù)據(jù)卻不知從何下手?這篇文章將帶你了解 Python 中進(jìn)行 HTML 解析的常用工具,還會(huì)通過一個(gè)【網(wǎng)頁提取小工具】的例子,看看如何將這些技術(shù)應(yīng)用起來,并簡單探討一下結(jié)合智能技術(shù)自動(dòng)提取正文、生成摘要和關(guān)鍵詞的可能性。
告別手動(dòng)復(fù)制粘貼:用CSS選擇器與XPath高效提取網(wǎng)頁數(shù)據(jù)
不管怎么說,先睹為快
工具效果圖:

工具簡單流程圖

為什么需要專門的HTML解析庫?只用正則表達(dá)式不夠嗎?
想象一下,你需要從好幾個(gè)競爭對(duì)手的網(wǎng)站上快速抓取產(chǎn)品介紹,或者想從新聞網(wǎng)站提取最新的科技動(dòng)態(tài)標(biāo)題和鏈接。
我們拿到的網(wǎng)頁源碼,通常是類似下面這樣的 HTML 結(jié)構(gòu):
<html>
<head><title>最新新聞</title></head>
<body>
<div class="news-list">
<article><h2><a href="/news/1" rel="external nofollow" >標(biāo)題一</a></h2><p>摘要...</p></article>
<article><h2><a href="/news/2" rel="external nofollow" >標(biāo)題二</a></h2><p>摘要...</p></article>
</div>
</body>
</html>如果直接用正則表達(dá)式來提取這些信息,你可能會(huì)發(fā)現(xiàn)一些麻煩:
- 容易失效:網(wǎng)站稍微調(diào)整一下頁面結(jié)構(gòu),寫好的正則表達(dá)式可能就用不了了,維護(hù)起來比較頭疼。
- 寫起來復(fù)雜:對(duì)于層層嵌套或者結(jié)構(gòu)比較亂的 HTML,編寫和調(diào)試正則表達(dá)式本身就挺花時(shí)間的。
- 功能有限:正則表達(dá)式不太擅長處理標(biāo)簽之間的層級(jí)關(guān)系,比如找到某個(gè)元素的父元素或兄弟元素。
小結(jié): 網(wǎng)頁越來越復(fù)雜,只靠正則表達(dá)式來提取數(shù)據(jù),往往效率不高,而且不夠穩(wěn)定。這時(shí)候,我們就需要更專業(yè)的工具了。
HTML解析庫就是為此而生的。它們能理解 HTML 的文檔結(jié)構(gòu)(也就是 DOM,文檔對(duì)象模型),把源碼解析成一個(gè)樹狀的對(duì)象。這樣,我們就能用更可靠、更方便的方式來找到并拿出我們想要的東西。

在 Python 里,比較常用的 HTML 解析庫有這么幾個(gè):
BeautifulSoup(BS4):它的接口設(shè)計(jì)得比較友好,學(xué)起來相對(duì)容易,適合新手入門和快速做一些小工具。lxml:它是基于 C 語言庫構(gòu)建的,解析速度通常更快,處理不規(guī)范 HTML 的能力也比較強(qiáng)(容錯(cuò)性好),還支持 XPath 這種強(qiáng)大的查詢語言。適合處理大型或者結(jié)構(gòu)復(fù)雜的文檔,或者對(duì)性能要求比較高的場景。
其他選擇:
html.parser: Python 自帶的,不用額外安裝,但速度和容錯(cuò)性一般。html5lib: 據(jù)說最接近瀏覽器的解析方式,容錯(cuò)性最好,但相應(yīng)地,速度會(huì)慢一些。
怎么選呢?
- 入門或者大部分情況:推薦試試
BeautifulSoup結(jié)合lxml解析器。這樣既能利用BeautifulSoup的易用性,也能獲得不錯(cuò)的性能。 - 追求更好的性能或需要處理復(fù)雜查詢:可以直接上手
lxml。
接下來,咱們先從 BeautifulSoup 開始,看看怎么用它來解析 HTML。
BeautifulSoup:上手簡單的HTML解析
BeautifulSoup (我們常叫它 BS4) 的設(shè)計(jì)初衷就是讓 HTML 解析這事兒變得簡單明了。
1. 安裝和準(zhǔn)備
# 安裝 BS4 和推薦搭配使用的 lxml 解析器 pip install beautifulsoup4 lxml
在 Python 代碼里這么用:
from bs4 import BeautifulSoup import requests # 用于獲取網(wǎng)頁內(nèi)容 # 示例 HTML (實(shí)際通常來自 requests 獲取的 response.text) html_doc = """ <html><head><title>一個(gè)簡單的例子</title></head> <body> <p class="title"><b>加粗標(biāo)題</b></p> <p class="story">這是一個(gè)段落... <a rel="external nofollow" class="sister" id="link1">鏈接1</a>, <a rel="external nofollow" class="sister" id="link2">鏈接2</a> and <a rel="external nofollow" class="sister" id="link3">鏈接3</a>; 開始了新的故事.</p> <p class="data">...</p> </body></html> """ # 初始化 BeautifulSoup 對(duì)象,告訴它我們想用 'lxml' 這個(gè)解析器 soup = BeautifulSoup(html_doc, 'lxml')
2. 按標(biāo)簽名訪問
如果 HTML 結(jié)構(gòu)比較簡單,可以直接用點(diǎn) (.) 加上標(biāo)簽名來訪問第一個(gè)匹配到的標(biāo)簽:
print(soup.title) # 輸出: <title>一個(gè)簡單的例子</title> print(soup.title.name) # 輸出: 'title' (標(biāo)簽名) print(soup.title.string) # 輸出: '一個(gè)簡單的例子' (標(biāo)簽內(nèi)文本) print(soup.p) # 輸出: <p class="title"><b>加粗標(biāo)題</b></p> (找到的第一個(gè) p 標(biāo)簽)
3. 用 find() 和 find_all() 精確查找 (常用方法)
這是最常用的查找方式了,可以根據(jù)標(biāo)簽名、CSS 類名、ID、甚至其他屬性來找。
find('tag', class_='css_class', id='id_val', attrs={'attr': 'value'}): 查找第一個(gè)符合條件的標(biāo)簽,找到就返回那個(gè)標(biāo)簽對(duì)象,找不到就返回None。find_all(...): 查找所有符合條件的標(biāo)簽,返回一個(gè)列表(這個(gè)列表可能是空的)。還可以用limit=N來限制最多找多少個(gè)。
舉個(gè)例子: 假設(shè)我們要提取所有 CSS 類是 product-title 的 h2 標(biāo)簽里的文字。
# 查找所有 class='product-title' 的 h2 標(biāo)簽
product_titles = soup.find_all('h2', class_='product-title')
# 遍歷結(jié)果列表
for title in product_titles:
# 使用 .get_text(strip=True) 獲取純文本,更穩(wěn)妥
print(title.get_text(strip=True))注意一個(gè)小細(xì)節(jié):class vs class_
因?yàn)?class 在 Python 里是個(gè)關(guān)鍵字(用來定義類),所以在 find 或 find_all 里按 CSS 類名查找時(shí),參數(shù)名叫 class_ (后面多了個(gè)下劃線)。當(dāng)然,你也可以用 attrs 字典來指定:soup.find_all('a', attrs={'class': 'sister'})。
4. 用 CSS 選擇器 select() (簡潔又強(qiáng)大)
如果你熟悉 CSS,那 select() 方法會(huì)讓你感覺很親切。它的語法和 CSS 選擇器幾乎一樣。
- 標(biāo)簽名:
select('p') - 類名:
select('.product-title') - ID:
select('#main-content') - 子元素:
select('div.article > h2')(找div下直接的h2子元素) - 后代元素:
select('div#news-list article')(找div下所有層級(jí)的article元素) - 屬性:
select('a[target="_blank"]')(找target屬性是_blank的a標(biāo)簽)
舉個(gè)例子: 提取新聞列表(假設(shè)是 div.news-list)里所有文章標(biāo)題(在 h2 里的鏈接 a)。
# 使用 CSS 選擇器定位
news_links = soup.select('div.news-list h2 a')
for link in news_links:
# link.string 獲取文本, link['href'] 獲取 href 屬性
print(f"標(biāo)題: {link.string.strip()}, 鏈接: {link['href']}")
5. 提取文本和屬性
找到目標(biāo)標(biāo)簽后,我們通常關(guān)心的是里面的文字內(nèi)容,或者是它的某個(gè)屬性值(比如鏈接的 href)。
獲取文本:
.string: 這個(gè)屬性比較"挑剔",只有當(dāng)標(biāo)簽里沒有其他嵌套標(biāo)簽,純粹是文本時(shí)才好用,否則可能返回None。.get_text(): (推薦使用) 這個(gè)方法能獲取標(biāo)簽內(nèi)所有的文本內(nèi)容,包括所有子標(biāo)簽里的。strip=True: 參數(shù)設(shè)為True可以去掉文本開頭和結(jié)尾的空白字符(像空格、換行符)。separator='sep': 如果標(biāo)簽內(nèi)有多段文本(比如被<br>分隔),可以用這個(gè)參數(shù)指定一個(gè)分隔符把它們連接起來。
獲取屬性:
tag['attr_name']: 像訪問字典一樣用方括號(hào)加屬性名。但如果這個(gè)屬性不存在,代碼會(huì)報(bào)錯(cuò)。tag.get('attr_name'): (推薦使用) 這個(gè)方法更安全,如果屬性不存在,它會(huì)返回None,而不是報(bào)錯(cuò)。
舉個(gè)例子: 獲取頁面上所有鏈接的文字和 URL。
all_links = soup.find_all('a')
for link in all_links:
link_text = link.get_text(strip=True)
link_url = link.get('href') # 用 get() 好處是,萬一這個(gè) a 標(biāo)簽沒有 href 屬性也不會(huì)出錯(cuò)
if link_url: # 最好判斷一下確實(shí)拿到了 URL 再打印
print(f"鏈接文字: {link_text}, URL: {link_url}")6. 在節(jié)點(diǎn)間導(dǎo)航 (遍歷DOM樹)
有時(shí)候,我們找到了一個(gè)節(jié)點(diǎn),還需要找它的父節(jié)點(diǎn)、子節(jié)點(diǎn)或者旁邊的兄弟節(jié)點(diǎn)。
.contents/.children: 獲取直接子節(jié)點(diǎn)的列表 / 迭代器。.descendants: 獲取所有后代節(jié)點(diǎn)(包括文字節(jié)點(diǎn))的迭代器。.parent/.parents: 獲取直接父節(jié)點(diǎn) / 所有祖先節(jié)點(diǎn)的迭代器。.next_sibling/.previous_sibling: 獲取下一個(gè)/上一個(gè)兄弟節(jié)點(diǎn)。注意,這可能拿到的是兩個(gè)標(biāo)簽之間的空白文本節(jié)點(diǎn),不一定是標(biāo)簽。.next_siblings/.previous_siblings: 獲取后面/前面所有兄弟節(jié)點(diǎn)的迭代器。.find_next_sibling(s)()/.find_previous_sibling(s)(): (比較實(shí)用) 這組方法可以查找符合條件的下一個(gè)/上一個(gè)標(biāo)簽兄弟,能幫你跳過那些文本節(jié)點(diǎn)。
舉個(gè)例子: 找到標(biāo)題標(biāo)簽后,想找它后面緊跟著的價(jià)格標(biāo)簽(假設(shè)是個(gè) span)。
# 假設(shè) title_tag 是已找到的標(biāo)題標(biāo)簽
# 查找其后第一個(gè) class='price' 的 span 兄弟標(biāo)簽
price_tag = title_tag.find_next_sibling('span', class_='price')
if price_tag:
print(f"價(jià)格是: {price_tag.get_text(strip=True)}")一個(gè)參數(shù) recursive=False:
在用 find_all() 等查找方法時(shí),可以加上 recursive=False 這個(gè)參數(shù)。它的意思是只查找當(dāng)前標(biāo)簽的直接子節(jié)點(diǎn),不再往更深層級(jí)的后代節(jié)點(diǎn)里查找。
lxml:追求速度和 XPath 的選擇
lxml 這個(gè)庫以速度快和解析能力強(qiáng)而出名,特別適合處理那些很大或者結(jié)構(gòu)很復(fù)雜的 HTML/XML 文檔。
1. 安裝和基本使用
pip install lxml
from lxml import etree
# 直接解析 HTML 字符串,lxml 會(huì)嘗試修復(fù)不規(guī)范的 HTML
tree = etree.HTML(html_doc) # html_doc 是之前的示例
# 也可以從文件或網(wǎng)絡(luò)響應(yīng)解析
# tree = etree.HTML(response.text)
# parser = etree.HTMLParser(encoding='utf-8') # 指定編碼
# tree = etree.parse('your_page.html', parser)2. XPath:一種強(qiáng)大的節(jié)點(diǎn)選擇語言
XPath 提供了一套靈活的語法規(guī)則,讓你可以在文檔樹里精確地定位到想要的節(jié)點(diǎn)。
常用的語法規(guī)則:
/: 從根節(jié)點(diǎn)開始選。//: 從文檔中任意位置開始查找(這個(gè)最常用)。 比如//div就是找所有的 div 元素。.: 代表當(dāng)前節(jié)點(diǎn)。..: 代表父節(jié)點(diǎn)。tag_name: 選擇指定標(biāo)簽名的節(jié)點(diǎn)。 比如a就是找所有 a 標(biāo)簽。@attr_name: 選擇屬性。 比如//@href就是找所有 href 屬性。*: 通配符,匹配任何元素節(jié)點(diǎn)。[@attr='value']: 根據(jù)屬性值精確匹配。 比如//a[@id='link1']就是找 id 等于 ‘link1’ 的 a 標(biāo)簽。[contains(@attr, 'substr')]: 檢查屬性值是否包含某個(gè)子字符串。 比如//div[contains(@class, 'article')]找 class 包含 ‘article’ 的 div。[starts-with(@attr, 'prefix')]: 檢查屬性值是否以某個(gè)前綴開頭。[N]: 選擇第 N 個(gè)匹配的元素(注意:XPath 的索引是從 1 開始算的,不是 0!)。 比如//ul/li[1]找 ul 下的第一個(gè) li。text(): 獲取節(jié)點(diǎn)的文本內(nèi)容。 比如//p/text()獲取 p 標(biāo)簽直接包含的文本。|: 表示"或"邏輯,可以合并多個(gè)路徑的結(jié)果。 比如//h2/text() | //h3/text()同時(shí)獲取 h2 和 h3 的文本。

用 XPath 試試(還是用前面的 html_doc):
# 獲取 class='story' 的 p 下面第一個(gè) a 標(biāo)簽的文本
first_link_text = tree.xpath('//p[@class="story"]/a[1]/text()') # a[1] 指的是第一個(gè) a 標(biāo)簽
print(first_link_text[0])XPath 的一些優(yōu)點(diǎn):
- 軸(Axis)選擇: 能支持更復(fù)雜的節(jié)點(diǎn)關(guān)系定位,比如找前面的兄弟節(jié)點(diǎn)、祖先節(jié)點(diǎn)等等,比 CSS 選擇器更靈活。
- 內(nèi)置函數(shù): 提供了一些有用的函數(shù),像
count()計(jì)數(shù),sum()求和,contains()判斷包含,normalize-space()清理空白字符等。
3. lxml 其實(shí)也支持 CSS 選擇器
lxml 借助一個(gè)叫 cssselect 的庫,也提供了對(duì) CSS 選擇器的支持。你需要先安裝它:pip install cssselect。
# 使用 .cssselect() 方法
link1_css_lxml = tree.cssselect('a#link1')
print(link1_css_lxml[0].text) # Element 對(duì)象有 .text 屬性
story_links_css_lxml = tree.cssselect('p.story a.sister')
print(f"CSS 選擇器找到 {len(story_links_css_lxml)} 個(gè) sister 鏈接")XPath 和 CSS 選擇器怎么選?
- 學(xué)習(xí)難度: 一般認(rèn)為 CSS 選擇器更容易上手。
- 功能: XPath 功能更強(qiáng)大,特別是在處理復(fù)雜的路徑、條件判斷或者需要利用軸選擇的時(shí)候。
- 性能 (在 lxml 內(nèi)部): 兩者性能通常差不多,有時(shí)候 XPath 甚至可能更快一點(diǎn)。
建議: 先掌握好 CSS 選擇器,它能搞定大部分場景。如果遇到 CSS 選擇器處理起來很麻煩或者實(shí)現(xiàn)不了的需求,再考慮用 XPath。
組合與比較:BeautifulSoup + lxml 是個(gè)好搭檔嗎?
把 BeautifulSoup 相對(duì)友好的接口和 lxml 的高速解析能力結(jié)合起來,確實(shí)是個(gè)常見的做法,能在開發(fā)效率和運(yùn)行速度之間取得不錯(cuò)的平衡。
# 指定 lxml 作為 BS4 的解析器 soup = BeautifulSoup(html_doc, 'lxml') # 然后就可以照常使用 soup.find, soup.select 這些 BS4 的方法了
性能特點(diǎn)大概是這樣:

- 速度: 通常情況下,直接用
lxml最快 >BS4+lxml組合 >BS4+html.parser(Python自帶)。處理大文件時(shí),lxml的速度優(yōu)勢(shì)會(huì)更明顯。 - 內(nèi)存:
lxml通常也更節(jié)省內(nèi)存。 - 易用性:
BeautifulSoup的 API 設(shè)計(jì)更符合 Python 的習(xí)慣,學(xué)起來感覺更順手一些。 - 容錯(cuò)性:
lxml和html5lib在處理不太規(guī)范的 HTML 代碼時(shí),表現(xiàn)比較好,不容易出錯(cuò)。
怎么選比較合適?
- 寫一些日常腳本、教學(xué)演示、或者快速搭個(gè)原型: 用
BeautifulSoup+lxml這個(gè)組合挺好,開發(fā)體驗(yàn)不錯(cuò),性能也足夠用了。 - 開發(fā)大規(guī)模爬蟲、或者對(duì)性能要求比較高的應(yīng)用: 可以優(yōu)先考慮直接使用
lxml。 - 需要處理非常不規(guī)范、甚至可以說是"臟"的 HTML: 可以試試
BeautifulSoup+html5lib這個(gè)組合,它的容錯(cuò)性最好。
動(dòng)手試試:一個(gè)簡單的【網(wǎng)頁提取小工具】
現(xiàn)在,咱們把前面學(xué)到的東西用起來,做一個(gè)簡單的【網(wǎng)頁提取小工具】。
這個(gè)小工具有啥用?
- 學(xué)習(xí)研究: 比如快速扒某個(gè)網(wǎng)頁的主要文字內(nèi)容,或者看看文獻(xiàn)摘要。
- 信息收集: 從一些報(bào)告、資訊網(wǎng)站抓取文本,做初步的摘要和關(guān)鍵詞提取。
- 內(nèi)容輔助: 找資料時(shí),快速獲取網(wǎng)頁主體內(nèi)容,幫助提煉重點(diǎn)。
核心功能(可以考慮后續(xù)擴(kuò)展):
- 輸入一個(gè)網(wǎng)址(URL),嘗試提取出頁面的主要文本內(nèi)容。
- 生成一個(gè)簡單的摘要(比如就提取前幾句話)。
- 提取一些關(guān)鍵詞(比如基于詞頻統(tǒng)計(jì))。
- (擴(kuò)展思路:可以對(duì)接外部的智能處理服務(wù),做得更智能)
技術(shù)選型: Python + Flask (做個(gè)簡單的網(wǎng)頁界面) + requests (獲取網(wǎng)頁) + BeautifulSoup/lxml (解析HTML) + (可選的智能接口)
界面大概長這樣:
一個(gè)簡單的頁面,有個(gè)輸入框讓你填網(wǎng)址,一個(gè)提交按鈕,下面顯示提取結(jié)果(比如原文預(yù)覽、摘要、關(guān)鍵詞)。

核心代碼邏輯 (用 Flask + BeautifulSoup 舉例,并標(biāo)注了可擴(kuò)展的地方):
# --- codes/web_content_extractor/app.py ---
# (代碼和之前版本差不多,這里重點(diǎn)看邏輯和注釋)
# ... Flask 初始化和模板渲染設(shè)置 ...
# ... 可以先定義簡單的摘要和關(guān)鍵詞提取函數(shù) (simple_summarizer, simple_keyword_extractor) ...
@app.route('/', methods=['GET', 'POST'])
def index():
result = None
error = None
if request.method == 'POST':
url = request.form.get('url')
if url:
# 模擬瀏覽器發(fā)請(qǐng)求,加個(gè) User-Agent 通常是個(gè)好習(xí)慣
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'}
try:
response = requests.get(url, headers=headers, timeout=10) # 設(shè)置超時(shí)防止卡死
response.raise_for_status() # 如果請(qǐng)求失敗 (例如 404, 500),這里會(huì)拋出異常
# 嘗試讓 requests 自動(dòng)檢測(cè)編碼,或者根據(jù)情況手動(dòng)指定 response.encoding = 'utf-8'
response.encoding = response.apparent_encoding
soup = BeautifulSoup(response.text, 'lxml') # 用 lxml 解析器
# --- 提取主要內(nèi)容的邏輯 ---
# 這部分是難點(diǎn),沒有完美通用的方法,通常需要嘗試多種策略
main_content_html = None
# 策略1: 嘗試查找常見的表示主要內(nèi)容的標(biāo)簽或類名
# (這些選擇器是經(jīng)驗(yàn)性的,可能需要根據(jù)目標(biāo)網(wǎng)站調(diào)整)
content_selectors = ['article', 'main', '.content', '#main', '.post-body', '.entry-content', '.article-content']
for selector in content_selectors:
content_element = soup.select_one(selector) # select_one 找到第一個(gè)匹配的
if content_element:
# 簡單判斷下內(nèi)容長度,避免選中一個(gè)空標(biāo)簽或無關(guān)的小區(qū)域
if len(content_element.get_text(strip=True)) > 100: # 閾值可以調(diào)整
main_content_html = content_element
print(f"策略1命中: {selector}")
break
# 策略2: 如果策略1沒找到,嘗試清理掉干擾元素(導(dǎo)航、頁腳、廣告等),然后用 body
if not main_content_html:
print("嘗試策略2: 清理通用模塊...")
# 創(chuàng)建一個(gè)副本進(jìn)行清理操作,以免影響原始 soup 對(duì)象
soup_for_cleaning = BeautifulSoup(str(soup), 'lxml')
# 要移除的元素選擇器列表 (根據(jù)常見情況列出,可能需要補(bǔ)充)
elements_to_remove = ['script', 'style', 'header', 'footer', 'nav', '.sidebar', '.ads', '.comment', '#comments', '.related-posts']
for selector in elements_to_remove:
try:
for tag in soup_for_cleaning.select(selector):
tag.decompose() # decompose() 會(huì)將標(biāo)簽及其內(nèi)容從樹中移除
except Exception as e:
# select 找不到元素不會(huì)報(bào)錯(cuò),但其他操作可能出錯(cuò)
print(f"清理 {selector} 時(shí)可能出錯(cuò): {e}") # 記錄下日志或打印出來
# 清理后,假設(shè) body 里剩下的主要是正文
if soup_for_cleaning.body:
main_content_html = soup_for_cleaning.body
else:
# 如果連 body 都沒有,那就沒辦法了
main_content_html = soup_for_cleaning # 退而求其次
# 從選中的 HTML 塊中提取純文本
main_text = "未能有效提取內(nèi)容。"
if main_content_html:
# get_text 獲取所有文本,用換行符分隔,并去除首尾空白
raw_text = main_content_html.get_text(separator='\n', strip=True)
# 清理多余的空行
main_text = re.sub(r'\n\s*\n+', '\n', raw_text)
# --- 擴(kuò)展點(diǎn):對(duì)接外部智能處理服務(wù) ---
# 如果有條件,可以在這里把提取到的 main_text 發(fā)送給外部 API
# 比如:
# summary = call_external_summary_api(main_text)
# keywords = call_external_keyword_api(main_text)
# 目前,我們先用之前定義的簡單版本
summary = simple_summarizer(main_text) # 假設(shè)有這個(gè)函數(shù)
keywords = simple_keyword_extractor(main_text) # 假設(shè)有這個(gè)函數(shù)
result = {
'url': url,
# 只顯示部分內(nèi)容作為預(yù)覽,避免頁面過長
'content': main_text[:1000] + "... (預(yù)覽)" if len(main_text) > 1000 else main_text,
'summary': summary,
'keywords': keywords
}
except requests.exceptions.Timeout:
error = "請(qǐng)求超時(shí),目標(biāo)網(wǎng)站可能響應(yīng)慢或無法訪問。"
except requests.exceptions.RequestException as e:
error = f"請(qǐng)求網(wǎng)頁時(shí)出錯(cuò): {e}"
except Exception as e:
error = f"處理過程中發(fā)生錯(cuò)誤: {type(e).__name__} - {e}"
# 最好在后臺(tái)記錄詳細(xì)錯(cuò)誤日志
app.logger.error(f"處理 URL {url} 出錯(cuò): {e}", exc_info=True)
else:
error = "請(qǐng)輸入一個(gè)網(wǎng)址。"
# 把結(jié)果傳給 HTML 模板去顯示
return render_template('index.html', result=result, error=error)
# ... Flask 應(yīng)用的啟動(dòng)代碼 ...(其他相關(guān)文件如 index.html, requirements.txt, README.md 的內(nèi)容也需要相應(yīng)調(diào)整,這里重點(diǎn)展示了 app.py 的核心邏輯和思考過程)
探索更智能的處理:從提取到理解
上面這個(gè)代碼只是搭了個(gè)基礎(chǔ)架子,更有意思的地方在于,它可以作為一個(gè)起點(diǎn),去對(duì)接各種文本處理的 API 服務(wù):
- 更好的摘要: 不再是簡單取前幾句,而是調(diào)用專門的摘要服務(wù),傳入
main_text和要求(比如"請(qǐng)將這段文字總結(jié)為150字左右的核心觀點(diǎn)")。 - 更準(zhǔn)的關(guān)鍵詞: 同樣,調(diào)用關(guān)鍵詞提取服務(wù),讓它從
main_text中找出最相關(guān)的幾個(gè)詞。 - 還能做什么:
- 情感判斷:分析提取出來的評(píng)論是好評(píng)還是差評(píng)。
- 內(nèi)容分類:自動(dòng)判斷文章屬于哪個(gè)領(lǐng)域(科技、體育、娛樂等)。
- 內(nèi)容改寫/生成:基于提取的內(nèi)容,進(jìn)行二次創(chuàng)作。
- 信息問答:針對(duì)提取出來的長篇內(nèi)容,回答用戶提出的相關(guān)問題。
安全提醒:API 密鑰要放好
如果你調(diào)用的服務(wù)需要 API Key(訪問憑證),千萬別直接寫在代碼里。推薦用 .env 文件來管理:
- 安裝
python-dotenv庫:pip install python-dotenv - 在項(xiàng)目根目錄下創(chuàng)建一個(gè)名為
.env的文件,里面寫YOUR_API_KEY='你的密鑰' - 在 Python 代碼里加載:
import os from dotenv import load_dotenv load_dotenv() # 加載 .env 文件中的環(huán)境變量 api_key = os.getenv('YOUR_API_KEY') # 從環(huán)境變量讀取 # 然后用 api_key 去調(diào)用服務(wù)- 記得把
.env文件添加到.gitignore里,避免上傳到代碼倉庫。
運(yùn)行和測(cè)試:
按照 README.md 文件里的說明(如果還沒有,需要?jiǎng)?chuàng)建一個(gè)),在你的電腦上把這個(gè)小工具跑起來(通常是運(yùn)行 python app.py),然后在瀏覽器里打開 http://127.0.0.1:5000 (或者 Flask 啟動(dòng)時(shí)提示的地址),輸入一些不同的網(wǎng)頁鏈接試試看效果。
注意: 這個(gè)工具僅用于學(xué)習(xí),請(qǐng)勿用于非法用途。
本次代碼未傳倉庫,想要學(xué)習(xí),請(qǐng)?jiān)u論區(qū)交流
總結(jié):HTML解析是處理網(wǎng)頁數(shù)據(jù)的基礎(chǔ)
通過這篇文章,我們一起了解了 Python 里常用的 HTML 解析庫 BeautifulSoup 和 lxml,熟悉了 CSS 選擇器和 XPath 這兩種定位元素的方法,還動(dòng)手嘗試做了一個(gè)簡單的【網(wǎng)頁提取小工具】,并探討了結(jié)合外部智能服務(wù)提升功能的可能性。
接下來可以學(xué)點(diǎn)啥?
- 處理動(dòng)態(tài)加載的網(wǎng)頁: 很多網(wǎng)頁內(nèi)容是用 JavaScript 渲染出來的,只用
requests拿不到。可以了解下 Selenium、Playwright 這些可以模擬瀏覽器行為的工具。 - 更高級(jí)的選擇器技巧: 深入學(xué)習(xí) XPath 的軸(axis)、函數(shù),以及 CSS 的一些高級(jí)用法。
- 爬蟲框架: 如果需要做更復(fù)雜的爬蟲項(xiàng)目,可以了解下 Scrapy 這樣的專業(yè)框架。
- 反爬蟲和應(yīng)對(duì): 網(wǎng)站會(huì)有各種反爬措施,需要學(xué)習(xí)如何偽裝 User-Agent、使用代理 IP、處理 Cookie、識(shí)別驗(yàn)證碼等。
- 異步爬蟲: 為了提高爬取效率,可以研究下用
asyncio配合aiohttp來實(shí)現(xiàn)異步并發(fā)請(qǐng)求。
n 里常用的 HTML 解析庫 BeautifulSoup 和 lxml,熟悉了 CSS 選擇器和 XPath 這兩種定位元素的方法,還動(dòng)手嘗試做了一個(gè)簡單的【網(wǎng)頁提取小工具】,并探討了結(jié)合外部智能服務(wù)提升功能的可能性。
接下來可以學(xué)點(diǎn)啥?
- 處理動(dòng)態(tài)加載的網(wǎng)頁: 很多網(wǎng)頁內(nèi)容是用 JavaScript 渲染出來的,只用
requests拿不到??梢粤私庀?Selenium、Playwright 這些可以模擬瀏覽器行為的工具。 - 更高級(jí)的選擇器技巧: 深入學(xué)習(xí) XPath 的軸(axis)、函數(shù),以及 CSS 的一些高級(jí)用法。
- 爬蟲框架: 如果需要做更復(fù)雜的爬蟲項(xiàng)目,可以了解下 Scrapy 這樣的專業(yè)框架。
- 反爬蟲和應(yīng)對(duì): 網(wǎng)站會(huì)有各種反爬措施,需要學(xué)習(xí)如何偽裝 User-Agent、使用代理 IP、處理 Cookie、識(shí)別驗(yàn)證碼等。
- 異步爬蟲: 為了提高爬取效率,可以研究下用
asyncio配合aiohttp來實(shí)現(xiàn)異步并發(fā)請(qǐng)求。
到此這篇關(guān)于Python HTML解析:BeautifulSoup,Lxml,XPath使用教程的文章就介紹到這了,更多相關(guān)Python HTML解析:BeautifulSoup,Lxml,XPath內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實(shí)現(xiàn)整數(shù)的二進(jìn)制循環(huán)移位
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)整數(shù)的二進(jìn)制循環(huán)移位,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03
Python Sweetviz輕松實(shí)現(xiàn)探索性數(shù)據(jù)分析
Sweetviz是一個(gè)開放源代碼Python庫,可生成精美的高密度可視化文件,以單行代碼啟動(dòng)EDA(探索性數(shù)據(jù)分析)。輸出是一個(gè)完全獨(dú)立的HTML應(yīng)用程序,該系統(tǒng)圍繞快速可視化目標(biāo)值和比較數(shù)據(jù)集而構(gòu)建。其目標(biāo)是幫助快速分析目標(biāo)特征,訓(xùn)練與測(cè)試數(shù)據(jù)以及其他此類數(shù)據(jù)表征任務(wù)2021-11-11
Python實(shí)戰(zhàn)之利用Geopandas算出每個(gè)省面積
GeoPandas是一個(gè)基于pandas,針對(duì)地理數(shù)據(jù)做了特別支持的第三方模塊。本文將利用GeoPandas計(jì)算出每個(gè)省的面積,感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下吧2021-12-12
Gauss-Seidel迭代算法的Python實(shí)現(xiàn)詳解
這篇文章主要介紹了Gauss-Seidel迭代算法的Python實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-06-06
PyTorch計(jì)算損失函數(shù)對(duì)模型參數(shù)的Hessian矩陣示例
這篇文章主要為大家介紹了PyTorch計(jì)算損失函數(shù)對(duì)模型參數(shù)的Hessian矩陣的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
xshell會(huì)話批量遷移到mobaxterm的工具(python小工具)
這篇文章主要介紹了xshell會(huì)話批量遷移到mobaxterm的工具,使用方法也超級(jí)簡單,本文通過python代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12

