Skip to content

Instantly share code, notes, and snippets.

@westphahl
Created May 24, 2011 16:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westphahl/989000 to your computer and use it in GitHub Desktop.
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)
#!/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