Python请求:使用响应.raw作为子流程的stdin

2024-06-28 21:14:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我只想使用requests.get()下载一些文件(可能非常大),然后将数据传递给subprocess.Popen创建的另一个进程的stdin。示例代码是

In [137]: r = requests.get('http://www.google.com', stream=True)
In [138]: p = subprocess.Popen(['wc'], stdin=r.raw, stdout=subprocess.PIPE)
In [139]: p.communicate()

这不好用。两个问题:

  1. 即使网络很好,也需要很长时间才能完成。原因是子进程尝试读取一些数据直到超时。在

    $ sudo strace -p 181082                                                                                                             
    strace: Process 181082 attached
    read(0, "", 16384)                      = 0   <== Here, it takes very long time.
    fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
    write(1, "      0       0       0\n", 24) = 24
    close(0)                                = 0
    close(1)                                = 0
    close(2)                                = 0
    exit_group(0)                           = ?
    +++ exited with 0 +++
    
  2. 传递给stdin的数据不正确。如您所见,wc的输出是0 0 0

我试图设置r.raw.decode_content = True,但没有帮助。在

注意:由于get下载的文件可能非常大,使用r.content等是不可接受的。在

注意:我使用的是python2.7。在


Tags: 文件数据intrueclosegetraw进程
1条回答
网友
1楼 · 发布于 2024-06-28 21:14:18

最简单的方法是使用response.iter_content递增地读取响应正文并将其分块写入进程的stdin:

import requests
import subprocess
r = requests.get('http://www.stackoverflow.com', stream=True)
r.raise_for_status()
p = subprocess.Popen(['wc'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
for chunk in r.iter_content(2048):
    p.stdin.write(chunk)
stdout, stderr = p.communicate()
print("wc output:", stdout)

这样就不会使用requests中的任何类似文件的对象,但不需要。请注意,Popen已经创建了一个类似于文件的对象(即管道),它可以作为process.stdin访问,您可以使用它在进程到达时将数据实时传递给进程。在

目前还不清楚p.communicate()在这里做了两件事:

  • 关闭标准输入管道,而不向其写入任何数据,告诉wc写入完毕,它可以输出计数
  • 然后将stdout管道中的所有内容读入变量。在

注意:wc很适合这里,因为它在打印到stdout之前消耗了整个stdin,但是如果您的进程在stdin之前尝试写入stdout,那么这种方法可能会死锁。在这种情况下,当进程等待Python从p.stdout读取数据时,程序可能会冻结在p.stdin.write上。在

为了正确地处理这个问题,您需要分别编写读写线程。communicate()为您做了这件事,但它只接受字符串形式的输入,而不接受流或生成器。另一种选择是让进程写入临时文件而不是管道。在

相关问题 更多 >