由于我的scaper运行速度很慢(一次一页)所以我正在尝试使用线程使其更快地运行。我有一个函数scrape(网站),它可以在网站上进行抓取,因此我可以轻松地创建每个线程并在每个线程上调用start()。
现在我想实现一个num_threads变量,它是我想要同时运行的线程数。处理这些多线程的最佳方法是什么?
对于ex:假设num_threads = 5,我的目标是启动5个线程然后抓住列表中的前5个网站并刮掉它们,然后如果线程#3完成,它将从列表中抓取第6个网站以立即刮掉,不要等到其他线程结束。
有关如何处理它的任何建议?谢谢
答案 0 :(得分:0)
取决于。
如果您的代码花费大部分时间等待网络操作(可能在网络抓取应用程序中),则线程是合适的。实现线程池的最佳方法是在3.4中使用concurrent.futures
。如果做不到这一点,你可以创建一个threading.Queue
对象,并将每个线程编写为一个无限循环,从队列中消耗工作对象并处理它们。
如果您的代码在下载后花费大部分时间处理数据,则由于GIL,线程无用。 concurrent.futures
提供对进程并发性的支持,但同样仅适用于3.4+。对于较旧的蟒蛇,请使用multiprocessing
。它提供Pool
类型,简化了创建进程池的过程。
您应该对您的代码进行分析(使用cProfile
)来确定您遇到的这两种方案中的哪一种。
答案 1 :(得分:0)
如果您使用的是Python 3,请查看concurrent.futures.ThreadPoolExecutor
从文档ThreadPoolExecutor Example中提取示例:
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
# Retrieve a single page and report the url and contents
def load_url(url, timeout):
conn = urllib.request.urlopen(url, timeout=timeout)
return conn.readall()
# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# Start the load operations and mark each future with its URL
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
如果您正在使用Python 2,则可以使用backport:
from concurrent import futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
def load_url(url, timeout):
return urllib.request.urlopen(url, timeout=timeout).read()
with futures.ThreadPoolExecutor(max_workers=5) as executor:
future_to_url = dict((executor.submit(load_url, url, 60), url)
for url in URLS)
for future in futures.as_completed(future_to_url):
url = future_to_url[future]
if future.exception() is not None:
print('%r generated an exception: %s' % (url,
future.exception()))
else:
print('%r page is %d bytes' % (url, len(future.result())))