Python线程池爬虫,数据紊乱问题

代码说明

使用线程池模块 →from concurrent.futures import ThreadPoolExecutor

# 创建线程池
self.pool = ThreadPoolExecutor(30)
# 提交任务
for i in range(self.idqueue.qsize()):
  sur = self.pool.submit(self.get_response, self.idqueue.get())
  sur.add_done_callback(self.parse)
self.pool.shutdown(True)
# 获取响应,请求url2需要携带响应1的参数,请求url3需要携带响应2的参数,以此类推
def get_response():
  url1 = ""
  response1 = ""
  url2 = ""
  response2 = ""
  url3 = ""
  response3 = ""
  url4 = ""
  response4 = ""
  return (response1,response2,response3,response4,)
# 解析内容并做持久化存储
def parse(reslst:Future):
  with lock: # 线程锁
    res1,res2,res3,res4 = reslst.result()
    # 下面代码为解析响应的内容后并存入excel

问题描述

目前发现的问题是在解析函数里少数response4的响应内容会错乱

需求

各位大lao给我指导一下代码有哪些问题,或许使用scrapy框架更好些?

该回答引用GPTᴼᴾᴱᴺᴬᴵ,具体如下:

根据你提供的代码,可能出现数据错乱的原因有几个:

1、在线程池中,每个线程之间的执行顺序是不确定的,有可能先完成的任务后返回结果,这可能会导致响应内容错乱。
2、在解析函数中,使用了共享变量(lock),但是代码片段中没有给出锁的具体实现,如果锁的实现不正确,也可能会导致数据错乱。
3、另外,由于代码片段中未给出完整的代码,不清楚响应内容是如何处理的,如果在处理响应内容时存在问题,也可能会导致数据错乱。

   建议你可以尝试使用Scrapy框架进行爬虫开发,Scrapy框架自带的调度器和去重器,可以有效避免数据重复和数据错乱的问题。同时,Scrapy框架也提供了异步处理的功能,可以更好地提升爬虫的效率。

   如果你仍然想使用线程池进行爬虫开发,可以考虑使用多线程安全的队列模块(queue)来传递响应内容,确保每个任务的响应结果和任务本身一一对应,避免数据错乱的问题。同时,确保锁的正确实现,避免多线程之间的竞争问题。

你上面提供的代码可以尝试按照如下改动,具体改完的代码如下:

# 创建线程池
self.pool = ThreadPoolExecutor(30)

# 定义一个字典,用于存储每个任务的响应结果
response_dict = {}

# 提交任务
for i in range(self.idqueue.qsize()):
    task_id = self.idqueue.get()
    # 将任务id作为参数传递给 get_response 函数
    sur = self.pool.submit(self.get_response, task_id)
    # 将任务id与对应的Future对象存储到字典中
    response_dict[task_id] = sur

self.pool.shutdown(True)

# 解析内容并做持久化存储
with lock:
    for task_id, future_obj in response_dict.items():
        res1, res2, res3, res4 = future_obj.result()
        # 下面代码为解析响应的内容后并存入excel

# 获取响应,请求url2需要携带响应1的参数,请求url3需要携带响应2的参数,以此类推
def get_response(task_id):
    url1 = ""
    response1 = ""
    # 根据任务id获取url1需要的参数
    param1 = get_param1(task_id)
    # 构造url1并发送请求
    # ...
    # 获取响应1的结果
    # ...
    
    url2 = ""
    response2 = ""
    # 根据任务id和响应1的结果获取url2需要的参数
    param2 = get_param2(task_id, response1)
    # 构造url2并发送请求
    # ...
    # 获取响应2的结果
    # ...
    
    url3 = ""
    response3 = ""
    # 根据任务id和响应2的结果获取url3需要的参数
    param3 = get_param3(task_id, response2)
    # 构造url3并发送请求
    # ...
    # 获取响应3的结果
    # ...
    
    url4 = ""
    response4 = ""
    # 根据任务id和响应3的结果获取url4需要的参数
    param4 = get_param4(task_id, response3)
    # 构造url4并发送请求
    # ...
    # 获取响应4的结果
    # ...
    
    return (response1, response2, response3, response4)

如果以上回答对您有所帮助,望采纳~谢谢