Zeitliche Bedingung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
h0rnung
User
Beiträge: 46
Registriert: Mittwoch 28. Mai 2014, 11:41

Servus zusammen,

ich versuche einen RSS Feed zu streamen. Den Feed findet ihr unter http://www.lse.co.uk/chat/recent/. Aktuell funktioniert das einmalige Ausgeben der letzten feeds ganz gut, meine Probleme sind dennoch

1.) Die Automatisierung
2.) Die Gewaehrleistung, dass kein Kommentar fehlt bzw. kein Kommentar doppelt vorkommt

Code: Alles auswählen

import urllib2
from urllib2 import urlopen
import re
import cookielib
from cookielib import CookieJar
import time

oj = CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(oj))
opener.addheaders = [{'User-agent','Mozilla/5.0'}]

def main():
    try:
        read = urllib2.urlopen('http://www.lse.co.uk/chat/recent/').read()
        #print read

        try:
            titles = re.findall(r'<title>(.*?)</title>',read)
            descriptions = re.findall(r'<description>(.*?)</description>',read)
            pubDates = re.findall(r'<pubDate>(.*?)</pubDate>',read)
            #links = re.findall(r'<link.*?href="(.*?)"',read)
            for title in titles:
                print title

            for description in descriptions:
                print description

            for pubDate in pubDates:
                print pubDate

        except Exception, e:
            print 'mistake'

    except Exception, e:
        print 'mistake'
main()
Um meine oben genannten Ziele umzusetzen habe ich mir ein moegliches Vorgehen so ueberlegt:

- die Main() wird automatisch jede Minute A ausgefuehrt
- Dabei werden nur xml-items betrachtet (Datensaetze ausgelesen) deren timestamp-Minute B mit der Minute A uebereinstimmt. So wuerde das Auslesen der RSS Feeds quasi automatisch ablaufen und da ich immer nur die Kommentare einer bestimmten Minute abfrage, kann es keine Dubletten geben.

Schoene Theorie ... aber leider habe ich ueberhaupt keine Ahnung wie ich das implementieren soll. Ueber hilfe vorallem in Form von Beispiel Code freue ich mich sehr!

Tausend Dank!
BlackJack

@h0rnung: XML parst man nicht mit regulären Ausdrücken sondern mit einem XML-Parser. Beziehungsweise wenn es etwas spezialisierteres für die XML-Anwendung gibt, dann das. In diesem Falle gäbe es zum Beispiel das `feedparser`-Modul.

Das Du keine Beiträge verpasst kann man nicht wirklich garantieren, musst halt oft genug abrufen. Die <ttl>-Angabe kann da nützlich sein. Das keine Beiträge doppelt ausgegeben werden stellt man einfach sicher in dem man sich zum Beispiel in einer Datenbank merkt was man schon gesehen hat. Der Link sollte zum Beispiel eindeutig sein. Nur nach der letzten oder aktuellen Zeit würde ich nicht gehen, weil man so ganz allgemein nicht garantieren kann das nicht auch Beiträge aus der Vergangenheit verzögert im Feed auftauchen.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Die unspezifiziert abgefangenen Exceptions sind nicht schön. Hier solltest du gezielter angeben welche Ausnahme du erwartest. Zweimal die gleiche Art von Exception geschachtelt abzufangen ist ohnehin sinnfrei. Zudem solltest du, wenn du nicht gerade auf einem sehr antiken Python arbeitest, die zukunftssichere Variante mit as verwenden (except Exception as e:)
h0rnung
User
Beiträge: 46
Registriert: Mittwoch 28. Mai 2014, 11:41

Herzlichen Dank, ich habe meinen Code jetzt mittels feedparser angepasst. Das klappt so weit auch ganz gut. Danke auf fuer den Tipp mit dem Link zur Beseitigung von Dubletten. Das sollte auch funktionieren.

Das Hauptproblem bleibt aber weitgehend ungeloest

Wie schaffe ich es den RSS feed in "real time" zu streamen? Kann ich den bestehenden Code (oder am besten nur die main() Instanz) zB alle 10 Sekunden automatisch ausfuehren?

Code: Alles auswählen

import feedparser
import urllib2
import cookielib
from cookielib import CookieJar
from urllib2 import urlopen

dummy = CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(dummy))
opener.addheaders = [{'User-agent','Mozilla/5.0'}]

def main():
    try:
        feed = "http://www.lse.co.uk/chat/recent/"
        #['summary_detail', 'published_parsed', 'links', 'author', 'title', 'summary', 'title_detail', 'link', 'authors', 'author_detail', 'published']
        title = [i.title for i in feedparser.parse(feed).entries]
        summary = [i.summary for i in feedparser.parse(feed).entries]
        published = [i.published for i in feedparser.parse(feed).entries]
        
        #print [i.keys() for i in feedparser.parse(feed).entries]
        print title
        print summary
        print published
        
    except Exception as e:
        print 'mistake'
