Python多线程批量循环请求提速:DataFrame内600条URL请求过慢如何优化?
问题根因
- 未做线程同步:你将线程设置为守护线程(
daemon=True),且主线程启动所有子线程后没有等待执行完成就直接结束,未跑完的子线程被强制销毁,仅少量先完成的线程能写入结果。 - 共用非线程安全对象:
requests.Session不是线程安全的,多线程共用同一个session实例会引发请求混乱、响应异常,大量请求本身就执行失败。 - 线程创建无限制:一次性启动600多个线程会带来极高的线程切换开销,同时极易触发目标站点的反爬策略,进一步提升请求失败率。
- 无异常捕获逻辑:单条请求出现网络超时、页面结构变动、目标元素不存在等问题时,会直接导致对应线程崩溃,无法写入结果。
- 结果顺序错位:多线程执行顺序不固定,直接向列表追加结果会导致最终
owner列表的顺序和原DataFrame的Link列顺序不匹配,即使拿到结果也会出现数据对应错误。
优化实现方案
推荐使用concurrent.futures.ThreadPoolExecutor进行线程池管理,自动管控线程数量、同步执行结果,同时保证结果和原数据的对应关系,优化后代码如下:
import requests from bs4 import BeautifulSoup from tqdm import tqdm from concurrent.futures import ThreadPoolExecutor, as_completed import pandas as pd # 单链接爬取逻辑,单独创建session保证线程安全 def fetch_owner(link, cookies, timeout=10): try: # 每个线程单独创建session,避免共用非线程安全对象 s = requests.Session() resp = s.get(link, cookies=cookies, timeout=timeout) resp.raise_for_status() # 主动抛出HTTP错误 soup = BeautifulSoup(resp.content, 'lxml') owner_elem = soup.find('input', {'id': 'GlobalBodyContent_InternalBodyContent_BodyContent_Owner'}) return owner_elem.get('value') if owner_elem else None except Exception as e: # 异常可以自行打印记录,返回空值避免线程崩溃 # print(f"链接{link}爬取失败:{str(e)}") return None def main(df, cookies, max_workers=15): # 用字典存储 链接-结果 对应关系,避免顺序错位 link_to_owner = {} # 初始化线程池 with ThreadPoolExecutor(max_workers=max_workers) as executor: # 提交所有任务,生成 未来对象-链接 的映射 future_to_link = {executor.submit(fetch_owner, link, cookies): link for link in df['Link']} # 带进度条遍历执行完成的任务 for future in tqdm(as_completed(future_to_link), total=len(future_to_link)): link = future_to_link[future] link_to_owner[link] = future.result() # 按原DataFrame的Link顺序生成owner列表 df['Owner'] = df['Link'].map(link_to_owner) return df # 调用执行 cookies = {} # 替换为你自己的cookies df = main(df, cookies) print(df)
额外优化建议
- 调整
max_workers参数:根据目标站点的反爬严格程度调整,一般设置为10-30即可,数值过大容易触发反爬导致请求失败。 - 增加重试机制:可以对网络超时、5xx类服务端错误增加重试逻辑,进一步提升爬取成功率。
- 控制请求频率:如果反爬严格,可以在单线程爬取逻辑中增加随机延时,避免请求过于集中。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

