使用Python和FastAPI构建网站爬虫:Oncolo医疗文章抓取实战
使用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规则和使用条款
- 控制爬取频率,不对目标网站造成过大负担
- 尊重版权,不将抓取的内容用于商业用途(除非得到授权)
- 保护个人隐私数据,不抓取和存储敏感信息
只有合法、合理地使用爬虫技术,才能发挥其真正的价值,并避免不必要的法律风险。