2009-04-28 3 views
2

Während ich mit Python seit ein paar Monaten jetzt (nur ein Bastler) gespielt habe, weiß ich sehr wenig über Web-Programmierung (ein wenig HTML, null Javascript, etc). Das heißt, ich habe ein aktuelles Projekt, das mich zum ersten Mal auf Web-Programmierung aufmerksam macht. Dies führte mich zu fragen:Python HTML-Ausgabe (erster Versuch), mehrere Fragen (Code enthalten)

What's easiest way to get Python script output on the web?

Thx auf die Antworten, ich habe einige Fortschritte. Fürs Erste benutze ich nur Python und HTML. Ich kann meinen Projektcode nicht posten, also schrieb ich ein kleines Beispiel mit der Twitter-Suche (siehe unten).

Meine Fragen sind:

(1) bin ich etwas schrecklich dumm zu tun? Ich fühle mich wie WebOutput() ist klar, aber ineffizient. Wenn ich Javascript benutze, nehme ich an, dass ich eine HTML-Vorlagendatei schreiben und dann nur die Daten aktualisieren könnte. Ja? bessere Möglichkeit, dies zu tun?

(2) An welchem ​​Punkt wäre ein Framework für eine App wie diese geeignet? Overkill?

Entschuldigung für die grundlegenden Fragen - aber ich möchte nicht zu viel Zeit damit verbringen, den falschen Weg zu gehen.

import simplejson, urllib, time 

#query, results per page 
query = "swineflu" 
rpp = 25 
jsonURL = "http://search.twitter.com/search.json?q=" + query + "&rpp=" + str(rpp) 

#currently storing all search results, really only need most recent but want the data avail for other stuff 
data = [] 

#iterate over search results 
def SearchResults(): 
    jsonResults = simplejson.load(urllib.urlopen(jsonURL)) 
    for tweet in jsonResults["results"]: 
     try: 
      #terminal output 
      feed = tweet["from_user"] + " | " + tweet["text"] 
      print feed 
      data.append(feed) 
     except: 
      print "exception??" 

# writes latest tweets to file/web 
def WebOutput(): 
    f = open("outw.html", "w") 
    f.write("<html>\n") 
    f.write("<title>python newb's twitter search</title>\n") 
    f.write("<head><meta http-equiv='refresh' content='60'></head>\n") 
    f.write("<body>\n") 
    f.write("<h1 style='font-size:150%'>Python Newb's Twitter Search</h1>") 
    f.write("<h2 style='font-size:125%'>Searching Twitter for: " + query + "</h2>\n") 
    f.write("<h2 style='font-size:125%'>" + time.ctime() + " (updates every 60 seconds)</h2>\n") 

    for i in range(1,rpp): 
     try: 
      f.write("<p style='font-size:90%'>" + data[-i] + "</p>\n") 
     except: 
      continue 

    f.write("</body>\n") 
    f.write("</html>\n") 
    f.close() 

while True: 
    print "" 
    print "\nSearching Twitter for: " + query + " | current date/time is: " + time.ctime() 
    print "" 
    SearchResults() 
    WebOutput() 
    time.sleep(60) 
+1

Thx alle (llimllib, THC4k, dbr, Buddy)! Ich sehe definitiv den Nutzen eines Frameworks auch für ein kleines Projekt. Ich werde mir die verschiedenen Optionen ansehen und sehen, welche mir am besten gefällt. llimllib: danke für das sehr klare Beispiel. Das war sehr hilfreich! dbr: großes thx !! viel mehr als ich erwartet hatte. (1) sorry über die api referenz, es war nur alte code ich habe vergessen zu löschen. (2) liebe die Umschreibungen/Korrekturen - viel besser und werde diesen Ansatz in meinem gesamten Code verwenden. (3) In einige Unicode-Probleme mit Ihrem Code zu kommen - nicht sicher warum, aber einige Nachforschungen anstellen. – timepilot

Antwort

8

Es wäre nicht übertrieben, ein Framework für so etwas zu verwenden; Python-Frameworks sind in der Regel sehr leicht und einfach zu bedienen und erleichtern das Hinzufügen von Features zu Ihrer kleinen Site. Aber es ist auch nicht erforderlich; Ich gehe davon aus, dass Sie dies zu Lernzwecken tun und darüber sprechen, wie ich den Code ändern würde.

Sie tun Templating ohne Vorlage-Engine in Ihrer WebOutput-Funktion; Es gibt alle Arten von ordentlichen Templatesprachen für Python, von denen mein Favorit mako ist. Wenn der Code in dieser Funktion jemals haariger wird als derzeit, würde ich ihn in eine Vorlage aufteilen; Ich werde dir zeigen, wie das in einem Moment aussehen würde. Aber zuerst würde ich mehrzeilige Strings verwenden alle diese f.write die zu ersetzen, und String-Substitution anstelle der Zugabe Strings:

