Python如何处理批量的多线程?

时间:2017-10-27 08:42:54

标签: python multithreading

我正在Python中构建一个简单的webcrawler。我将不得不通过~50k网站,我想通过一些多线程来加快这个过程。

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

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

然后在main函数中,我从完整的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时添加一个新的线程,但这听起来有点hackish 。

我正在研究python' Queue,但从https://docs.python.org/3/library/queue.html的示例来看,我仍然需要依赖.join(),因此等待所有线程队列已经执行。

有没有办法添加类似"事件监听器"到一个线程,这样每当一个线程完成,我可以用一个新线程更新线程列表?

1 个答案:

答案 0 :(得分:0)

或许再看一下Queue,你不需要每批次加入或者根本不需要加入。

您可以将所有50K网站放入队列。我想这可能会调用jobs,而有限数量的线程通常称为workers。然后,每个工作人员从队列中获取一个项目,对其进行处理并继续从队列中拾取项目,直到完成为止。所做的工作各不相同。一个建议是将None放在每个工作人员的队列中,每个工作人员一看到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/