Python如何处理批量多线程?

问题描述 投票:0回答:1

我正在用 Python 构建一个简单的网络爬虫。我将不得不浏览约 50k 个网站,我想通过一些多线程来加速这个过程。

我定义了一个爬虫类来爬取每个网站作为Thread的元对象:

Crawler(Thread):
     def __init__(self, url, depth, wait):
...

然后在主函数中,我从完整的 URL 列表中批量迭代 10 个 URL,并为每个 URL 创建一个 Crawler 对象:

    for i in range(index, math.ceil(len(urls) / 10)):
        jobs = []
        for url in urls[i * 10:(i + 1) * 10]:
            s = Crawler(url)
            s.setDaemon(True)
            s.start()
            jobs.append(s)

        for j in jobs:
            j.join()

问题是,对于每个批次,我必须等待所有线程完成。这是低效的,因为当我有 9 个 100 页的网站和只有 1 个 10,000 页的网站时,这 9 个网站将在几分钟内完成,但我将不得不等待一个小时才能完成 10,000 页的大型网站在我可以继续下一批之前完成。

为了优化,最好从 10 个 Crawler 线程开始,然后,每次 Crawler 线程完成时,使用列表中的下一个 url 创建一个新的 Crawler,直到列表完成。

我想我可以摆脱 join() 并在

threading.enumerate
的长度上进行 while 循环,每次长度低于 10 时添加一个新线程,但这听起来有点老套。

我正在研究Python的

Queue
,但从https://docs.python.org/3/library/queue.html的示例来看,我仍然必须依赖
.join()
,因此等待所有队列中已执行的线程。

有没有办法向线程添加“事件监听器”之类的东西,以便每当线程完成时我都可以用新线程更新线程列表?

python multithreading
1个回答
3
投票

也许再次查看队列,您不需要每批加入或根本不需要加入。

您可以将所有 50K 个网站放入队列中。我可能会称之为

jobs
,而有限数量的线程通常被称为
workers
之类的东西。然后,每个工作人员从队列中拾取一个项目,对其进行处理,然后继续从队列中拾取项目,直到完成。所做的事情意味着不同。一个建议是将
None
放入每个worker的队列中,每个worker一旦看到
None
就会停止。但您还可以使用其他信号。然后,您可以使用
join
等待所有工作线程完成。在这种情况下,工作人员不需要是守护进程。 (您不想为每个 URL 创建一个单独的线程)

例如:

from threading import Queue, Thread

def crawl_worker(q):
  while True:
    url = q.get()
    if url is None:
      break
    # do something with url

url_queue = Queue()

# populate the queue
for url in urls:
  url_queue.put(url)

num_workers = 10

workers = [
  Thread(target=crawl_worker, args=(url_queue,))
  for _ in range(num_workers)
]

# add a None signal for each worker
for worker in workers:
  url_queue.put(None)

# start all workers
for worker in workers:
  worker.start()

# wait for all workers to finish
for worker in workers:
  worker.join()

# we're done now

还有其他选择。如果您实际上并不是将其视为一项练习,而是想完成一些事情,那么我会推荐 https://scrapy.org/

© www.soinside.com 2019 - 2024. All rights reserved.