f.write("""<html> 
<title>python newb's twitter search</title> 
<head><meta http-equiv='refresh' content='60'></head> 
<body> 
<h1 style='font-size:150%'>Python Newb's Twitter Search</h1> 
<h2 style='font-size:125%'>Searching Twitter for: %s</h2> 
<h2 style='font-size:125%'>%s (updates every 60 seconds)</h2>""" % (query, time.ctime())) 

for datum in reversed(data): 
    f.write("<p style='font-size:90%'>%s</p>" % (datum)) 

f.write("</body></html>") 

Beachten Sie auch, dass ich Ihre for-Schleife ein wenig vereinfacht; Ich erkläre weiter, ob das was ich mache keinen Sinn ergibt.

Wenn Sie Ihre WebOutput Funktion Mako konvertieren, würden Sie zuerst mako am Anfang der Datei importieren mit:

import mako 

Dann würden Sie den ganzen Körper von WebOutput replace() mit:

f = file("outw.html", "w") 
data = reversed(data) 
t = Template(filename='/path/to/mytmpl.txt').render({"query":query, "time":time.ctime(), "data":data}) 
f.write(t) 

Schließlich würden Sie eine Datei /path/to/mytmpl.txt machen, die wie folgt aussieht:

<html> 
<title>python newb's twitter search</title> 
<head><meta http-equiv='refresh' content='60'></head> 
<body> 
<h1 style='font-size:150%'>Python Newb's Twitter Search</h1> 
<h2 style='font-size:125%'>Searching Twitter for: ${query}</h2> 
<h2 style='font-size:125%'>${time} (updates every 60 seconds)</h2> 

% for datum in data: 
    <p style'font-size:90%'>${datum}</p> 
% endfor 

</body> 
</html> 

Und Sie können sehen, dass das Schöne, was Sie erreicht haben, die Ausgabe (oder "Ansichtsebene" in Webbegriffen) von dem Code trennt, der die Daten packt und formatiert (die "Modellschicht" und "Controller-Schicht"). Dies wird es Ihnen viel leichter machen, die Ausgabe Ihres Skripts in Zukunft zu ändern.

(Anmerkung: ich nicht den Code testen habe ich hier vorgestellt habe, entschuldige mich, wenn es nicht ganz richtig, sollte es aber im Grunde allerdings funktionieren.)

+0

Ich denke, umgekehrt (Daten [-rpp:]) kann zu reversed (Daten) vereinfacht werden (Rpp ist, wie viele Elemente die API zurückgibt) – dbr

+1

guten Punkt .... Ich kopierte dieses Bit seiner Reichweite() ohne zu denken. Fest. – llimllib

1

Das Problem, mit dem Sie konfrontiert werden, ist, dass Sie das Python ändern müssen, wann immer Sie das HTML ändern wollen. Für diesen Fall könnte das in Ordnung sein, aber es kann zu Problemen führen. Ich denke, dass die Verwendung von etwas mit einem Vorlagensystem sehr sinnvoll ist. Ich würde vorschlagen, Django anzuschauen. Die tutorial ist sehr gut.

4

Ich würde vorschlagen, eine Vorlage mit der Ausgabe zu erzeugen, können Sie mit den buildin string.Template oder versuchen, etwas schicker, zum Beispiel Mako (oder Gepard, Genshi, Jinja, Kind, etc.) starten.

Python hat viele schöne Web-Frameworks, die kleinste von ihnen web.py oder werkzeug

wäre, wenn Sie ein fullblown Rahmen wollen, schauen Sie sich Pylons oder Django aber diese sind wirklich übertrieben für ein Projekt wie das.

5

String formatting kann viel sauberer machen Dinge, und weniger Fehler -anfällig.

Einfaches Beispiel wird %s durch a title ersetzt:

my_html = "<html><body><h1>%s</h1></body></html>" % ("a title") 