main()
mcdwerner
User
Beiträge: 113
Registriert: Donnerstag 7. Juli 2011, 14:27

@h0rnung:
versuch mal

Code: Alles auswählen

import time
while True:
    main()
    time.sleep(10)
https://docs.python.org/2/library/time.html#time.sleep
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Da gibt es noch einiges zu verbessern. Ich fange mal von oben nach unten an:

Die dummy-Variable ist überflüssig, da du sie nur an einer Stelle verwendest. In Zeile 8 kannst du gleich schreiben:

Code: Alles auswählen

opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(CookieJar()))
Du hast noch Code auf modulebene stehen, welcher dort nicht hingehört. Darunter fallen Zeilen 7 bis 9 und Zeile 26. Das solltest du alles in eine main-Funktion packen und dann mittels des folgendem Idioms aufrufen:

Code: Alles auswählen

if __name__ == "__main__":
    main()
Dann kannst du dein Modul auch importieren und wiederverwenden.

Das "feed" aus Zeile 13 solltest du besser zu einem Parameter def Funktion machen, dann kannst du sie mehrfach verwenden.

Die Namen in Zeilen 15 bis 17 sind irreführend. Es steckt ja nicht nur ein Titel drin, sondern mehrere. Ansonsten ist es auch keine gute Idee zusammenhängende Daten in verschiedenen Datenstrukturen zu halten. Du solltest zusammengehörige Daten gleich zusammenführen:

Code: Alles auswählen

entries = [
    (entry.title, entry.summary, entry.published)
    for entry
    in feedparser.parse(feed).entries
Auch ist "i" kein guter Name für die Laufvariablen der LC. Ein "i" oder "j" impliziert, dass es sich um einen Integer handelt. Komplexere Strukturen sind unerwartet. Vergib in diesem Fall einfach richtige Namen.

Das Abfangen aller Ausnahmen in Zeile 24 ist keine so tolle Idee. Damit verdeckst du dir ganz hervorragend Programmierfehler, bzw. versetzt dein Programm möglicherweise in einen ungültigen Zustand. Das kann zu riesigen Problemen und schwer zu findenden Fehlern führen. Fange daher immer nur die Fehler ab, welche du auch tatsächlich erwartest und sinnvoll behandeln kannst. Die anderen Fehler möchtest du zu Gesicht bekommen, inklusive der Fehlermeldung, sonst hast du keine Chance Probleme zu beheben.

"mistake" bedeutet nicht "Fehler" in dem Sinne in dem du es verwendest. Hier würde man eher "error" verwenden. Wie aber oben schon beschrieben, ist deine Fehlerbehandlung so nicht sinnvoll. Noch nicht einmal den Fehler ausgeben ist ein weiterer Fehler. Im Sinne von "mistake", nicht von "error" ;-)
Das Leben ist wie ein Tennisball.
h0rnung
User
Beiträge: 46
Registriert: Mittwoch 28. Mai 2014, 11:41

Zu aller erst ein fettes DANKESCHOEN, Ihr alle habt mir sehr geholfen!

@mcdwerner: Danke fuer deinen Vorschlag ich werde diesen aber erst umsetzen/ausprobieren sobald der Rest des Codes stimmig ist.

@EyDU: Vor allem dir danke ich ... du hast dir echt Muehe gegeben :). Ich habe versucht deine Punkte umzusetzen. Ein paar Probleme bei der Umsetzung habe ich aber mit Du hast noch Code auf modulebene stehen

Ich verstehe, dass ich die akt. Zeilen 7 und 24 auf der Ebene nichts verloren haben ... soll ich die dazu in die main-Funktion packen oder eine neue Funktion bauen und diese dann in der main aufrufen (Code BSP waer super hammer oberspitze :P).

Und zu dem ErrorTerm. Das habe ich auch schon gemerkt, dass das nicht sonderlich toll ist (da er bei verschiedensten Fehlern (sogar Teilfehlern) einfach irgendwo Error ausgibt und das in der Masse der RSS Daten gerne mal untergeht und eine genaue Fehlerdefinition nicht ersichtlich ist). Hast du da vllt ein BSP, wie man Standartfehler trennt/definiert oder was die haeufigsten Fehler sind!?

Herzlichen Dank nochmal ... mein leihenhaftes Auftreten haengt mit meiner ungenuegenden Programmiererfahrung zusammen. Bin hier erst am Anfang ... also habt bitte etwas Geduld :)

Code: Alles auswählen

import feedparser
import urllib2
import cookielib
from cookielib import CookieJar
from urllib2 import urlopen

opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(CookieJar()))
opener.addheaders = [{'User-agent','Mozilla/5.0'}]

