Python代码并行化遇Pickle错误及执行冻结问题求助
解决Joblib并行爬虫的Pickle错误与冻结问题
第一个Pickle错误的原因
你遇到的AttributeError: Can't pickle local object 'delayed.<locals>.delayed_function'问题,核心原因是不能在delayed()里直接操作主进程的局部列表m:
- 多进程模式下,每个子进程有独立的内存空间,主进程的
m无法被子进程直接修改,即便能序列化,子进程的append操作也不会同步到主进程的列表。 delayed()需要序列化你传入的操作,而m.append(...)属于绑定到局部变量m的操作,Pickle无法序列化这种带局部上下文的对象。
重构后冻结的排查与解决
你把操作封装到process()函数后解决了Pickle问题,但出现无限冻结,大概率是这几个原因:
- Input数据处理不严谨:CSV读取的每一行是列表(比如
["https://example.com"]),你用str(i)转成字符串后再正则去除符号,容易出现意外格式错误,导致scrape_code处理失败甚至阻塞。 - Scrape函数无超时机制:如果
scrape_code里的网络请求没有设置超时,某个卡住的请求会导致整个并行任务挂起。 - 多进程模式不适合IO密集型任务:爬虫属于IO密集型(大部分时间在等待网络响应),用多进程反而会因为进程间序列化开销大拖慢速度,甚至导致异常。
修正后的完整代码
import csv from joblib import Parallel, delayed import multiprocessing def scrape_code(url): # 这里要确保你的scrape函数有超时机制,比如用requests的话: # import requests # try: # response = requests.get(url, timeout=10) # # 处理响应... # return processed_data # except Exception as e: # print(f"爬取{url}失败: {e}") # return None pass # 替换成你的实际爬取逻辑 def process(url): # 直接返回爬取结果,不需要在函数内新建列表 return scrape_code(url) if __name__ == "__main__": # 正确读取CSV链接:每行取第一个元素,避免字符串转换的麻烦 with open('C:\\links.csv','r') as source: reader = csv.reader(source) # 跳过表头(如果有的话),没有就去掉next(reader) next(reader) inputlist = [row[0].strip() for row in reader if row[0].strip()] # IO密集型任务推荐用线程池,避免多进程的序列化开销 # n_jobs设为-1表示用所有可用线程 results = Parallel(n_jobs=-1, backend='threading')( delayed(process)(url) for url in inputlist ) # 过滤掉爬取失败的None结果 valid_results = [res for res in results if res is not None] print(f"有效爬取结果数量: {len(valid_results)}")
关键优化点
- 数据读取优化:直接提取CSV每行的第一个元素,避免不必要的字符串转换和正则操作,减少出错概率。
- 线程池替代多进程:用
backend='threading',爬虫是IO密集型任务,线程池开销更小,且不需要序列化复杂对象,避免Pickle相关问题。 - 超时与异常处理:在
scrape_code里必须添加超时和异常捕获,防止单个请求卡住导致整个任务冻结。 - 结果过滤:最后过滤掉爬取失败的结果,保证数据有效性。
内容的提问来源于stack exchange,提问作者AD233
相关产品推荐
相关产品推荐

