2009-04-28 5 views
2

Alors que je jouais avec python depuis quelques mois (juste un amateur), je connais très peu la programmation web (un peu html, zero javascript, etc). Cela dit, j'ai un projet en cours qui me fait regarder la programmation web pour la première fois. Cela m'a amené à demander:Sortie Python html (première tentative), plusieurs questions (code inclus)

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

Thx les réponses, j'ai fait quelques progrès. Pour l'instant, je n'utilise que python et html. Je ne peux pas poster mon code de projet, j'ai donc écrit un petit exemple en utilisant la recherche sur twitter (voir ci-dessous).

Mes questions sont les suivantes:

(1) Je suis en train de faire quoi que ce soit terriblement stupide? J'ai l'impression que WebOutput() est clair mais inefficace. Si j'ai utilisé javascript, je suppose que je pourrais écrire un fichier de modèle html et juste mettre à jour les données. Oui? meilleure façon de le faire?

(2) à quel moment un cadre serait-il approprié pour une application comme celle-ci? exagéré?

Désolé pour les questions de base - mais je ne veux pas passer trop de temps à faire fausse route.

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 tous (llimllib, THC4k, dbr, Buddy)! Je vois vraiment l'intérêt d'utiliser un framework même pour un petit projet. Je vais jeter un coup d'oeil aux différentes options et voir lequel je préfère. llimllib: merci pour l'exemple très clair. C'était très utile! dbr: big thx !! bien au-delà de ce que j'attendais. (1) désolé pour la référence api, c'était juste l'ancien code que j'ai oublié de supprimer. (2) aime la réécriture/corrections - beaucoup mieux et utilisera cette approche dans tout mon code aller de l'avant. (3) rencontrer des problèmes d'unicode avec votre code - ne sais pas pourquoi mais fera quelques recherches. – timepilot

Répondre

8

Il ne serait pas exagéré d'utiliser un cadre pour quelque chose comme ça; Les frameworks python ont tendance à être très légers et faciles à utiliser, et faciliteraient grandement l'ajout de fonctionnalités à votre minuscule site. Mais ce n'est pas non plus nécessaire. Je suppose que vous faites cela à des fins d'apprentissage et de parler de la façon dont je changerais le code.

Vous effectuez des modèles sans moteur de modèle dans votre fonction WebOutput; il y a toutes sortes de langages de gabarit soignés pour python, mon préféré est mako. Si le code de cette fonction devient plus poilu qu'il ne l'est actuellement, je le décomposerais en un modèle; Je vais vous montrer à quoi cela ressemblerait dans un instant. Mais d'abord, j'utiliser des chaînes multilignes pour remplacer toutes ces années f.write et substitution de chaîne au lieu d'ajouter des chaînes:

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>") 

Notez également que j'ai simplifié votre boucle un peu; Je vais expliquer plus loin si ce que j'ai mis n'a pas de sens.

Si vous deviez convertir votre fonction WebOutput à Mako, vous devez d'abord importer mako en haut de votre fichier avec:

import mako 

Ensuite, vous remplacer tout le corps de WebOutput() avec:

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) 

Enfin, vous feriez un /path/to/mytmpl.txt de fichier qui ressemble à ceci:

<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> 

Et vous pouvez voir que la bonne chose que vous avez accomplie est de séparer la sortie (ou "view layer" dans les termes web) du code qui saisit et formate les données (les "layer model" et "layer controller"). Cela vous permettra de modifier plus facilement la sortie de votre script dans le futur.

(Note: Je n'ai pas testé le code que je vous ai présenté ici, excuses si ce n'est pas tout à fait raison il devrait essentiellement travailler bien.)

+0

Je pense inversé (données [-rpp:]) peut être simplifié à inversé (données) (rpp est combien d'éléments l'API retourne) – dbr

+1

bon point .... J'ai copié ce peu de son range() sans y penser. Fixé. – llimllib

1

