Created
May 24, 2011 16:04
-
-
Save westphahl/989000 to your computer and use it in GitHub Desktop.
Abfrage von Ankünften für Bahnhöfe (Q&D mit gevent/BeautifulSoup)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import gevent | |
from gevent import monkey, GreenletExit | |
monkey.patch_all() | |
import sys | |
import urllib2 | |
from datetime import datetime, date, time | |
from BeautifulSoup import BeautifulSoup | |
""" | |
URL mit GET-Parametern zur Abfrage der Ankuenfte | |
Query-Parameter (alle zwingend!): | |
rt=1 -> Funktion unbekannt | |
input=%s -> Name des Bahnhofs (muss gueltig sein) | |
boardType=arr -> Ankuenfte anzeigen | |
time=actual -> momentane Zeit verwenden | |
productFilter=11110 -> Bitmap (1=on/0=off) | |
| ICE | IC/EC | IR/D | NV(RB/RE) | S-Bahn | | |
start=yes -> Funktion unbekannt | |
""" | |
URL = "http://reiseauskunft.bahn.de/bin/bhftafel.exe/dn?" +\ | |
"rt=1&" +\ | |
"input=%s&" +\ | |
"boardType=arr&" +\ | |
"time=actual&" +\ | |
"productsFilter=11110&" +\ | |
"start=yes" | |
class BhfWorker: | |
""" | |
Worker-Klasse fuer einen Bahnhof. | |
""" | |
def __init__(self, name): | |
self.bhf = name | |
def __str__(self): | |
return self.bhf | |
def __call__(self): | |
try: | |
while True: | |
late, current, ontime = self.get_arrivals() | |
print("-------------------------") | |
print("> %s (Arrivals) @ %sh" % | |
(self.bhf, current.strftime('%H:%M'))) | |
# Abfrage in 60s, wenn Verspaetungen vorhanden | |
if (len(late) > 0): | |
sleep_sec = 60 | |
elif (len(ontime) > 0): | |
""" | |
Berechnung der Zeit (in Sekunden) bis zur naechsten | |
Ankunft eines Zuges, wobei das Polling-Intervall nicht | |
kleiner als 60 Sekunden sein darf. | |
""" | |
sleep_sec = int((ontime[0][0] - current).total_seconds()) | |
if (sleep_sec < 60): | |
sleep_sec = 60 | |
else: | |
""" | |
Wenn keine Verspaetungen vorhanden und keine Ankuenfte | |
geplant sind, erfolgt das Polling im 15 Minuten Takt. | |
> Die Bahnseite zeigt Ankuenfte 120 Minuten vorher an, | |
> d.h. dass diese Zeit noch vergroessert werden koennte. | |
""" | |
sleep_sec = 60*15 | |
print("Late: %s - On Time: %s - Next query in: %s sec" % | |
(len(late), len(ontime), sleep_sec)) | |
if (len(ontime) > 0): | |
print("Next arrival: %s @ %sh - %s min to go" % ( | |
ontime[0][1], | |
ontime[0][0].strftime('%H:%M'), | |
int((ontime[0][0] - current).total_seconds() / 60))) | |
for arrival, train in late: | |
print("%s @ %sh - Late: %s min" % ( | |
train, | |
arrival.strftime('%H:%M'), | |
int((current - arrival).total_seconds() / 60))) | |
gevent.sleep(sleep_sec) | |
except GreenletExit: | |
return self.bhf | |
def get_arrivals(self): | |
response = urllib2.urlopen(URL % urllib2.quote(self.bhf)) | |
html = response.read() | |
soup = BeautifulSoup(html) | |
table = soup.find('table', 'result') | |
rows_raw = table.findAll('tr') | |
# Ueberschriften und Navigation entfernen | |
row_list = rows_raw[2:-1] | |
late = [] | |
ontime = [] | |
late_flag = True | |
for row in row_list: | |
# Stunden und Minuten trennen und in Integer konvertieren | |
h, m = row('td', 'time')[0].text.split(':') | |
hour = int(h) | |
minute = int(m) | |
# Erzeugen einer "datetime"-Instanz fuer die Zeile | |
row_time = datetime.combine( | |
date.today(), time(int(hour), int(minute))) | |
if (row.get('class') == 'current'): | |
""" | |
Diese Zeile zeigt die aktuelle Zeit an. | |
Alle nachfolgenden Zeilen sind keine Verspaetungen mehr, | |
darum Flag setzten und die aktuelle Zeit speichern. | |
""" | |
late_flag = False | |
current_time = row_time | |
# Mit naechster Zeile fortfahren (nur aktuelle Zeit) | |
continue | |
# Zugnamen ohne Leerzeichen speichern | |
train_name = ''.join(row('td', 'train')[1].text.split()) | |
if late_flag: | |
late.append((row_time, train_name)) | |
else: | |
ontime.append((row_time, train_name)) | |
return (late, current_time, ontime) | |
class Strecke: | |
def __init__(self): | |
self.id = 1 | |
def __eq__(self, obj): | |
if (isinstance(obj, self.__class__) and (self.id == obj.id)): | |
return True | |
else: | |
return False | |
def get_track_id(self): | |
return self.id | |
@classmethod | |
def get_latest_track(cls): | |
return Strecke() | |
if __name__ == '__main__': | |
bhf_list = [ | |
u"Stuttgart Hbf", | |
u"Ulm Hbf", | |
u"Muenchen Hbf", | |
u"Berlin Hbf", | |
u"Memmingen", | |
] | |
# Worker-Objekte fuer jeden Bahnhof erzeugen | |
worker_list = [BhfWorker(bhf) for bhf in bhf_list] | |
# Jeden Worker in eigenem Greenlet ausfuehren | |
jobs = [gevent.spawn(worker) for worker in worker_list] | |
# TODO nur Platzhalter | |
current_track = Strecke() | |
while True: | |
try: | |
# Pruefen, ob neue Strecke vorhanden | |
if (current_track == Strecke.get_latest_track()): | |
gevent.sleep(60) | |
else: | |
gevent.killall(jobs) | |
# TODO neue Worker erzeugen | |
except KeyboardInterrupt: | |
gevent.killall(jobs) | |
print("-----------------------") | |
sys.exit(0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment