เป็นไปได้ไหมที่จะสตรีมเอาต์พุตจากกระบวนการย่อยของ python ไปยังหน้าเว็บแบบเรียลไทม์

ขอบคุณล่วงหน้าสำหรับความช่วยเหลือใด ๆ ฉันค่อนข้างใหม่กับ python และใหม่กว่ากับ html

เมื่อไม่กี่วันที่ผ่านมาฉันได้ลองสร้างเว็บเพจที่มีปุ่มเพื่อทำงานบนโฮมเซิร์ฟเวอร์

ในขณะนี้ ฉันมีสคริปต์หลามที่สร้างหน้าพร้อมปุ่ม:

(See the simplified example below. removed code to clean up post)

จากนั้นสคริปต์หลามซึ่งรันคำสั่งดังกล่าวและส่งออกไปที่ 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

ฉันอาจจำเป็นต้อง PIPE ผลลัพธ์ของสคริปต์ 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 ในเบราว์เซอร์และกระบวนการย่อยบนเซิร์ฟเวอร์ เพื่อตอบคำถามของคุณ: stdout จากกระบวนการย่อยจะถูกส่งไปยังเบราว์เซอร์   -  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 โดยใช้ 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 ตกลง   -  person jfs    schedule 26.04.2014
comment
ฉันจัดการเพื่อให้มันใช้งานได้ตั้งแต่ข้อความล่าสุดของฉัน ปัญหาเดียวที่ฉันมีตอนนี้คือรอให้บัฟเฟอร์ python เต็มก่อนที่จะเริ่มพิมพ์ คำสั่ง Bash ดูเหมือนจะส่งออกไปยัง iframe เกือบจะทันที แต่สคริปต์หลามอื่น ๆ ที่ทำงานผ่านดูเหมือนว่าจะบัฟเฟอร์ก่อนจากนั้นจึงส่งออก @เจเอฟเซบาสเตียน   -  person ButtzyB    schedule 27.04.2014
comment
หากคุณคิดว่าคุณพบวิธีแก้ปัญหาแล้ว คุณสามารถโพสต์เป็นคำตอบของคุณเองได้ เพื่อแก้ไขปัญหาการบัฟเฟอร์ คุณได้ลอง python -u แล้วหรือยัง ต่อไปนี้เป็นวิธีแก้ปัญหาหลายอย่างสำหรับปัญหาการบัฟเฟอร์บล็อกสำหรับกระบวนการย่อย 'stdout   -  person jfs    schedule 27.04.2014


คำตอบ (1)


คุณต้องใช้การเข้ารหัสการถ่ายโอนแบบ HTTP chunked เพื่อสตรีมเอาต์พุตบรรทัดคำสั่งที่ไม่มีบัฟเฟอร์ โมดูล wsgiserver ของ 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 (เวอร์ชันเชอร์รี่เก่ากว่า)

# 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 update) และส่งออกไปยัง 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