我尝试使用requests-futures
库发送一批异步HTTP请求,并确定每个页面内容中是否存在特定字节字符串。
这是同步版本。请注意,我实际抓取的网站不是Stack Overflow,实际上URL的长度大约是20,000。在下面的示例中,我平均每个循环的平均停留时间为1秒,这意味着整个批次将以此速率运行半天。
import timeit
import requests
KEY = b'<meta name="referrer"'
def filter_url(url):
"""Presence or absence of `KEY` in page's content."""
resp = requests.get(url, stream=True)
return resp.content.find(KEY) > -1
urls = [
'https://stackoverflow.com/q/952914/7954504',
'https://stackoverflow.com/q/48512098/7954504',
'https://stackoverflow.com/q/48511048/7954504',
'https://stackoverflow.com/q/48509674/7954504',
'https://stackoverflow.com/q/15666943/7954504',
'https://stackoverflow.com/q/48501822/7954504',
'https://stackoverflow.com/q/48452449/7954504',
'https://stackoverflow.com/q/48452267/7954504',
'https://stackoverflow.com/q/48405592/7954504',
'https://stackoverflow.com/q/48393431/7954504'
]
start = timeit.default_timer()
res = [filter_url(url) for url in urls]
print(timeit.default_timer() - start)
# 11.748123944002145
现在,当我以异步方式进行此操作时:
from requests_futures.sessions import FuturesSession
session = FuturesSession()
def find_multi_reviews(urls):
resp = [session.get(url).result() for url in urls]
print(resp)
return [i.content.find(KEY) > -1 for i in resp]
start = timeit.default_timer()
res2 = find_multi_reviews(urls)
print(timeit.default_timer() - start)
# 1.1806047540012514
我可以获得10倍的加速。这没关系 - 但我可以做得更好吗?截至目前,我仍在关注运行时间不到2小时。是否有技巧,例如增加工人数量或在单独的流程中执行 会导致速度提升吗?
答案 0 :(得分:0)
如果你的IO(网络)绑定而非CPU限制,你可以轻松增加你使用的线程数:
session = FuturesSession(max_workers=30)
# you can experiment with the optimal number in your system/network
我希望有所帮助!
答案 1 :(得分:0)
经过进一步调查,在这种情况下看起来我的CPU绑定而不是网络绑定。
这让我相信ProcessPoolExecutor
会在这里提供一些改进。但是,我最终做的只是直接用concurrent.futures
构建一个简化版本。这将时间再缩短了一半:
def filter_url(url):
"""Presence or absence of `KEY` in page's content."""
resp = requests.get(url, stream=True)
return resp.content.find(KEY) > -1
def main():
res = []
with ProcessPoolExecutor() as executor:
for url, b in zip(urls, executor.map(filter_url, urls)):
res.append((url, b))
return res
start = timeit.default_timer()
res = main()
print(timeit.default_timer() - start)
# 0.5077149430002464