使用Python和FastAPI構建網站爬蟲:Oncolo醫療文章抓取實戰
- 前言
- 項目概述
- 技術棧
- 代碼分析
- 1. 導入必要的庫
- 2. 初始化FastAPI應用
- 3. 定義請求模型
- 4. 核心爬蟲功能
- 4.1 URL驗證和準備
- 4.2 設置HTTP請求
- 4.3 發送請求和解析HTML
- 4.4 提取文章內容
- 4.5 保存結果和返回數據
- 5. API端點定義
- 6. 啟動服務器
- 爬蟲技術要點分析
- 1. 請求頭和會話管理
- 2. 重試機制
- 3. HTML解析技巧
- 4. 遞歸內容提取
- 5. 錯誤處理和異常管理
- 爬蟲開發的最佳實踐
- 改進建議
- 總結
- 注意事項
前言
在數據分析和信息收集的時代,網絡爬蟲技術已成為獲取互聯網數據的重要手段。本文將通過分析一個實際的爬蟲項目,幫助大家了解如何使用Python構建一個功能完善的網站爬蟲API,特別是針對醫療類網站的內容抓取。
項目概述
這個項目是一個基于FastAPI的Web服務,專門用于抓取日本醫療網站Oncolo的文章內容。該API可以接收文章URL,然后抓取并解析文章的各個部分,包括標題、副標題、發布日期、正文內容、標簽和作者信息等,最后將結果保存為文本文件并返回JSON格式的響應。
技術棧
- Python:編程語言
- FastAPI:構建API服務
- BeautifulSoup:HTML解析
- Requests:HTTP請求處理
- Pydantic:數據驗證
代碼分析
1. 導入必要的庫
import requests
from bs4 import BeautifulSoup
import os
import time
import re
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optionalfrom bs4.element import NavigableString, Tag
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from requests.packages.urllib3.exceptions import InsecureRequestWarning
這部分導入了項目所需的所有庫,包括HTTP請求、HTML解析、文件操作和API構建等功能模塊。
2. 初始化FastAPI應用
# 安全でないリクエストの警告を無効化
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)app = FastAPI(title="Oncoloの記事スクレイピングAPI", description="Oncoloウェブサイトの記事內容をスクレイピングするAPI")
這里初始化了FastAPI應用,并禁用了不安全請求的警告(因為代碼中使用了verify=False
選項)。
3. 定義請求模型
class ScrapeRequest(BaseModel):url: str
使用Pydantic定義了一個簡單的請求模型,只包含一個URL字段。
4. 核心爬蟲功能
scrape_oncolo_article
函數是整個項目的核心,它完成了以下任務:
4.1 URL驗證和準備
def scrape_oncolo_article(url: str):# URL驗證if not url.startswith('https://oncolo.jp/news/'):raise ValueError("URLはoncolo.jp/news/形式である必要があります")# 從URL提取文件名file_id = url.split('/news/')[1]output_filename = f"{file_id}.txt"
這部分代碼驗證URL是否符合要求的格式,并從URL中提取文章ID作為保存文件的名稱。
4.2 設置HTTP請求
# HTTP請求頭設置headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8','Connection': 'keep-alive','Upgrade-Insecure-Requests': '1',}# 創建會話對象session = requests.Session()# 設置重試策略retry_strategy = Retry(total=5,backoff_factor=1,status_forcelist=[429, 500, 502, 503, 504],)adapter = HTTPAdapter(max_retries=retry_strategy)session.mount("https://", adapter)session.mount("http://", adapter)
這部分代碼設置了HTTP請求頭,創建了一個會話對象,并配置了重試策略。這是爬蟲開發中的重要步驟,因為:
- 自定義User-Agent可以模擬瀏覽器行為,避免被網站識別為爬蟲
- 設置重試策略可以處理臨時網絡問題和服務器錯誤
- 使用會話對象可以在多次請求之間保持Cookie等狀態
4.3 發送請求和解析HTML
try:# 禁用SSL驗證response = session.get(url, headers=headers, verify=False, timeout=30)response.raise_for_status() # 如果請求失敗則拋出異常# 使用BeautifulSoup解析HTMLsoup = BeautifulSoup(response.text, 'html.parser')
這部分代碼發送HTTP請求并使用BeautifulSoup解析返回的HTML內容。注意這里禁用了SSL驗證(verify=False
),這在開發階段可能有用,但在生產環境中應該避免。
4.4 提取文章內容
代碼使用BeautifulSoup的選擇器功能,根據網站的HTML結構提取各種信息:
- 主標題和副標題
- 發布日期和更新日期
- 文章正文內容
- 標簽信息
- 作者信息和簡介
例如,提取標題的代碼:
# 提取標題和副標題
h1_element = main_section.find('h1')
main_title = ""
subtitle = ""if h1_element:subtitle_element = h1_element.find('span', class_='subtitle')if subtitle_element:subtitle = subtitle_element.get_text(strip=True)# 創建h1的副本以避免修改原始souptitle_copy = BeautifulSoup(str(h1_element), 'html.parser').h1subtitle_in_copy = title_copy.find('span', class_='subtitle')if subtitle_in_copy:subtitle_in_copy.decompose() # 從副本中刪除副標題元素main_title = title_copy.get_text(strip=True)else:main_title = h1_element.get_text(strip=True)
4.5 保存結果和返回數據
# 保存為純文本文件
output_path = os.path.join('scraped_data', output_filename)
with open(output_path, 'w', encoding='utf-8') as f:f.write(f"メインタイトル: {main_title}\n")f.write(f"サブタイトル: {subtitle}\n")# ...其他內容...# 返回包含所有內容的響應
return {"success": True,"message": f"記事は {output_path} に保存されました","filename": output_filename,"title": main_title,# ...其他字段...
}
這部分代碼將提取的內容保存到文本文件中,并返回一個包含所有數據的JSON響應。
5. API端點定義
@app.post("/scrape/", summary="Oncolo記事のスクレイピング", description="oncolo.jpの記事URLを提供し、コンテンツをスクレイピングして保存します")
async def scrape_article(request: ScrapeRequest):try:result = scrape_oncolo_article(request.url)return resultexcept ValueError as e:raise HTTPException(status_code=400, detail=str(e))except HTTPException as e:raise eexcept Exception as e:raise HTTPException(status_code=500, detail=f"サーバーエラー: {str(e)}")@app.get("/", summary="APIルートパス", description="APIの基本情報を返します")
async def root():return {"message": "Oncolo記事スクレイピングAPIへようこそ", "usage": "/scrape/エンドポイントにPOSTリクエストを送信し、oncolo.jpの記事URLを提供してください"}
這部分代碼定義了兩個API端點:
/scrape/
:接收POST請求,包含要抓取的URL/
:根路徑,返回API的基本信息
6. 啟動服務器
if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)
這部分代碼使用uvicorn啟動FastAPI服務器,監聽所有網絡接口的8000端口。
爬蟲技術要點分析
1. 請求頭和會話管理
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',# ...其他頭信息...
}
session = requests.Session()
要點:
- 設置User-Agent模擬真實瀏覽器,避免被網站攔截
- 使用Session對象維護會話狀態,提高效率并保持登錄狀態
2. 重試機制
retry_strategy = Retry(total=5,backoff_factor=1,status_forcelist=[429, 500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
要點:
- 實現自動重試機制,處理臨時網絡問題
- 使用指數退避策略(backoff_factor)避免頻繁請求
- 針對特定HTTP錯誤碼(如429太多請求、500服務器錯誤等)進行重試
3. HTML解析技巧
soup = BeautifulSoup(response.text, 'html.parser')
main_section = soup.find('section', class_='main')
要點:
- 使用BeautifulSoup進行HTML解析,比正則表達式更可靠
- 根據HTML結構和CSS選擇器定位元素
- 處理復雜的嵌套結構和不同類型的內容
4. 遞歸內容提取
for content in content_div.contents:if isinstance(content, NavigableString):# 處理文本節點elif isinstance(content, Tag):# 處理標簽節點# 遞歸處理子節點
要點:
- 區分處理文本節點和標簽節點
- 遞歸遍歷復雜的DOM結構
- 保持原始格式和鏈接信息
5. 錯誤處理和異常管理
try:# 爬蟲代碼
except requests.exceptions.RequestException as e:raise HTTPException(status_code=500, detail=f"リクエストエラー: {str(e)}")
except Exception as e:raise HTTPException(status_code=500, detail=f"処理エラー: {str(e)}")
要點:
- 區分不同類型的異常(請求錯誤、解析錯誤等)
- 提供有意義的錯誤信息
- 將內部錯誤轉換為適當的HTTP狀態碼
爬蟲開發的最佳實踐
-
尊重robots.txt:雖然本例中沒有顯示,但在實際開發中應該檢查目標網站的robots.txt文件,遵守其訪問規則。
-
控制請求頻率:使用
time.sleep()
或更復雜的限速機制,避免對目標服務器造成過大負擔。 -
異常處理:全面的異常處理確保爬蟲能夠穩定運行,即使遇到意外情況。
-
模塊化設計:將爬蟲功能拆分為多個模塊,如請求發送、內容解析、數據存儲等,便于維護和擴展。
-
數據驗證:使用Pydantic等工具驗證輸入和輸出數據,確保數據的完整性和一致性。
改進建議
-
添加請求延遲:在代碼中添加
time.sleep()
,控制請求頻率,避免被目標網站封禁。 -
啟用SSL驗證:在生產環境中應啟用SSL驗證(刪除
verify=False
),提高安全性。 -
使用代理池:對于大規模爬蟲,可以實現代理IP輪換機制,避免IP被封。
-
添加日志系統:記錄爬蟲運行狀態和錯誤信息,便于調試和監控。
-
實現增量爬取:檢查已爬取的內容,只抓取新內容,提高效率。
總結
這個項目展示了如何使用Python構建一個功能完善的網站爬蟲API,涵蓋了HTTP請求、HTML解析、內容提取、數據存儲和API服務等多個方面。通過分析這個實例,我們可以學習到網絡爬蟲開發的核心技術和最佳實踐。
爬蟲開發是一個需要不斷學習和適應的過程,因為網站結構經常變化,反爬蟲技術也在不斷升級。希望這篇文章能夠幫助你理解爬蟲開發的基本原理和技術要點,為你的爬蟲項目提供參考。
注意事項
在使用爬蟲技術時,請務必遵守以下原則:
- 遵守網站的robots.txt規則和使用條款
- 控制爬取頻率,不對目標網站造成過大負擔
- 尊重版權,不將抓取的內容用于商業用途(除非得到授權)
- 保護個人隱私數據,不抓取和存儲敏感信息
只有合法、合理地使用爬蟲技術,才能發揮其真正的價值,并避免不必要的法律風險。