Python循环爬取JSON拼接pandas DataFrame效率低如何优化?
代码运行慢的核心原因
你的代码耗时久主要来自两个完全可以避免的性能瓶颈:
- 串行执行网络请求:1000个接口请求按顺序逐个发起,每发一个请求就要等服务器响应完成才会发下一个,90%以上的运行时间都耗在网络等待上,CPU资源几乎闲置
- 循环内反复拼接DataFrame:
pd.concat每次执行都会全量复制已有的全部DataFrame数据,1000次循环的时间复杂度达到O(n²),数据量越大后续拼接的开销越高
优化方案
1. 用线程池把串行请求改成并发请求
网络IO密集型场景下,多线程的优化收益最明显,不需要改动核心爬取逻辑,只要把请求任务丢进线程池并行执行就行。注意控制并发数,不要太高避免触发接口限流或者被封IP,一般设置10-20的并发数就可以把总请求时间压缩到原来的1/10甚至更短。
2. 取消循环内的concat操作,最后一次性生成DataFrame
不要每爬一页就拼接一次DataFrame,先把所有爬取到的解析结果存在普通Python列表里,等所有页面爬完之后,用收集好的全量数据一次性生成最终的DataFrame,彻底避免反复复制数据的额外开销。
优化后的完整代码
import requests import pandas as pd from concurrent.futures import ThreadPoolExecutor, as_completed page_total = 1000 # 并发数根据目标接口的承受能力调整,建议不超过30 CONCURRENT_NUM = 15 def crawl_page(page_num): url = f"https://api.XXXXXX.XXX/XXX{page_num}XXX" # 加超时避免个别请求卡住整个任务 resp = sess.get(url, timeout=10) resp.raise_for_status() return resp.json() if __name__ == "__main__": all_records = [] # 初始化requests Session复用TCP连接,进一步减少连接开销 sess = requests.Session() with ThreadPoolExecutor(max_workers=CONCURRENT_NUM) as executor: task_map = {executor.submit(crawl_page, i): i for i in range(1, page_total+1)} for task in as_completed(task_map): current_page = task_map[task] try: page_json = task.result() # 单页数据解析后转成字典行追加到总列表 page_records = pd.json_normalize(page_json).to_dict("records") all_records.extend(page_records) except Exception as e: print(f"第{current_page}页爬取出错:{str(e)}") # 全量数据收集完成后一次性生成DataFrame final_df = pd.DataFrame(all_records)
额外优化提示
- 如果接口返回的JSON结构固定,不需要嵌套解析,可以直接手动提取字段存入列表,比调用
pd.json_normalize速度更快 - 如果爬取过程中需要记录断点,可以每爬100页就把当前的all_records存成本地json文件,避免程序中途崩溃要从头重爬
- 如果目标接口有严格的限流规则,可以在
crawl_page函数里加少量sleep,配合并发数调整到不会触发限流的阈值即可
内容的提问来源于stack exchange,提问作者pythong
相关产品推荐
相关产品推荐

