Python中如何加速TMDB API分页Get请求以获取剧集ID?
如何高效获取TMDB 2017年12月发布的所有剧集ID
嘿,我完全懂你现在的烦恼——3600多次串行请求确实慢到让人崩溃。先给你明确结论:TMDB的Discover API本身没有直接返回所有结果的批量接口,所以完全不用循环是做不到的,但我们可以通过并发请求来大幅缩短耗时,这是目前最可行的优化方案。下面给你具体的优化思路和代码示例:
核心优化思路:并发请求替代串行循环
TMDB的API速率限制是每10秒最多40个请求,所以只要控制并发数在合理范围内(比如一次同时发4-8个请求),就不会触发限流,同时能把耗时从串行的几十分钟压缩到几分钟。
方案1:使用线程池并发(适合不熟悉异步的开发者)
可以用requests-futures库,它基于requests和线程池实现,代码改动很小:
首先安装依赖:
pip install requests-futures
然后修改代码:
import requests from requests_futures.sessions import FuturesSession # 配置API参数 API_KEY = "你的API密钥" BASE_TV_URL = "https://api.themoviedb.org/3/discover/tv" params = { 'release_date.gte': '2017-12-01', 'release_date.lte': '2017-12-31', 'api_key': API_KEY, } # 初始化会话,设置线程池大小(控制并发数,建议4-8,避免触发限流) session = FuturesSession(max_workers=6) # 先获取总页数 response = requests.get(BASE_TV_URL, params=params) response.raise_for_status() # 处理请求错误 total_pages = response.json()['total_pages'] print(f"总页数:{total_pages}") # 批量提交所有页面的请求 futures = [] for page in range(1, total_pages + 1): page_params = params.copy() page_params['page'] = page future = session.get(BASE_TV_URL, params=page_params) futures.append(future) # 收集所有结果 tv_ids = set() for future in futures: response = future.result() if response.ok: data = response.json() # 直接提取id,不用遍历所有键值对 for item in data['results']: if item.get('id'): tv_ids.add(item['id']) print(f"共获取到{len(tv_ids)}个剧集ID")
方案2:使用异步请求(性能更优)
如果追求更高效率,可以用aiohttp实现异步请求,这在处理大量请求时比线程池更高效:
首先安装依赖:
pip install aiohttp
代码示例:
import aiohttp import asyncio API_KEY = "你的API密钥" BASE_TV_URL = "https://api.themoviedb.org/3/discover/tv" params = { 'release_date.gte': '2017-12-01', 'release_date.lte': '2017-12-31', 'api_key': API_KEY, } async def fetch_page(session, page): """异步获取单页数据并提取ID""" page_params = params.copy() page_params['page'] = page async with session.get(BASE_TV_URL, params=page_params) as response: if response.status == 200: data = await response.json() return {item['id'] for item in data['results'] if item.get('id')} return set() async def main(): # 先获取总页数 async with aiohttp.ClientSession() as session: async with session.get(BASE_TV_URL, params=params) as response: response.raise_for_status() total_pages = (await response.json())['total_pages'] print(f"总页数:{total_pages}") # 分批处理请求(避免一次性创建太多任务,控制并发数) batch_size = 8 tv_ids = set() for i in range(0, total_pages, batch_size): batch_pages = range(i+1, min(i+batch_size+1, total_pages+1)) tasks = [fetch_page(session, page) for page in batch_pages] batch_results = await asyncio.gather(*tasks) for result in batch_results: tv_ids.update(result) print(f"共获取到{len(tv_ids)}个剧集ID") if __name__ == "__main__": asyncio.run(main())
额外的代码优化细节
除了并发,你原来的代码还有几个可以优化的点:
- 使用
response.json()替代手动json.loads(myResponseTv.content.decode('utf-8')),更简洁高效; - 直接通过
item['id']提取ID,不用遍历所有键值对,节省时间; - 使用
requests.Session或aiohttp.ClientSession复用HTTP连接,减少TCP握手的开销; - 用
params字典构建请求参数,避免字符串拼接导致的错误(比如URL中的&符号问题)。
注意事项
- 严格遵守TMDB的API速率限制:如果并发数太高,可能会收到429错误(限流),此时可以降低并发数,或者添加重试机制;
- 可以考虑添加错误重试逻辑,比如请求失败时自动重试几次,避免因网络波动导致数据缺失;
- 如果剧集数量很大,建议把ID分批保存到文件或数据库,避免内存占用过高。
内容的提问来源于stack exchange,提问作者Dookoto_Sea
相关产品推荐
相关产品推荐