oder mehrere Male (Titel ist das gleiche, und jetzt "mein Inhalt" angezeigt wird, wo die zweite %s ist:

my_html = "<html><body><h1>%s</h1>%s</body></html>" % ("a title", "my content") 

Sie können auch benannte Schlüssel verwenden, wenn Sie %s, wie %(thekey)s tun, was bedeutet, dass Sie nicht verfolgen müssen, in welcher Reihenfolge die sind. Anstelle von list, verwenden Sie einen dictionary, die den Schlüssel zu einem Wert abbildet:

my_html = "<html><body><h1>%(title)s</h1>%(content)s</body></html>" % { 
    "title": "a title", 
    "content":"my content" 
} 

Das größte Problem mit dem Skript ist, können Sie eine globale Variable verwenden (data). Ein viel bessere Weg wäre:

  • Aufruf search_results, mit dem Argument „swineflu“
  • search_results gibt eine Liste der Ergebnisse, speichern das Ergebnis in einer Variablen
  • Anruf WebOutput, mit der Suche Ergebnisse Variable als Argument
  • WebOutput einen String zurückgibt, Ihre HTML
  • schreiben die zurückgegebene HTML-Dateien enthalten

WebOutput würde den HTML-Code (als String) zurückgeben und in eine Datei schreiben. Etwas wie:

Schließlich ist das twitterd Modul nur erforderlich, wenn Sie auf Daten zugreifen, die eine Anmeldung erfordern. Die öffentliche Zeitleiste ist öffentlich und kann ohne Authentifizierung aufgerufen werden, sodass Sie den twitterd-Import und die Zeile api = entfernen können.Wenn Sie twitterd verwenden wollten, würden Sie etwas mit dem api Variable, zum Beispiel zu tun haben:

api = twitterd.Api(username='username', password='password') 
statuses = api.GetPublicTimeline() 

Also, ich die Art, wie das Drehbuch geschrieben haben könnte, ist:

import time 
import urllib 
import simplejson 

def search_results(query, rpp = 25): # 25 is default value for rpp 
    url = "http://search.twitter.com/search.json?q=%s&%s" % (query, rpp) 

    jsonResults = simplejson.load(urllib.urlopen(url)) 

    data = [] # setup empty list, within function scope 
    for tweet in jsonResults["results"]: 
     # Unicode! 
     # And tweet is a dict, so we can use the string-formmating key thing 
     data.append(u"%(from_user)s | %(text)s" % tweet) 

    return data # instead of modifying the global data! 

def web_output(data, query): 
    results_html = "" 

    # loop over each index of data, storing the item in "result" 
    for result in data: 
     # append to string 
     results_html += " <p style='font-size:90%%'>%s</p>\n" % (result) 

    html = """<html> 
    <head> 
    <meta http-equiv='refresh' content='60'> 
    <title>python newb's twitter search</title> 
    </head> 
    <body> 
     <h1 style='font-size:150%%'>Python Newb's Twitter Search</h1> 
     <h2 style='font-size:125%%'>Searching Twitter for: %(query)s</h2> 
     <h2 style='font-size:125%%'> %(ctime)s (updates every 60 seconds)</h2> 
    %(results_html)s 
    </body> 
    </html> 
    """ % { 
     'query': query, 
     'ctime': time.ctime(), 
     'results_html': results_html 
    } 

    return html 


def main(): 
    query_string = "swineflu" 
    results = search_results(query_string) # second value defaults to 25 

    html = web_output(results, query_string) 

    # Moved the file writing stuff to main, so WebOutput is reusable 
    f = open("outw.html", "w") 
    f.write(html) 
    f.close() 

    # Once the file is written, display the output to the terminal: 
    for formatted_tweet in results: 
     # the .encode() turns the unicode string into an ASCII one, ignoring 
     # characters it cannot display correctly 
     print formatted_tweet.encode('ascii', 'ignore') 


if __name__ == '__main__': 
    main() 
# Common Python idiom, only runs main if directly run (not imported). 
# Then means you can do.. 

# import myscript 
# myscript.search_results("#python") 

# without your "main" function being run 

(2) An welchem ​​Punkt wäre ein Framework für eine App wie diese geeignet? Overkill?

würde ich sagen, immer einen Web-Framework verwenden (mit wenigen Ausnahmen)

Nun seltsam, das könnte scheinen, alle Zeit, die ich Korrekturen an das Skript nur ausgegeben gegeben erklären .. aber mit der oben Änderungen an Ihrem Skript, es ist unglaublich einfach zu tun, da alles schön funktionierte!

Mit CherryPy, einem wirklich einfachen HTTP-Framework für Python, können Sie Daten einfach an den Browser senden, anstatt ständig eine Datei zu schreiben.

Dies setzt voraus, dass das obige Skript als twitter_searcher.py gespeichert wird.

Hinweis Ich habe CherryPy noch nie zuvor verwendet, dies ist nur das HelloWorld-Beispiel auf der CherryPy-Homepage, mit ein paar Zeilen, die aus der obigen main() -Funktion des Skripts kopiert wurden!

import cherrypy 

# import the twitter_searcher.py script 
import twitter_searcher 
# you can now call the the functions in that script, for example: 
# twitter_searcher.search_results("something") 

class TwitterSearcher(object): 
    def index(self): 
     query_string = "swineflu" 
     results = twitter_searcher.search_results(query_string) # second value defaults to 25 
     html = twitter_searcher.web_output(results, query_string) 

     return html 
    index.exposed = True 

cherrypy.quickstart(TwitterSearcher()) 

speichern und das Skript ausführen, wechseln Sie dann zu http://0.0.0.0:8080/ und es wird Ihre Seite zeigen!

Das Problem damit, auf jeder Seite laden wird es die Twitter API abfragen. Dies wird kein Problem sein, wenn Sie es nur verwenden, aber mit Hunderten (oder sogar Zehner) von Leuten, die die Seite betrachten, würde es langsamer werden (und Sie könnten schließlich durch die Twitter-API eingeschränkt/blockiert werden))

Die Lösung ist im Grunde zurück zum Anfang .. Sie würden das Suchergebnis auf Disc schreiben (Cache) und twittern, wenn die Daten mehr als ~ 60 Sekunden alt sind. Du könntest auch in CherryPy's caching options schauen .. aber diese Antwort wird ziemlich absurd lang ..

Verwandte Themen