【问题标题】:How to stream POST data into Python requests?如何将 POST 数据流式传输到 Python 请求中?
【发布时间】:2017-02-22 06:35:40
【问题描述】:

我正在使用 Python requests 库发送 POST 请求。生成 POST 数据的程序部分可以写入到任意文件类对象(输出流)中。

我怎样才能使这两个部分适合?

我原以为requests 会为此用例提供流接口,但似乎没有。它只接受一个类似文件的对象作为data 参数,它从中读取。它没有提供我可以写入的类似文件的对象。

这是 Python HTTP 库的基本问题吗?

目前的想法:

似乎最简单的解决方案是fork() 并让请求库通过管道与 POST 数据生产者进行通信。

有没有更好的办法?

或者,我可以尝试使 POST 数据生成器复杂化。但是,它正在解析一个 XML 流(来自标准输入)并生成一个新的 XML 流以用作 POST 数据。然后我反过来遇到同样的问题:XML 序列化程序库想要 write 到类似文件的对象中,我不知道 XML 序列化程序提供类似文件的对象的任何可能性其他人可以阅读

我也知道最干净、最经典的解决方案是协程,它在 Python 中通过生成器 (yield) 可以使用。 POST 数据可以通过 (yield) 而不是类似文件的对象进行流式传输,并使用拉式解析器。

但是,可以让requests 接受 POST 数据的迭代器吗?是否有一个 XML 序列化器可以很容易地与 yield 结合使用?

或者,是否有任何包装器对象将写入到类似文件的对象变成生成器,和/或提供包装迭代器的类似文件的对象?

【问题讨论】:

  • 为什么requests必须提供“一个可以写入的类文件对象”?它被设计为在前台工作而不是后台模式,因此它需要读取而不是提供描述符并被动等待输入。如果需要它,你可以自己提供它,就像:r,w=(os.fdopen(f,mode) for f,mode in zip(os.pipe(),("rb","wb"))) - 然后在不同的线程中运行这两个部分。

标签: python xml http python-requests generator


【解决方案1】:

request 确实将迭代器或生成器作为data 参数,详细信息在Chunk-Encoded Requests 中描述。在这种情况下传输编码需要分块,因为事先不知道数据大小。

这是一个非常简单的例子,它使用queue.Queue 并且可以用作类似文件的对象进行写入:

import requests
import queue
import threading

class WriteableQueue(queue.Queue):

    def write(self, data):
        # An empty string would be interpreted as EOF by the receiving server
        if data:
            self.put(data)

    def __iter__(self):
        return iter(self.get, None)

    def close(self):
        self.put(None)

# quesize can be limited in case producing is faster then streaming
q = WriteableQueue(100)

def post_request(iterable):
    r = requests.post("http://httpbin.org/post", data=iterable)
    print(r.text)

threading.Thread(target=post_request, args=(q,)).start()

# pass the queue to the serializer that writes to it ...    
q.write(b'1...')
q.write(b'2...')

# closing ends the request
q.close()

【讨论】:

  • 我不认为if data: 真的需要。为什么空数据会“被解释为 EOF”?迭代器标记是None,而不是空字节字符串b''。所以q.write(b'') 无论如何都不会被解释为 EOF,不是吗?
  • @vog 不是哨兵将其解释为 EOF,请求在遇到b'' 时终止流式传输请求。当使用外部库(例如 xml 序列化程序)写入这个类似文件的对象时,您不能确定它不会写入空字符串。它可能会无条件地做类似out.write(b''.join(some_maybe_empty_list)) 的事情。写一个空字符串不应该“关闭”文件(它并没有真正关闭,只是终止当前的迭代器。添加closed 状态不会太难)
  • 哦,很高兴知道。我调整了代码中的注释以更清楚地反映这个问题。 (如果我误解了什么,请随时修复评论。)
  • 实际上,我不得不再次编辑,因为我再次检查了它,它不是请求,而是另一端的服务器将其解释为 EOF。 requests 只是发送一个大小为 0 的块,服务器正确地将其视为流的结尾。
【解决方案2】:

将需要推送接口用于其数据接收器的数据生产者与需要拉取接口用于其数据源的数据消费者连接的唯一方法是通过中间缓冲区。这样的系统只能通过“并行”运行生产者和消费者来运行——生产者填充缓冲区,消费者从中读取,每个都在必要时暂停。这种并行性可以用协作多任务来模拟,其中生产者在缓冲区满时将控制权交给消费者,而当缓冲区变空时消费者将控制权返回给生产者。通过采用生成器方法,您将为您的案例构建一个定制的协作多任务解决方案,与基于管道的简单方法相比,这几乎不会变得更简单,其中调度生产者和消费者的责任完全由操作系统。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 2020-03-28
    • 1970-01-01
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多