Tutorial - TakeTV Streamingtermine in Kalender schreiben
- October 27, 2013
- tutorial
- german python starcraft
- no comments
- Schritt 1: Streamdaten von Webseite laden
- 1.1 Webseite lesen und Parsen
- Webseite aufrufen
- Den HTML-Inhalt speichern
- Den HTML-Text durch Beautiful Soup parsen
- 1.2 Begegnungen auslesen
- 1.3 Datum und Zeit auslesen
- 1.4 Datum und Zeit formatieren
- 1.5 Angaben in Datenstruktur speichern
- Zusammenfassug
- Schritt 2: Daten in iCal Format schreiben
- 2.1: Kalender erstellen
- 2.2: Events hinzufügen
- Zusammenfassug
- Schritt 3: iCal schreiben und auf Webserver laden
- Endresultat
TakeTV ist eine von vielen Seiten, die ihre Termine im Web veröffentlichen, allerdings in einem für Kalender unlesbaren Format. Mit etwas Pythoncode kann dem Abhilfe geschafft werden. In einem ersten Schritt werden die Termine von der Webseite gelesen; im zweiten Schritt diese dann in eine iCalendar-Datei geschrieben, welches von den meisten Kalendarprogrammen gelesen werden kann.
Schritt 1: Streamdaten von Webseite laden
Die Streamingtermine auf TakeTV werden durch HTML in folgendem Format repräsentiert:
<div class="ym-grid stream_item own">
<div class="column_left">
<span class="datum">29.10</span>
<span class="time">19:00h</span>
</div>
<div class="column_right">
<h2>
EPS WINTER CUP #4
</h2>
</div>
</div>
<div class="ym-grid stream_item even">
<div class="column_left">
<span class="datum">31.10</span>
<span class="time">15:30h</span>
</div>
<div class="column_right">
<h2>
ATC - MOUSESPORTS VS LIQUID
</h2>
</div>
</div>
Das heisst:
- Die Begegnung steht zwischen
<h2>
-Tags. - Das Datum steht zwischen
<span>
-Tags der Klasse “datum” und stehen vor dem Titel der Begegnung. - Die Zeit steht zwischen
<span>
-Tags der Klasse “time” und erscheinen ebenfalls vor der Begegnung.
Die drei Angaben können in Python ohne Nachinstallation von Modulen ausgelesen werden, z.B. mit einer Regular Expression, davon wird jedoch aus gutem Grund abgeraten. Eine hervorragende Alternative ist Scrapy. Scrapy benötigt aber insbesondere auf Windows viele Schritte bei der Installation, setzt Grundkenntnisse der eleganten XPath-Sprache voraus und ist für dieses Projekt wahrscheinlich zu viel des Guten. Im Folgenden verwende ich deshalb das ebenfalls sehr empfehlenswerte Beautiful Soup, welches ohne viel Einarbeitung zum Ziel führt.
1.1 Webseite lesen und Parsen
Als erstes wird die Webseite mit den Streamingdaten aufgerufen, der HTML-Inhalt gelesen und durch Beautiful Soup geparsed. Für den Webseitenaufruf nutze ich requests, gleiches kann aber mit urllib erreicht werden.
import requests from bs4 import BeautifulSoup ## Webseite aufrufen r = requests.get('http://www.taketv.net/streams') ## Den HTML-Inhalt speichern html = r.text ## Den HTML-Text durch Beautiful Soup parsen doc = BeautifulSoup(html)
1.2 Begegnungen auslesen
Wie erwähnt stehen die Begegnungen zwischen <h2>
-Überschriften. Es sind gleichzeitig die einzigen <h2>
-Tags auf der Seite, so dass bloss alle <h2>
-Tags auf der Seite auszulesen sind um an die Matchups zu kommen. Die getText()-Methode liefert schliesslich den Text zwischen den Tags:
# Iteriere über alle h2-Tags = Begegnungen for h2 in doc.find_all('h2'): # Inhalt der h2-Tags ist die Begegnung. who = h2.getText()
1.3 Datum und Zeit auslesen
Das Datum und die Zeit stehen in -Tags vor der Begegnung. Über die findPrevious()-Methode kann Beautiful Soup rückwärts nach Elementen mit bestimmten Kriterien suchen. Wir brauchen die -Tags mit Attribut
# Datum und Zeit auslesen date = h2.findPrevious('span', {'class':'datum'}).getText() time = h2.findPrevious('span', {'class':'time'}).getText()
1.4 Datum und Zeit formatieren
Das iCalendar-Format verlangt nach Zeitpunkte in einem genau definierten Format. Um dieses Format später zu erzeugen, werden Datum und Zeitangabe interpretiert und als Pythons datetime-Variablen gespeichert. Wir verbinden zuerst das Datum und die Zeit zu einem String mit ‘{0} {1}’.format(date,time). Als Ergebnis erhalten wir daraus z.B. 25.10 13:00h. Diese Datumsformat konvertieren wir dann mit der strptime-Methode und dem Format ‘%d.%m %H:%Mh’ (%d steht dabei für den Tag, %m für den Monat, etc.). Da die Jahresangabe fehlt, setzen wir dieses manuell auf das aktuelle Jahr (führt beim Jahreswechsel unweigerlich zu Problemen, der Einfachheit halber ignorieren wir diese Unschönheit hier). Als letztes brauchen wir für iCalendar noch die Zeitzone, diese erhalten wir über das Modul pytz:
# Zeit und Datum (letzteres ohne Jahresangabe) von String nach datetime konvertieren
when_dt = datetime.strptime('{0} {1}'.format(date,time), '%d.%m %H:%Mh')
# Manuell das Jahr auf das aktuelle Jahr setzen
when_dt = when_dt.replace(year=datetime.now().year)
# Zeitzone (benötigt 'import pytz')
berlin = pytz.timezone('Europe/Berlin')
1.5 Angaben in Datenstruktur speichern
Als Letztes speichern wir alle Angaben eines Termins in einem Dictionary event, und legen diese in einer Liste events ab. Folgende fehlende Angaben werden ausserdem noch ergänzt:
- location: als Ort der Veranstaltung wird fix der Link zu TwitchTV verwendet.
- created: der Zeitpunkt, an dem der Kalendereintrag generiert wurde, hier nehmen wir die aktuelle Zeit.
- end: der Zeitpunkt, an dem der Event endet. Wir schätzen die Länge der Übertragung auf 2.5 Stunden.
Python snippet:
event = {
'summary': 'TakeTV - {0}'.format(who),
'location': 'www.twitch.tv/taketv',
'created': datetime.utcnow().replace(tzinfo=pytz.utc),
'start' : when_dt.replace(tzinfo=berlin),
'end' : (when_dt+timedelta(hours=2,minutes=30)).replace(tzinfo=berlin),
}
events.append( event )
Zusammenfassug
Hier die komplette Funktion, um die Streamingdaten von der Webseite zu holen:
import requests
from bs4 import BeautifulSoup
from datetime import datetime, timedelta
import pytz
def get_stream_events():
events = []
r = requests.get('http://www.taketv.net/streams'
html = r.text
doc = BeautifulSoup(html)
entries = []
for h2 in doc.find_all('h2'):
who = h2.getText().encode('utf-8')
date = h2.findPrevious('span', {'class':'datum'}).getText()
time = h2.findPrevious('span', {'class':'time'}).getText()
when_dt = datetime.strptime('{0} {1}'.format(date,time), '%d.%m %H:%Mh')
when_dt = when_dt.replace(year=datetime.now().year)
berlin = pytz.timezone('Europe/Berlin')
event = {
'summary': 'TakeTV - {0}'.format(who),
'location': 'www.twitch.tv/taketv',
'created': datetime.utcnow().replace(tzinfo=pytz.utc),
'start' : when_dt.replace(tzinfo=berlin),
'end' : (when_dt+timedelta(hours=2,minutes=30)).replace(tzinfo=berlin),
}
events.append( event )
return events
Schritt 2: Daten in iCal Format schreiben
Die generierten Events werden mit dem Python Modul icalendar ins eine Kalendardatei geschrieben.
2.1: Kalender erstellen
Als Erstes wird der Kalender erstellt, eine ID gesetzt und die Version von iCal geschrieben.
from icalendar import Calendar, Event
cal = Calendar()
cal.add('proid', 'TakeTV Streaming Dates')
cal.add('version', '2.0')
2.2: Events hinzufügen
Nun werden die Events hinzugefügt. Dabei sind vordefinierte Felder zu verwendent, z.B. dtstart für den Startzeitpunkt des Events:
for e in events:
event = Event()
event.add('summary', e['summary'])
event.add('dtstart',e['start'])
event.add('dtend',e['end'])
event.add('dtstamp',e['created'])
event.add('location', e['location'])
event['uid'] = "{0}#{1}".format(e['start'],e['summary'])
cal.add_component(event)
Zusammenfassug
Hier die ganze Funktion:
from icalendar import Calendar, Event
def create_calendar(events):
cal = Calendar()
cal.add('proid', 'TakeTV Streaming Dates')
cal.add('version', '2.0')
for e in events:
event = Event()
event.add('summary', e['summary'])
event.add('dtstart',e['start'])
event.add('dtend',e['end'])
event.add('dtstamp',e['created'])
event.add('location', e['location'])
event['uid'] = "{0}#{1}".format(e['start'],e['summary'])
cal.add_component(event)
return cal
Schritt 3: iCal schreiben und auf Webserver laden
Der erstellte Kalender (aus create_calendar() wird nun in eine Datei geschrieben:
FILENAME = 'taketv.ics'
with open(FILENAME, 'wb') as f:
f.write(ical.to_ical())
Die Datei kann nun, falls ein eigener Webserver vorhanden ist, auf diesen hochgeladen werden und so von überall abonniert und abgerufen werden. Hier z.B. über FTP:
import ftplib
session = ftplib.FTP('ftp.server','username','password')
session.cwd('files')
with open(FILENAME,'rb') as file:
session.storbinary('STOR {0}'.format(FILENAME), file)
Endresultat
Der gesamte Code ist auf GitHub zu finden. Das Skript wird momentan alle 10 Minuten ausgeführt und das Ergebnis auf diese Domain hochgeladen:. Edit: Schaue aktuell nicht mehr auf TakeTV Starcraft und habe den Kalender abgeschaltet, das Skript sollte aber noch funktionieren.