可以实时将Python子进程的输出流式传输到网页吗?
非常感谢大家的帮助。我对Python还比较陌生,对HTML就更不熟悉了。
这几天我一直在尝试创建一个网页,里面有按钮可以在家里的服务器上执行一些任务。
目前我有一个Python脚本,它可以生成一个带按钮的页面:
(See the simplified example below. removed code to clean up post)
然后还有一个Python脚本,它会运行这些命令,并把结果输出到页面上的一个iframe
里:
(See the simplified example below. removed code to clean up post)
这个脚本在命令执行完后会输出所有的结果。我还尝试过给Python脚本加上-u
选项,这样可以让它不缓存输出。我也试过使用Python的subprocess
模块。如果有帮助的话,我运行的命令包括apt-get update
,还有其他一些Python脚本,用于移动文件和修复文件夹权限。
在普通的Ubuntu服务器终端中运行这些命令时,一切都很好,输出也是实时的。根据我的研究,它应该在命令执行时就开始输出结果。
有人能告诉我我哪里出错了吗?我是否应该使用其他语言来实现这个功能?
编辑:简化的例子:
初始页面:
#runcmd.html
<head>
<title>Admin Tasks</title>
</head>
<center>
<iframe src="/scripts/python/test/createbutton.py" width="650" height="800" frameborder="0" ALLOWTRANSPARENCY="true"></iframe>
<iframe width="650" height="800" 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()
我还没有测试上面的简化例子,所以不确定它是否能正常工作。
编辑:
简化的例子现在应该可以工作了。
编辑:
Imran的代码,如果我在浏览器中打开ip:8000,它的输出就像在终端中运行一样,这正是我想要的。不过我在我的网站上使用的是Apache服务器,并且用iframe来显示输出。我该如何在Apache中做到这一点呢?
编辑:
现在我已经通过Imran的例子让输出显示在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日:
我用Imran的方法运行的任何bash/shell命令似乎都能在接近实时的情况下输出到iframe。但如果我通过它运行任何Python脚本,输出就会被缓存,然后再发送到iframe。
我是否需要把运行的Python脚本的输出通过管道传递给运行网页服务器的脚本呢?
1 个回答
你需要使用HTTP 分块传输编码来实时输出命令行的结果。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版本(旧版CherryPy)
# 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'