如果你已經看到這堂課,說明你對於網頁抓取原理以及如何使用優秀的 BeautifulSoup
從 HTML 中提取想要的數據已經有不錯的了解了。不過,在網頁抓取的世界中,可不是所有事情都像教材裡寫的那麼順利。有時候,想要收集資料的夢想可能會變成和錯誤奮鬥的噩夢。所以,讓我們來聊聊如何避開這些暗礁,讓我們的抓取工具盡可能穩健。
1. 網頁抓取中的常見錯誤
404 錯誤和其他 HTTP 錯誤
經典問題:你嘗試獲取某個頁面,但回應你的是自豪的 "404 Not Found"。這可能是因為該頁面已經刪除或者移動了。其他常見的 HTTP 錯誤還包括 403(Forbidden)、500(伺服器內部錯誤)和 503(服務不可用)。
HTML 結構的變化
你花了很多時間寫程式碼來提取數據,但隔天網站就稍微“打扮了一下”,更改了 HTML 結構。哎,又得重寫一遍了!
請求次數的限制
一些網站對大量的網頁抓取行為開始懷疑。有時候,最好的情況是暫時封鎖你,最壞的情況是永久封鎖。
等待時間和超時
有時候頁面載入得很慢,如果等待時間超出了預設的超時時間,腳本可能會崩潰。
2. 錯誤處理的方法
使用 try-except
你的腳本不應該因為一點小失誤就崩潰。加入 try-except
區塊可以捕捉錯誤,讓你的網頁抓取工具即使遇到問題也能繼續運作。
import requests
from bs4 import BeautifulSoup
url = 'http://example.com/some-nonexistent-page'
try:
response = requests.get(url)
response.raise_for_status() # 引發 HTTPError 對壞回應
except requests.exceptions.HTTPError as errh:
print("HTTP 錯誤:", errh)
except requests.exceptions.ConnectionError as errc:
print("連接錯誤:", errc)
except requests.exceptions.Timeout as errt:
print("超時錯誤:", errt)
except requests.exceptions.RequestException as err:
print("哦哦某些其他問題", err)
好的腳本不僅會捕獲異常,還會對每種錯誤類型有應對措施。被 IP 封鎖了 - 換個 proxy 繼續;網站掛掉了 - 你可以先抓取其他的。如果頁面中找不到某些應有的元素,可以通知網頁抓取工具的擁有者更新抓取腳本並發送 email。
日誌記錄
「為什麼要記錄日誌呢?」你可能會問。因為日誌是你的第二雙眼睛。它們可以幫助你快速找出問題並加以解決。
import logging
logging.basicConfig(filename='scraper.log', level=logging.INFO)
try:
# 你的網頁抓取程式
pass
except Exception as e:
logging.error("發生了例外情況", exc_info=True)
使用超時與重試
有時候,只需要等一下再試試看。這時可以用超時和重試來幫助。
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
except requests.exceptions.Timeout:
print("超時錯誤發生了,重試中...")
# 重試請求或執行其他操作
3. 穩健抓取的案例
帶有錯誤處理的簡單抓取工具
我們來寫一個小型但穩定的抓取工具,能處理一些常見的錯誤。
import requests
from bs4 import BeautifulSoup
import time
import logging
logging.basicConfig(filename='scraper.log', level=logging.INFO)
url = 'http://example.com/some-nonexistent-page'
def fetch_html(url):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.text
except requests.exceptions.HTTPError as errh:
logging.error("HTTP 錯誤: %s", errh)
except requests.exceptions.ConnectionError as errc:
logging.error("連接錯誤: %s", errc)
except requests.exceptions.Timeout as errt:
logging.warning("超時錯誤發生了,正在重試...")
time.sleep(1) # 等一等再試
return fetch_html(url)
except requests.exceptions.RequestException as err:
logging.error("哦哦某些其他問題 %s", err)
return None
html_content = fetch_html(url)
if html_content:
soup = BeautifulSoup(html_content, 'html.parser')
# 你的數據提取程式碼
else:
print("未能取得頁面內容。")
部分保存數據
為了避免在意外發生時損失已經抓取到的數據,可以部分保存。例如,如果你從頁面列表中提取資料,邊提取邊保存結果,可以最大程度減少數據丟失的風險。
import csv
def save_to_csv(data, filename='data.csv'):
with open(filename, mode='a', newline='') as file:
writer = csv.writer(file)
writer.writerow(data)
因此,即使你的腳本在中途崩潰了,你也不會損失所有數據,並且可以從最後保存的狀態繼續。
你正在抓取的頁面可能會局部變化,大部分數據仍然可用。如果僅有小部分數據缺失,那麼放棄抓取並丟失寶貴的數據就不太划算了。
GO TO FULL VERSION