1. 为什么选择Python爬虫获取米游社原神资源
作为一个玩了三年原神的老玩家,我经常在米游社看到各种精美的角色同人图和官方壁纸。每次手动保存实在太麻烦,直到我发现了Python爬虫这个神器。用requests库抓取米游社API数据,不仅能批量下载高清图片,还能自动分类整理,简直是技术宅的二次元宝藏!
相比手动操作,爬虫有三大优势:一是效率高,几分钟就能获取上百张图片;二是可以定时自动更新,不错过任何新发布的优质内容;三是能实现智能分类,比如按角色、画师或发布时间整理。我实测下来,用Python脚本每天自动收集新图,半年就建起了超过2万张的原神图库。
2. 准备工作:环境配置与工具选择
2.1 基础环境搭建
首先需要安装Python环境,推荐使用3.8以上版本。我习惯用conda创建独立环境:
conda create -n genshin python=3.8 conda activate genshin核心依赖库就三个:requests处理网络请求、json解析API数据、os和pathlib管理文件路径。安装命令很简单:
pip install requests2.2 开发者工具的使用技巧
按F12打开浏览器开发者工具,切换到Network标签页。在米游社随便点开一张角色图,就能在XHR请求里找到关键的API接口。我常用的筛选技巧是:
- 过滤XHR类型请求
- 搜索关键词"get"或"list"
- 查看返回JSON数据的请求
找到目标API后,右键复制为cURL命令,再通过在线工具转成Python代码,能省去很多手动构造请求的时间。
3. 解析米游社API数据结构
3.1 关键接口分析
通过反复测试,我发现米游社主要使用两种API格式:
- 分页列表接口:通常包含"list"、"page"等字段
- 详情接口:包含高清图url和元数据
典型的响应结构如下:
{ "data": { "list": [ { "id": "123", "title": "甘雨同人图", "images": ["https://xxx.jpg"], "character": ["甘雨"], "author": "某画师" } ], "total": 100 } }3.2 动态加载逻辑破解
米游社采用滚动加载方式,通过page和size参数控制分页。我通过修改参数测试出最大允许的size值是50,超过会返回错误。所以批量获取时需要循环请求,直到list为空为止。
处理分页的代码模板:
page = 1 while True: params = {"page": page, "size": 50} response = requests.get(url, params=params) data = response.json() if not data["data"]["list"]: break # 处理当前页数据 page += 14. 图片下载与分类存储实战
4.1 高效下载方案
直接使用requests的流式下载可以避免内存溢出问题,特别是处理高清大图时。我的下载函数通常会添加重试机制:
def download_image(url, save_path): for i in range(3): # 最多重试3次 try: with requests.get(url, stream=True) as r: r.raise_for_status() with open(save_path, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) return True except Exception as e: print(f"第{i+1}次下载失败: {e}") return False4.2 智能分类系统
我设计了一套基于元数据的自动分类规则:
- 按角色创建文件夹(从API提取character字段)
- 按画师分子目录(author字段)
- 按月份归档(从create_time解析)
核心分类代码:
def organize_image(data): char_dir = Path("images") / data["character"][0] author_dir = char_dir / data["author"] author_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.fromtimestamp(data["create_time"]) month_dir = author_dir / f"{timestamp.year}-{timestamp.month:02d}" month_dir.mkdir(exist_ok=True) return month_dir / f"{data['id']}.jpg"5. 高级技巧与优化方案
5.1 增量爬取实现
为了避免重复下载,我会记录已爬取的id到sqlite数据库。每次启动时先查询已有id,只请求新内容:
import sqlite3 def get_existing_ids(): conn = sqlite3.connect('genshin.db') c = conn.cursor() c.execute('SELECT id FROM images') return {row[0] for row in c.fetchall()}5.2 反爬虫策略应对
米游社会对频繁请求进行限制,我的解决方案是:
- 随机延迟1-3秒 between请求
- 轮换User-Agent
- 使用代理IP池(需合规使用)
from time import sleep import random user_agents = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" ] def safe_request(url): headers = {"User-Agent": random.choice(user_agents)} sleep(random.uniform(1, 3)) return requests.get(url, headers=headers)6. 完整代码示例与使用说明
下面是我一直在用的增强版脚本框架,包含所有上述功能:
import requests import json from pathlib import Path import sqlite3 from datetime import datetime import random from time import sleep class GenshinSpider: def __init__(self): self.base_url = "https://api.miyoushe.com/xxx" self.db_conn = sqlite3.connect('genshin.db') self._init_db() def _init_db(self): c = self.db_conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS images (id TEXT PRIMARY KEY, title TEXT, character TEXT)''') self.db_conn.commit() def get_existing_ids(self): c = self.db_conn.cursor() c.execute('SELECT id FROM images') return {row[0] for row in c.fetchall()} def crawl(self, max_page=10): existing_ids = self.get_existing_ids() page = 1 while page <= max_page: data = self._fetch_page(page) if not data["list"]: break for item in data["list"]: if item["id"] not in existing_ids: self._process_item(item) page += 1 def _fetch_page(self, page): # 实现分页请求逻辑 pass def _process_item(self, item): # 实现图片下载和分类 pass if __name__ == "__main__": spider = GenshinSpider() spider.crawl(max_page=20)使用前需要替换base_url为实际API地址,建议先用浏览器开发者工具获取最新的接口信息。运行后会自动创建genshin.db数据库记录爬取状态,图片按角色/画师/月份分类存储在images目录下。