是否可以将 python 子进程的输出实时流式传输到网页?
Posted
技术标签:
【中文标题】是否可以将 python 子进程的输出实时流式传输到网页?【英文标题】:Is it possible to stream output from a python subprocess to a webpage in real time? 【发布时间】:2014-04-20 20:38:48 【问题描述】:提前感谢您的帮助。我对 python 还很陌生,对 html 也比较陌生。
最近几天我一直在尝试创建一个带有按钮的网页,以便在家庭服务器上执行任务。
目前我有一个 python 脚本可以生成一个带有按钮的页面:
(See the simplified example below. removed code to clean up post)
然后是运行所述命令并输出到页面上iframe
的python 脚本:
(See the simplified example below. removed code to clean up post)
这会在命令完成后输出整个完成的输出。我还尝试将 -u
选项添加到 python 脚本以无缓冲地运行它。我也尝试过使用 Python subprocess
。如果有帮助,我正在运行的命令类型是 apt-get update
,以及其他用于移动文件和修复文件夹权限的 Python 脚本。
当从普通的 Ubuntu 服务器终端运行时,它运行良好并实时输出,根据我的研究,它应该在命令运行时输出。
谁能告诉我哪里出错了?我应该使用其他语言来执行此功能吗?
编辑简化示例:
初始页面:
#runcmd.html
<head>
<title>Admin Tasks</title>
</head>
<center>
<iframe src="/scripts/python/test/createbutton.py" frameborder="0" ALLOWTRANSPARENCY="true"></iframe>
<iframe frameborder="0" ALLOWTRANSPARENCY="true" name="display"></iframe>
</center>
创建按钮的脚本:
cmd_page = '<form action="/scripts/python/test/runcmd.py" method="post" target="display" >' + '<label for="run_update">run updates</label><br>' + '<input align="Left" type="submit" value="runupdate" name="update" title="run_update">' + "</form><br>" + "\n"
print ("Content-type: text/html")
print ''
print cmd_page
应该运行命令的脚本:
# runcmd.py:
import os
import pexpect
import cgi
import cgitb
import sys
cgitb.enable()
fs = cgi.FieldStorage()
sc_command = fs.getvalue("update")
if sc_command == "runupdate":
cmd = "/usr/bin/sudo apt-get update"
pd = pexpect.spawn(cmd, timeout=None, logfile=sys.stdout)
print ("Content-type: text/html")
print ''
print "<pre>"
line = pd.readline()
while line:
line = pd.readline()
我还没有测试过上面的简化示例,所以不确定它是否有效。
编辑:
简化的示例现在应该可以工作了。
编辑:
如果我打开浏览器访问 ip:8000,下面的 Imrans 代码会显示输出,就像它在终端中运行一样,这正是我想要的。除了我为我的网站使用 Apache 服务器和一个 iframe 来显示输出。我如何使用 Apache 做到这一点?
编辑:
我现在使用下面的 Imrans 示例将输出转到 iframe,但它似乎仍然在缓冲,例如:
If I have it (the script through the web server using curl ip:8000) run apt-get update in terminal it runs fine but when outputting to the web page it seems to buffer a couple of lines => output => buffer => ouput till the command is done.
But running other python scripts the same way buffer then output everything at once even with the -u flag. While again in terminal running curl ip:800 outputs like normal.
这就是它应该的工作方式吗?
编辑 2014 年 3 月 19 日:
我使用 Imrans 方式运行的任何 bash / shell 命令似乎都会近乎实时地输出到 iframe。但是,如果我通过它运行任何类型的 python 脚本,输出会被缓冲然后发送到 iframe。
我是否可能需要将运行 Web 服务器的脚本运行的 python 脚本的输出通过管道传输?
【问题讨论】:
是的。这是code example 这看起来很混乱。对我来说,它看起来像是向终端发送命令并在那里显示它?我想要的是在 iframe 中显示命令的输出,就像它出现在终端中一样。 @J.F.塞巴斯蒂安 您的情况比代码示例中的情况更简单,其中消息在浏览器中的javascript
代码和服务器上的子进程之间来回发送。回答你的问题:子进程的标准输出被发送到浏览器。
是的,我在命令完成后拥有的代码会在 iframe 中填充命令的整个输出。我想要的是用每一行填充它,就像你在终端中运行命令时看到的那样。但我不知道怎么做。 @J.F.塞巴斯蒂安
the code example that I provided 在子进程刷新其内部标准输出缓冲区后立即发送输出(示例 python 程序使用 -u
标志,因此没有缓冲区;每个字节都立即发送)
【参考方案1】:
您需要使用HTTP chunked transfer encoding 流式传输无缓冲的命令行输出。 CherryPy 的 wsgiserver 模块内置了对分块传输编码的支持。 WSGI 应用程序可以是返回字符串列表的函数,也可以是生成字符串的生成器。如果您使用生成器作为 WSGI 应用程序,CherryPy 将自动使用分块传输。
假设这是程序,其输出将被流式传输。
# slowprint.py
import sys
import time
for i in xrange(5):
print i
sys.stdout.flush()
time.sleep(1)
这是我们的网络服务器。
2014 版(旧版 cherrpy)
# webserver.py
import subprocess
from cherrypy import wsgiserver
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
proc = subprocess.Popen(['python', 'slowprint.py'], stdout=subprocess.PIPE)
line = proc.stdout.readline()
while line:
yield line
line = proc.stdout.readline()
server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 8000), application)
server.start()
2018版
#!/usr/bin/env python2
# webserver.py
import subprocess
import cherrypy
class Root(object):
def index(self):
def content():
proc = subprocess.Popen(['python', 'slowprint.py'], stdout=subprocess.PIPE)
line = proc.stdout.readline()
while line:
yield line
line = proc.stdout.readline()
return content()
index.exposed = True
index._cp_config = 'response.stream': True
cherrypy.quickstart(Root())
用python webapp.py
启动服务器,然后在另一个终端用curl
发出请求,并观察输出逐行打印
curl 'http://localhost:8000'
【讨论】:
很抱歉,我说我对这个很陌生,所以对我来说太赤裸裸了。我在 ubuntu 服务器 13.10 上使用 apache 作为我的 Web 服务器。使用您的代码或类似的代码会输出到与按钮在同一页面上的 iframe 吗?如单击按钮 => 在服务器上运行子进程 => iframe 在发生时填充输出? @伊姆兰 你不必运行 Apache,这个脚本本身就是一个 Web 服务器。如果你有这个运行,你可以设置 iframe['src'] = 'localhost:8000' 使用你想作为查询字符串执行的命令,并在应用程序代码中从'environ'中解析查询字符串。 只是为了澄清我正在运行 apache,因为我在服务器上托管了一个基本网站。我试图有一个带有一些按钮的页面,这些按钮运行特定的任务(如 apt-get 更新)并在逐行发生时输出到 iframe。而不是在命令完成之后。有没有办法我可以用 apache 做到这一点? @伊姆兰 当我在终端中运行您的示例时,我得到的输出就像我在终端中输入了命令一样,我希望该输出出现在 iframe 中,就像命令在终端中运行一样。对不起,如果我听起来很混乱。 @伊姆兰 刚刚尝试了您的示例并将浏览器打开到 ip:8000,它完全显示了我想要的优秀。现在我如何用 apache 和 iframe 做到这一点?谢谢。 @伊姆兰以上是关于是否可以将 python 子进程的输出实时流式传输到网页?的主要内容,如果未能解决你的问题,请参考以下文章