发出异步请求的更快方法
Faster way to make asynchronous requests
我正在尝试使用 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 = [
'
'
'
'
'
'
'
'
'
'
]
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 小时的运行时间。是否有一些技巧,例如增加 worker 的数量或在单独的进程中执行,
会导致速度提高吗?
如果您受 IO(网络)限制而不是 CPU 限制,您可以轻松增加正在使用的线程数:
session = FuturesSession(max_workers=30)
# you can experiment with the optimal number in your system/network
希望对您有所帮助!
经过进一步调查,在这种情况下我似乎是 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
我正在尝试使用 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 = [
'
'
'
'
'
'
'
'
'
'
]
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 小时的运行时间。是否有一些技巧,例如增加 worker 的数量或在单独的进程中执行, 会导致速度提高吗?
如果您受 IO(网络)限制而不是 CPU 限制,您可以轻松增加正在使用的线程数:
session = FuturesSession(max_workers=30)
# you can experiment with the optimal number in your system/network
希望对您有所帮助!
经过进一步调查,在这种情况下我似乎是 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