使用requests.get()批量爬取NHL数据遇401错误的优化方案咨询
问题
使用requests.get()爬取多个不同URL的NHL赛事数据,小样本测试时能正常获取数据(响应码200),但通过gameIds循环批量请求时,若列表过大最终会出现401响应码。希望在不降低运行效率的前提下解决该问题。
相关代码:
gameIds=['2022020022','2022020028','2022020044','2022020072','2022020088'] #can be much larger list #function to get data from web def get_data(gameId): url=f'https://api-web.nhle.com/v1' proxies={} verify='' headers={} response=requests.get(url + f'/gamecenter/{gameId}/play-by-play') if response.status_code == 200: data=response.json() return data else: print('status code <> 200, status code is ' + str(response.status_code)) all_data=[] #empty list to append each game's data for i in gameIds: data=get_data(i) all_data.append(data)
注:已在requests.get()中添加proxies、headers、verify参数,但因隐私无法公开。确认代码在小样本下可行,需排查401错误的原因及解决办法。
401错误原因分析
- 请求频率过高触发反爬:NHL的API对批量请求有速率限制,小样本请求量低未触发阈值,大列表循环快速请求会被服务器判定为恶意爬取,部分API会用401(未授权)替代429(请求过多)作为限流响应。
- 会话/身份验证信息失效:批量请求过程中,headers中的验证信息(如token、Cookie)过期或被服务器重置,导致后续请求失去权限。
- 代理IP被封禁:若使用代理,批量请求中单个IP的请求次数过多,被服务器识别并封禁,引发无权限响应。
高效解决办法(不降低运行效率)
1. 添加随机化请求延迟
避免固定间隔的机械请求,用0.5-1.5秒的随机等待平衡速率控制和运行效率,既规避限流,又不会过度拖慢任务。
2. 使用requests.Session保持会话
复用TCP连接和会话状态,减少重复建立连接的开销,同时维持验证信息的有效性,降低被服务器识别为异常请求的概率。
3. 代理池轮换(若使用代理)
准备多个可用代理IP,每次请求随机切换代理,避免单个IP因请求量过大被封禁,保证批量请求的连续性。
4. 内置重试机制
遇到401错误时自动重试,同时刷新会话或切换代理,避免临时失效导致整个任务中断。
优化后的示例代码:
import requests import random import time gameIds=['2022020022','2022020028','2022020044','2022020072','2022020088'] # 初始化会话,复用连接和验证信息 session = requests.Session() session.proxies = {} # 替换为你的代理配置 session.headers = {} # 替换为你的headers信息 session.verify = '' # 替换为你的verify设置 def get_data(gameId): url = f'https://api-web.nhle.com/v1/gamecenter/{gameId}/play-by-play' retry_count = 2 # 设置重试次数 for _ in range(retry_count): try: response = session.get(url) if response.status_code == 200: return response.json() elif response.status_code == 401: # 401时刷新会话,重新设置验证信息 global session session = requests.Session() session.proxies = {} # 重新配置代理 session.headers = {} # 重新配置headers session.verify = '' print(f"请求{gameId}出现401,正在重试...") continue else: print(f"请求{gameId}失败,状态码:{response.status_code}") return None except Exception as e: print(f"请求{gameId}发生异常:{str(e)}") return None print(f"请求{gameId}重试{retry_count}次仍失败") return None all_data = [] for game_id in gameIds: data = get_data(game_id) all_data.append(data) # 添加随机延迟,避免触发限流 time.sleep(random.uniform(0.5, 1.5))
内容的提问来源于stack exchange,提问作者n_cen
相关产品推荐
相关产品推荐

