Python爬虫实战:用requests+json轻松抓取米游社原神COS图片(附完整源码)
Python爬虫实战:从API抓取到本地存储的完整技术解析
在当今数据驱动的时代,网络爬虫技术已成为开发者必备的基础技能之一。Python凭借其简洁的语法和丰富的第三方库,成为爬虫开发的首选语言。本文将以一个实际案例为切入点,系统讲解如何使用Python的requests库处理JSON API接口,并将获取的数据有效存储到本地文件系统。
1. 环境准备与基础概念
在开始编写爬虫代码前,我们需要确保开发环境配置正确,并理解几个核心概念。Python 3.6及以上版本都能良好支持本教程涉及的所有功能,建议使用最新稳定版以获得最佳性能和安全性。
必备工具安装:
- Python 3.6+
- requests库(
pip install requests) - 代码编辑器(VS Code/PyCharm等)
提示:建议使用虚拟环境管理项目依赖,避免包版本冲突。可通过
python -m venv venv创建虚拟环境。
API(Application Programming Interface)是现代网络应用数据交换的核心机制。与传统的HTML页面抓取相比,API接口通常返回结构化的JSON数据,具有以下优势:
| 特性 | API接口 | HTML页面 |
|---|---|---|
| 数据结构 | 结构化JSON | 非结构化HTML |
| 数据提取 | 直接解析 | 需要XPath/CSS选择器 |
| 请求效率 | 高效 | 相对较低 |
| 稳定性 | 较高 | 可能频繁变动 |
理解HTTP请求的基础知识对爬虫开发至关重要。GET请求是最常用的方法,用于从服务器获取资源。在接下来的实战中,我们将专门处理GET请求获取JSON数据。
2. 分析目标API接口
优秀的爬虫开发者首先是一名出色的"接口侦探"。我们需要学会分析目标网站的API调用方式,这是整个爬虫项目的关键第一步。
以社区类网站为例,大多数现代Web应用都采用前后端分离架构,通过API接口获取数据。使用浏览器开发者工具(F12打开)可以轻松监控网络请求:
- 打开浏览器开发者工具(Chrome右键"检查")
- 切换到"Network"选项卡
- 刷新页面并筛选XHR/fetch请求
- 分析返回JSON数据的请求URL
常见API特征:
- 包含"api"路径段
- 返回Content-Type为application/json
- 请求参数通常包含分页、过滤条件
- 使用GET/POST方法
对于需要身份验证的API,请求头中通常会包含:
- Authorization令牌
- Cookie信息
- 特定的App版本标识
# 示例:基本请求头配置
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
'Referer': 'https://example.com',
'Accept': 'application/json'
}
3. 使用requests获取和处理JSON数据
requests库是Python中最流行的HTTP客户端库,其API设计简洁直观。对于JSON API接口,我们主要使用其get()方法获取数据,并通过内置的json解析器处理响应。
完整的请求-响应处理流程:
- 构造请求URL和参数
- 设置适当的请求头
- 发送HTTP GET请求
- 检查响应状态码
- 解析JSON响应体
- 处理可能出现的异常
import requests
import json
def fetch_api_data(url, headers=None, params=None):
try:
response = requests.get(
url,
headers=headers,
params=params,
timeout=10
)
response.raise_for_status() # 检查HTTP错误
return response.json() # 解析JSON响应
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None
对于分页数据的处理,通常需要在循环中不断修改请求参数,直到获取所有数据。常见的分页参数形式包括:
- page/pageSize:
?page=1&pageSize=20 - offset/limit:
?offset=0&limit=20 - cursor:
?cursor=next_page_token
JSON数据处理技巧:
- 使用json.dumps()美化打印输出
- 使用jsonpath-ng库处理复杂JSON结构
- 注意处理可能的null值和类型转换
- 大型JSON考虑使用ijson流式解析
4. 文件存储与图片下载
获取数据后,我们需要将其持久化存储。对于文本类数据,可以直接写入文件;对于图片等二进制数据,则需要特殊处理。
文件存储最佳实践:
- 创建专用目录存储下载内容
- 处理文件名中的非法字符
- 避免重复下载相同内容
- 实现断点续传功能
- 添加适当的错误处理和日志
import os
from urllib.parse import quote
def ensure_directory_exists(path):
"""确保目录存在,不存在则创建"""
if not os.path.exists(path):
os.makedirs(path)
def sanitize_filename(filename):
"""清理文件名中的非法字符"""
keepchars = (' ', '.', '_', '-')
return "".join(
c for c in filename
if c.isalnum() or c in keepchars
).rstrip()
def download_image(url, save_path, headers=None):
"""下载并保存图片文件"""
try:
response = requests.get(url, headers=headers, stream=True)
response.raise_for_status()
with open(save_path, 'wb') as f:
for chunk in response.iter_content(1024):
f.write(chunk)
return True
except Exception as e:
print(f"下载失败 {url}: {e}")
return False
对于图片下载,有几个重要注意事项:
- 检查Content-Type确保是图片类型
- 使用stream=True分块下载大文件
- 处理可能的防盗链机制
- 尊重robots.txt和网站使用条款
5. 完整项目架构与优化
将上述各部分组合起来,我们可以构建一个结构良好的爬虫项目。良好的项目结构可以提高代码可维护性和复用性。
推荐的项目结构:
cosplay_crawler/
├── config.py # 配置文件
├── crawler.py # 核心爬虫类
├── storage.py # 存储处理
├── utils.py # 工具函数
└── main.py # 主程序入口
性能优化技巧:
- 使用会话(Session)复用TCP连接
- 实现并行下载提高效率
- 添加适当的延迟避免被封禁
- 缓存已处理的数据
- 实现增量抓取机制
import time
from concurrent.futures import ThreadPoolExecutor
class ImageCrawler:
def __init__(self, config):
self.session = requests.Session()
self.session.headers.update(config.HEADERS)
self.config = config
self.downloaded = set() # 已下载URL集合
def fetch_batch(self, page=1):
"""获取一批图片数据"""
params = {'page': page, 'size': self.config.PAGE_SIZE}
data = fetch_api_data(self.config.API_URL, self.session.headers, params)
return self.process_data(data)
def process_data(self, data):
"""处理原始数据,提取图片信息"""
if not data or 'items' not in data:
return []
return [
(item['title'], item['image_url'])
for item in data['items']
if item['image_url'] not in self.downloaded
]
def download_all(self, max_workers=4):
"""多线程下载所有图片"""
ensure_directory_exists(self.config.SAVE_PATH)
with ThreadPoolExecutor(max_workers=max_workers) as executor:
page = 1
while True:
items = self.fetch_batch(page)
if not items:
break
futures = []
for title, url in items:
filename = f"{sanitize_filename(title)}.{url.split('.')[-1]}"
save_path = os.path.join(self.config.SAVE_PATH, filename)
futures.append(executor.submit(
download_image,
url,
save_path,
self.session.headers
))
self.downloaded.add(url)
# 等待当前页下载完成
for future in futures:
future.result()
page += 1
time.sleep(self.config.REQUEST_INTERVAL)
6. 反爬策略与道德考量
在开发爬虫时,我们必须考虑目标网站的反爬机制,并遵守基本的网络道德规范。
常见反爬手段及应对:
- User-Agent检测:使用常见浏览器UA
- 请求频率限制:添加随机延迟
- IP封禁:使用代理IP池
- 验证码:使用识别服务或手动处理
- 行为分析:模拟人类操作模式
道德与法律注意事项:
- 遵守robots.txt协议
- 尊重版权和知识产权
- 不抓取个人隐私数据
- 控制请求频率不影响网站运营
- 查看网站的服务条款
注意:在实际项目中添加适当的日志记录和监控,可以帮助及时发现和解决问题。建议使用Python的logging模块实现分级日志记录。
import logging
from random import uniform
# 配置日志系统
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
class PoliteCrawler:
def __init__(self):
self.logger = logging.getLogger(__name__)
self.last_request_time = 0
def make_request(self, url):
"""礼貌的请求方法,包含随机延迟"""
elapsed = time.time() - self.last_request_time
if elapsed < 1.0: # 至少1秒间隔
delay = uniform(1.0, 3.0) - elapsed
if delay > 0:
time.sleep(delay)
try:
self.last_request_time = time.time()
response = requests.get(url)
response.raise_for_status()
return response
except Exception as e:
self.logger.error(f"请求失败: {url} - {str(e)}")
raise
在开发过程中,我经常遇到API接口变更导致爬虫失效的情况。为此,我通常会实现一个接口版本检测机制,当数据结构发生变化时能够及时发现并通知维护人员。另一个实用技巧是为爬虫添加邮件通知功能,当出现异常或完成任务时自动发送报告。
更多推荐

所有评论(0)