Можно ли передавать вывод из подпроцесса 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)

Это выводит весь готовый вывод после завершения команды. Я также попытался добавить параметр -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" 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()

Я не тестировал приведенный выше упрощенный пример, поэтому не уверен, что он функционален.

РЕДАКТИРОВАТЬ:

Упрощенный пример должен работать сейчас.

Редактировать:

Код Imrans ниже, если я открою браузер для ip: 8000, он отобразит вывод так же, как он работал в терминале, что именно то, что я хочу. За исключением того, что я использую сервер Apache для своего веб-сайта и iframe для отображения вывода. Как мне это сделать с Apache?

редактировать:

Теперь у меня есть вывод, поступающий в iframe, с использованием приведенного ниже примера Imrans, но он все еще, например, буферизуется:

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.

Это просто так должно работать?

РЕДАКТИРОВАТЬ 19-03-2014:

любая команда bash/shell, которую я запускаю с помощью Imrans, кажется, выводится в iframe почти в реальном времени. Но если я запускаю через него какой-либо скрипт Python, вывод буферизуется, а затем отправляется в iframe.

Возможно, мне нужно передать вывод сценария Python, который запускается сценарием, запускающим веб-сервер?


person ButtzyB    schedule 15.03.2014    source источник
comment
да. Вот пример кода   -  person jfs    schedule 15.03.2014
comment
Это выглядит запутанно. Мне кажется, что отправляет команду на терминал и отображает ее там?. я хочу отобразить вывод команды в iframe, как он будет отображаться в терминале. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 15.03.2014
comment
ваш случай еще проще, чем в примере кода, где сообщения отправляются туда и обратно между кодом javascript в браузере и подпроцессом на сервере. Чтобы ответить на ваш вопрос: стандартный вывод из подпроцесса отправляется в браузер.   -  person jfs    schedule 15.03.2014
comment
Да, с кодом, который у меня есть, после завершения команды iframe заполняется всеми выводами команды. я бы хотел, чтобы он заполнялся каждой строкой, как вы видите, когда команда запускается в терминале. Но я не знаю как. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 15.03.2014
comment
пример кода, который я предоставил, отправляет вывод, как только подпроцесс очищает свой внутренний буфер stdout (пример программы python использует -u флаг, поэтому буфера нет, каждый байт отправляется сразу)   -  person jfs    schedule 15.03.2014
comment
Но разве этот пример не создает новый веб-сервер? Я уже запускаю свой сайт с помощью сервера Apache, так как мне сделать это с Apache вместо запуска отдельного сервера? Как и при переходе на страницу на моем сайте сервера apache => нажмите кнопку (для запуска команды) => вывод в мой iframe. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 15.03.2014
comment
Вы можете попробовать запустить приложение wsgi из @Imran answer, используя mod_wsgi в Apache. Или попробуйте этот скрипт cgi   -  person jfs    schedule 16.03.2014
comment
скрипт пустой. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 16.03.2014
comment
попробуйте это   -  person jfs    schedule 16.03.2014
comment
Неважно, была ошибка форматирования @J.F.Sebastian   -  person ButtzyB    schedule 17.03.2014
comment
нет, вообще не выводит в веб-браузер @J.F.Sebastian   -  person ButtzyB    schedule 17.03.2014
comment
Что произойдет, если вы запустите curl http://youhost.com/path/to/script.py?   -  person jfs    schedule 17.03.2014
comment
использование curl из терминала работает нормально. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 17.03.2014
comment
проверьте, что ответ HTTP/1.1, а не HTTP/1.0   -  person jfs    schedule 17.03.2014
comment
да, ответ кажется правильным POST /scripts/python/runcmds.py HTTP/1.1 200 489 - это то, что отображается в журнале доступа. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 17.03.2014
comment
POST — это запрос. Посмотрите на ответ. Запускаем сетевой сниффер и ищем HTTP/1.1 200 OK (первая строка в ответе от сервера)   -  person jfs    schedule 17.03.2014
comment
Я очистил журнал и попробовал еще раз, и после нажатия кнопки не получается получить только сообщение, и страница ожидает завершения сценария, но ничего не выводится в iframe. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 17.03.2014
comment
попробуйте: curl -v http://youhost.com/path/to/script.py |& grep 'HTTP/' Что вы видите HTTP/1.0 или HTTP/1.1 200 OK?   -  person jfs    schedule 26.04.2014
comment
Мне удалось заставить его работать с момента моего последнего сообщения. Единственная проблема, с которой я столкнулся сейчас, это ожидание заполнения буфера Python, прежде чем он начнет печатать. Команды Bash, похоже, почти сразу выводятся в iframe, но другие скрипты Python, запускаемые через него, сначала буферизуются, а затем выводятся. @Дж.Ф.Себастьян   -  person ButtzyB    schedule 27.04.2014
comment
если вы думаете, что нашли решение; вы можете опубликовать его как свой собственный ответ. Чтобы решить проблему с буферизацией, вы пробовали python -u? Вот несколько обходных путей для проблемы буферизации блоков для stdout подпроцесса.   -  person jfs    schedule 27.04.2014


Ответы (1)


Вам необходимо использовать кодирование передачи по частям HTTP для потоковой передачи небуферизованного вывода командной строки. Модуль wsgiserver CherryPy имеет встроенную поддержку кодирования передачи по частям. Приложения 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'
person Imran    schedule 15.03.2014
comment
Извините, как я уже сказал, я довольно новичок в этом, так что голый со мной. Я использую apache в качестве своего веб-сервера на сервере ubuntu 13.10. При использовании вашего кода или подобного он будет выводиться в iframe на той же странице, что и кнопка? как при нажатии кнопки => запускает подпроцесс на сервере => iframe заполняется выводом, как это происходит? @Имран - person ButtzyB; 15.03.2014
comment
Вам не нужно запускать Apache, этот скрипт сам по себе является веб-сервером. Если у вас это запущено, вы можете установить iframe['src'] = 'localhost:8000' с помощью команды, которую вы хотите для выполнения как строки запроса и анализа строки запроса из «окружения» в коде приложения. - person Imran; 15.03.2014
comment
Просто чтобы уточнить, я использую apache, так как размещаю базовый веб-сайт на сервере. Я пытаюсь создать страницу с некоторыми кнопками, которые запускают определенные задачи (например, обновление apt-get) и выводят в iframe, как это происходит построчно. Вместо того, чтобы выполнить команду. Есть ли способ сделать это с помощью apache? @Имран - person ButtzyB; 15.03.2014
comment
Когда я запускаю ваш пример в терминале, я получаю вывод, как если бы я ввел команду в терминал. Я бы хотел, чтобы этот вывод отображался в iframe, как если бы команда выполнялась в терминале. Извините, если я кажусь запутанным. @Имран - person ButtzyB; 15.03.2014
comment
только что попробовал ваш пример и открыл браузер на ip: 8000, и он отображает именно то, что я хочу. Отлично. теперь, как мне сделать это с apache и iframe? Спасибо. @Имран - person ButtzyB; 15.03.2014
comment
Apache в этом случае не имеет отношения, iframe позволяет вам встраивать внешний источник в веб-страницу, поэтому вы просто добавляете iframe в свой html. (Имейте в виду, что некоторые средства межсайтовой безопасности могут заблокировать его) - person Jonathan; 30.05.2017
comment
Я пытаюсь реализовать то же самое. Я использовал форму кода версии 2018 выше, но она не печатает вывод построчно. Я получаю вывод сразу (через 5 секунд). Что-то изменилось? @Имран - person Hemabh; 27.08.2020