def main():
    
    try:
        feed = "http://www.lse.co.uk/chat/recent/"
        #['summary_detail', 'published_parsed', 'links', 'author', 'title', 'summary', 'title_detail', 'link', 'authors', 'author_detail', 'published']
        entries = [(entry.title, entry.summary, entry.published)
            for entry
            in feedparser.parse(feed).entries]

        print entries
        
    except Exception as e:
        print 'Error'

main()
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

h0rnung hat geschrieben:Ich verstehe, dass ich die akt. Zeilen 7 und 24 auf der Ebene nichts verloren haben ... soll ich die dazu in die main-Funktion packen oder eine neue Funktion bauen und diese dann in der main aufrufen (Code BSP waer super hammer oberspitze :P).
Im Idealfall gibst du deiner jetzigen main-Funktion einen vernünftigen Namen und verfrachtest den Rest in eine neue main-Funktion. Die kannst du dir aber eigentlich auch sparen, da Zeilen sieben und acht gar nicht mehr gebraucht werden. Durch das feedparser-Modul sind die überflüssig geworden und können aus dem Code raus. Auch deine Importe solltest du aufräumen.
h0rnung hat geschrieben:Und zu dem ErrorTerm. Das habe ich auch schon gemerkt, dass das nicht sonderlich toll ist (da er bei verschiedensten Fehlern (sogar Teilfehlern) einfach irgendwo Error ausgibt und das in der Masse der RSS Daten gerne mal untergeht und eine genaue Fehlerdefinition nicht ersichtlich ist). Hast du da vllt ein BSP, wie man Standartfehler trennt/definiert oder was die haeufigsten Fehler sind!?
Es ist doch noch viel schlimmer. Mit verlorenen Feeds kannst du sicher noch halbwegs leben, aber du versteckst damit sogar eigene Programmierfehler. Das geht irgendwann nicht gut aus. Welche möglichen Ausnahmen es gibt, dass musst du im feedparser-Modul nachschauen. Eigentlich interessiert dich wohl nur die feedparser.parse-Methode. Und auch nur deren Fehler solltest du abfangen. Wie ich schon schrieb: Wenn du einen Fehler nicht behandeln kannst, weil er entweder nicht behandelbar ist oder unbekannt, dann behandle ihn gar nicht. Lasse lieber ein Programm abschmieren als in einem inkonsistenten Zustand.

Mal ein wenig aufgeräumt:

Code: Alles auswählen

import feedparser

def fetch_feed(url):
    return [
        (entry.title, entry.summary, entry.published)
        for entry
        in feedparser.parse(feed).entries]

def main():
    FEED_URL = "http://www.lse.co.uk/chat/recent/"
    feed = fetch_feed(FEED_URL)

    print feed


if __name__ == "__main__":
    main()
Das Leben ist wie ein Tennisball.
h0rnung
User
Beiträge: 46
Registriert: Mittwoch 28. Mai 2014, 11:41

Nochmal tausend Dank! Eure beiden Antworten zusammen ergeben genau das was ich gesucht habe .. perfekt!

Hier mein abschliessender Code:

Code: Alles auswählen

import feedparser
import time

def feed_load(feed):
    return [(entry.title, entry.summary, entry.published, entry.link)
            for entry
            in feedparser.parse(feed).entries]
     
def main():
    FEED_URL = "http://www.lse.co.uk/chat/recent/"
    feed = feed_load(FEED_URL)

    print feed
     
if __name__ == "__main__":
    while True:
        main()
        time.sleep(10)
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

h0rnung hat geschrieben:Nochmal tausend Dank! Eure beiden Antworten zusammen ergeben genau das was ich gesucht habe .. perfekt!
Ich würde ja noch die while-Schleife verschieben. In der Funktion main ist sie deutlich sinnvoller untergebracht und es ist ja nun auch nicht wirklich erforderlich, die FEED_URL bei jedem Durchlauf neu auf den gleichen Wert zu setzen.
BlackJack

10 Sekunden bei einem Feed der eine TTL von 5 Minuten angibt, kann eventuell vom Anbieter als zu kurz oder gar unverschämt angesehen werden.
h0rnung
User
Beiträge: 46
Registriert: Mittwoch 28. Mai 2014, 11:41

@BlackJack

Verstehe ich dich richtig: Da der Anbieter immer die Kommentare der letzten 5 Minuten im Feed anzeigt wuerdest du mir empfehlen den Feed nur zB alle 4 Minuten abzufragen?

Ps Woher weisst du das die TTL auf 5 ist .. oder schaetzt du das weil aktuell die letzten 5 Minuten angezeigt werden?
BlackJack

@h0rnung: Im Feed steht ein <ttl>-Element mit dem Wert 5.
Antworten