【问题标题】:Python Requests: Don't wait for request to finishPython 请求:不要等待请求完成
【发布时间】:2014-11-19 16:05:49
【问题描述】:

在 Bash 中,可以通过附加 & 在后台执行命令。如何在 Python 中做到这一点?

while True:
    data = raw_input('Enter something: ') 
    requests.post(url, data=data) # Don't wait for it to finish.
    print('Sending POST request...') # This should appear immediately.

【问题讨论】:

  • 与 Python 中受 CPU 限制的并发问题不同,这可以通过单独的线程或使用multiprocessing.dummy 来解决线程池。

标签: python python-requests


【解决方案1】:

这里有一个 hacky 方法:

try:
    requests.get("http://127.0.0.1:8000/test/",timeout=0.0000000001)
except requests.exceptions.ReadTimeout: 
    pass

【讨论】:

  • 您经常会以这种方式丢失响应。问题是关于 requests.post 的,它的主体也比简单的 get 更脆弱,超时时间很短。
  • 当我们不需要来自 api 的任何响应时效果很好
  • 尝试此操作时,服务器没有收到请求。有什么想法吗?
  • 尝试将超时时间增加到 1.0
  • 这种方法很老套。在很多情况下它都会失败。
【解决方案2】:

我使用multiprocessing.dummy.Pool。我在模块级别创建了一个单例线程池,然后使用pool.apply_async(requests.get, [params]) 来启动任务。

这个命令给了我一个未来,我可以无限期地将它添加到其他未来的列表中,直到我想收集全部或部分结果。

multiprocessing.dummy.Pool 是一个线程池而不是进程池。

示例(适用于 Python 2 和 3,只要安装了 requests):

from multiprocessing.dummy import Pool

import requests

pool = Pool(10) # Creates a pool with ten threads; more threads = more concurrency.
                # "pool" is a module attribute; you can be sure there will only
                # be one of them in your application
                # as modules are cached after initialization.

if __name__ == '__main__':
    futures = []
    for x in range(10):
        futures.append(pool.apply_async(requests.get, ['http://example.com/']))
    # futures is now a list of 10 futures.
    for future in futures:
        print(future.get()) # For each future, wait until the request is
                            # finished and then print the response object.

请求将同时执行,因此运行所有十个请求所用的时间不应超过最长的一个。此策略将仅使用一个 CPU 内核,但这应该不是问题,因为几乎所有时间都将用于等待 I/O。

【讨论】:

  • 您的解决方案看起来很有趣,但也令人困惑。什么是未来?什么是模块级别?你能提供一个可行的例子吗?
  • @octosquidopus 添加示例回答
  • 您的示例运行良好,但这并不是我想要做的。我不想发送并发请求,而是一次发送一个,但不会阻塞其余代码。我的例子现在应该不那么模棱两可了。
  • 我认为格式应该是pool.apply_async(requests.post, [url], {'data': data})。函数签名本质上是 (function_to_run, list_of_positional_args, dict_of_kwargs)。
  • r 不是响应对象,它是响应对象的未来。您会通过r.get() 获得真正的响应——这会产生一个与任何其他对象相同的响应对象。如果您只想要状态码,您可以执行r.get().status_code(请注意,如果请求导致异常,则在您调用get() 时会引发异常)。您也可以执行response = r.get() 并照常进行。如果您在实际异步请求完成之前r.get(),那么您将自动等到请求完成后再继续。
【解决方案3】:

来自Andrew Gorcester 的优雅解决方案。此外,在不使用期货的情况下,可以使用callbackerror_callback 属性(参见 doc) 以执行异步处理:

def on_success(r: Response):
    if r.status_code == 200:
        print(f'Post succeed: {r}')
    else:
        print(f'Post failed: {r}')

def on_error(ex: Exception):
    print(f'Post requests failed: {ex}')

pool.apply_async(requests.post, args=['http://server.host'], kwargs={'json': {'key':'value'},
                        callback=on_success, error_callback=on_error))

【讨论】:

    【解决方案4】:

    根据doc,你应该搬到另一个图书馆:

    阻塞还是非阻塞?

    使用默认传输适配器,Requests 不提供 任何类型的非阻塞 IO。 Response.content 属性将阻塞 直到整个响应被下载。如果您需要更多 粒度,库的流式传输功能(请参阅流式传输 请求)允许您在以下位置检索更少量的响应 一次。但是,这些调用仍然会阻塞。

    如果您担心阻塞 IO 的使用,有很多 将请求与 Python 之一结合起来的项目 异步框架。

    两个很好的例子是 grequestsrequests-futures.

    【讨论】:

    • 我尝试了 requests-futures,但在 csrf = s.cookies['csrftoken'] 失败。
    【解决方案5】:

    如果你可以在单独的python程序中编写要单独执行的代码,here是基于子处理的可能解决方案。

    否则您可能会发现有用的this question 和相关答案:诀窍是使用线程库启动一个单独的线程来执行分离的任务。

    这两种方法的一个警告可能是您必须管理的项目数(即线程数)。如果parent中的items太多,你可以考虑暂停每批项目,直到至少一些线程完成,但我认为这种管理并不简单。

    对于更复杂的方法,您可以使用基于参与者的方法,我自己没有使用过this library,但我认为在这种情况下它会有所帮助。

    【讨论】:

      【解决方案6】:
      from multiprocessing.dummy import Pool
      import requests
      
      pool = Pool()
      
      def on_success(r):
          print('Post succeed')
      
      def on_error(ex):
          print('Post requests failed')
      
      def call_api(url, data, headers):
          requests.post(url=url, data=data, headers=headers)
      
      def pool_processing_create(url, data, headers):
          pool.apply_async(call_api, args=[url, data, headers], 
          callback=on_success, error_callback=on_error)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-19
        • 2020-07-22
        • 1970-01-01
        • 2020-06-08
        相关资源
        最近更新 更多