【问题标题】:Faster way to make asynchronous requests更快地发出异步请求
【发布时间】:2018-02-02 20:06:24
【问题描述】:

我正在尝试使用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 小时的运行时间。是否有技巧,例如增加工人的数量或在单独的进程中执行, 会导致这里的速度提高吗?

【问题讨论】:

  • 多处理模块可能会有所帮助。如果你有多台机器,你应该看看任务队列(比如 celery)

标签: python asynchronous python-requests requests-futures


【解决方案1】:

如果您受 IO(网络)限制而不是 CPU 限制,您可以轻松增加正在使用的线程数:

session = FuturesSession(max_workers=30)  
# you can experiment with the optimal number in your system/network

希望对你有帮助!

【讨论】:

    【解决方案2】:

    经过进一步调查,在这种情况下,我似乎受 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-05
      • 1970-01-01
      • 1970-01-01
      • 2011-01-08
      • 2017-06-17
      • 2017-03-17
      相关资源
      最近更新 更多