首页 新闻 会员 周边

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

0
悬赏园豆:20 [已解决问题] 解决于 2023-03-29 12:56

这个函数是用来获取响应的,请求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

现在遇到的问题是少数数据会紊乱,多线程开始入口如下

pool = ThreadPoolExecutor(30)
for i in range(idqueue.qsize()):
sur = pool.submit(get_response, idqueue.get())
sur.add_done_callback(parse)
pool.shutdown(True)

KikN的主页 KikN | 初学一级 | 园豆:38
提问于:2023-03-23 09:42
< >
分享
最佳答案
0

这种情况通常是由于多线程执行顺序不一致导致的。在您的代码中,您通过异步地使用ThreadPoolExecutor提交任务来获取响应,而不是同步地获取响应。

这意味着在处理和解析响应时,您不能保证每个响应的顺序都正确。在多线程的情况下,每个响应的到达时间可能会有所不同,这会影响您的数据处理和存储的顺序。

为了解决这个问题,您可以考虑使用一个有序的队列来保存响应,并在处理响应时按照先到先处理的顺序进行处理。也就是说,每当您获取一个响应时,您需要将其放入一个有序队列中,并在将其处理时按照它们到达的顺序进行处理。

以下是一些可能的代码示例,用于演示如何使用有序队列在多线程环境中有序处理响应:

from queue import PriorityQueue

class Response:
    def __init__(self, data, index):
        self.data = data
        self.index = index
    
    def __lt__(self, other):
        return self.index < other.index
        
q = PriorityQueue()

def get_response(i):
    # 获取响应并添加到队列中
    response = YourResponseFunction(i)
    q.put(Response(response, i))

def parse_response(q):
    while not q.empty():
        response = q.get()
        # 处理响应并存储数据
        parse(response.data)

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

# 向线程池提交任务
for i in range(idqueue.qsize()):
    pool.submit(get_response, i)

# 用队列中的响应处理数据
parse_response(q)

在这个示例代码中,我们使用了Python的优先队列来按照到达的顺序保存响应。在get_response函数中,我们首先获取响应并将其打包成一个Response对象,然后将其插入到队列中。

在parse_response函数中,我们首先从队列中读取响应,然后处理并存储响应。为了保证顺序,我们不断从队列中读取响应,直到队列为空。

希望这个解决方案能够帮助您解决数据乱序问题。

收获园豆:14
薛小谦 | 初学一级 |园豆:195 | 2023-03-28 15:12
其他回答(1)
0

这段代码中的get_response函数返回了4个响应,分别对应4个URL。parse函数接收到这4个响应后,会对它们进行解析并将解析结果保存到Excel中。在多线程环境下,如果有多个线程同时调用parse函数,就有可能导致数据紊乱。为了避免这种情况,您可以使用线程锁来保护parse函数。在parse函数中,您可以使用Python的threading.Lock类来创建一个线程锁,然后在需要保护的代码块前后分别调用acquire和release方法来获取和释放线程锁。这样可以确保同一时间只有一个线程可以访问parse函数。以下是一个简单的示例:

import threading

lock = threading.Lock()

def parse(reslst:Future):
with lock:
res1,res2,res3,res4 = reslst.result()
# 解析响应并保存到Excel

在这个示例中,我们创建了一个名为lock的线程锁,并在parse函数中使用with语句来获取和释放线程锁。这样可以确保同一时间只有一个线程可以访问parse函数,从而避免数据紊乱的问题。

收获园豆:6
十四年新* | 园豆:229 (菜鸟二级) | 2023-03-23 14:06

我的代码中有使用lock呀

支持(0) 反对(0) KikN | 园豆:38 (初学一级) | 2023-03-23 14:08
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册