Le problème que vous rencontrerez est que vous devrez changer le python chaque fois que vous voulez changer le code HTML. Dans ce cas, cela pourrait être bien, mais cela peut entraîner des problèmes. Je pense que l'utilisation de quelque chose avec un système de gabarit a beaucoup de sens. Je suggère de regarder Django. Le tutorial est très bon.

4

Je suggère d'utiliser un modèle pour générer la sortie, vous pouvez commencer avec la construction string.Template ou essayer quelque chose de plus fantaisiste, par exemple Mako (ou Cheetah, Genshi, Jinja, Kid, etc.).

Python a beaucoup frameworks web agréable, le plus petit d'entre eux seraient web.py ou werkzeug

Si vous voulez un cadre épanoui, regardez Pylons ou Django mais ce sont vraiment surpuissant pour un projet comme ça.

5

String formatting peut rendre les choses beaucoup plus nettes et moins d'erreurs -enclin.

Exemple simple, %s est remplacé par a title:

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

Ou plusieurs fois (titre est le même, et maintenant "mon contenu" est affiché lorsque la deuxième %s est:

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

Vous pouvez également utiliser des clés nommées lorsque vous faites %s, comme %(thekey)s, ce qui signifie que vous n'avez pas besoin de garder trace de l'ordre dans lequel se trouvent les %s. Au lieu d'un list, vous utilisez un dictionary, qui mappe la clé d'une valeur:

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

Le plus gros problème avec votre script, vous utilisez une variable globale (data). Un beaucoup mieux serait:

  • search_results d'appel, avec un argument de « swineflu »
  • search_results retourne une liste de résultats, stocker le résultat dans une variable
  • appel WebOutput, la recherche des résultats variables comme argument
  • WebOutput retourne une chaîne contenant votre code HTML
  • écrire le code HTML retourné à votre fichier

WebOutput renvoie le code HTML (en tant que chaîne) et l'écrit dans un fichier. Quelque chose comme:

results = SearchResults("swineflu", 25) 
html = WebOutput(results) 
f = open("outw.html", "w") 
f.write(html) 
f.close() 

Enfin, le module twitterd n'est requis que si vous accédez à des données nécessitant une connexion. Le calendrier public est, bien, public, et peut être consulté sans aucune authentification, de sorte que vous pouvez supprimer l'importation twitterd, et la ligne api =.Si vous ne voulez utiliser twitterd, vous devez faire quelque chose avec la variable api, par exemple:

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

Ainsi, la façon dont je pourrais avoir écrit le script est:

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) à quel moment un cadre serait-il approprié pour une application comme celle-ci? exagéré?

Je dirais toujours utiliser un framework web (avec quelques exceptions)

Maintenant, cela peut sembler étrange, étant donné tout le temps que je viens de passer à expliquer les correctifs à votre script .. mais, au-dessus modifications à votre script, c'est incroyablement facile à faire, puisque tout a été bien fonctionné! En utilisant CherryPy, qui est un framework HTTP très simple pour Python, vous pouvez facilement envoyer des données au navigateur, plutôt que d'écrire constamment un fichier.

Cela suppose que le script ci-dessus est enregistré sous la forme twitter_searcher.py.

Remarque: Je n'ai jamais utilisé CherryPy auparavant, ce n'est que l'exemple HelloWorld sur la page d'accueil CherryPy, avec quelques lignes copiées de la fonction main() du script ci-dessus!

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()) 

Enregistrez et exécutez ce script, puis accédez à http://0.0.0.0:8080/ et il va montrer votre page!

Le problème avec ceci, à chaque chargement de page il questionnera l'API de Twitter. Ce ne sera pas un problème si vous l'utilisez seulement, mais avec des centaines (voire des dizaines) de personnes qui regardent la page, ça commence à ralentir (et vous pourriez obtenir un tarif limité/bloqué par l'API twitter, éventuellement

La solution est fondamentalement de retour au début .. Vous écririez (cache) le résultat de la recherche sur le disque, en re-cherchant twitter si les données datent de plus de 60 secondes. Vous pouvez également regarder dans CherryPy's caching options .. mais cette réponse devient plutôt absurdement longue ..

Questions connexes