Erstellen und Anbieten von RSS-Feeds

Veröffentlicht: 29. Januar 2016 (Zuletzt geändert am 25. Juli 2019)
0 Kommentare
Enthalten in einer Kategorie: Website
Teil 9 von 10 in der Beitragsreihe 'Automatisierung statischer Webseiten'

Aktualisierung vom 20.07.2019: Diese Webseiten werden nicht mehr mit dem Python-Script erzeugt, das hier beschrieben wird. Stattdessen nutze ich jetzt Worldpress, das für alle Beiträge und Kategorien automatisch RSS-Feeds erzeugt. Das Skript selbst funktioniert aber weiterhin problemlos für statische Webseiten.

Ein RSS-Feed bietet für interessierte Besucher die Möglichkeit auf neu veröffentlichte Artikel hingewiesen zu werden. Dazu muss der Feed mit einem geeigneten Programm abonniert werden. Das Programm prüft dann regelmäßig, ob sich etwas geändert hat und weißt ggf. darauf hin. Mit den bisher durch das eigene Python-Skript zur Verfügung gestellten Informationen, ist es auch möglich einen solchen Feed zu erstellen.

Der RSS-Feed wird als einfache XML-Textdatei realisiert, dess Aufbau nach der Dokumententypdefinition der Firmen My Netscape Network und UserLand Software mehr oder weniger standartisiert ist. Ich verwende die aktuelle RSS 2.0.1 Spezifikation. Ich habe mich gegen die Nutzung des geplanten Nachfolgeformats Atom entschieden, weil es noch nicht so weit verbreitet ist. Da es sich bei Atom aber ebenfalls um eine XML-Textdatei handelt, sehe ich kein Problem, diese bei Bedarf relativ schnell anbieten zu können.

Der nachfolgende Code-Ausschnitt zeigt den Aufbau einer RSS-Datei am Beispiel aller Artikel in der Kategorie Elektronik.

<?xml version='1.0' encoding='utf-8'?>
<rss version="2.0">
<channel>
	<title>RSS-Feed: Alle Artikel der Kategorie Elektronik</title>
	<link>https://www.redpiramide.de</link>
	<description>Dieser Feed enthaelt alle Artikel der Kategorie Elektronik, die auf www.redpiramide.de
	veroeffentlicht werden.</description>
	<language>de-de</language>
	<copyright>2015 by Markus Meyer</copyright>
	<image>
		<title>redpiramide.de Logo</title>
		<url>https://www.redpiramide.de/img/logo/footer-logo.png</url>
		<link>https://www.redpiramide.de</link>
	</image>
	
	<item>
		<title>Lichtorgel mit Mikrofon und Leistungsausgang</title>
		<pubDate>Sun, 22 Oct 2006 17:00:00 GMT</pubDate>
		<link>https://www.redpiramide.de/projects/24/page.html</link>
		<description>Eine Lichtorgel visualisiert akustische Signale indem je nach Tonhoehe verschieden farbige
		Lampen aufleuchten. Diese Lichtorgel ist unterteilt das akustische Signal in drei frei einstellbare
		Kanaele. Zur Aufnahme des Signals dient ein kleines Mikrofon. Am Ausgang befinden sich drei Jumbo-LEDs,
		die ueber einen Leistungsausgang mit bis zu 9 Gluehlampen unterstuetzt werden koennen [...]</description>
	</item>
	<item>
		....
	</item>
</channel>
</rss>

Die Datei startet mit der Angabe das es sich um eine XML-Datei handelt und das alle Texte in UTF-8 kodiert sind. Danach folgen allgemeine Angaben zum Feed. Neben den Pflichtangaben zu Titel, Link zur Hauptseite und der Beschreibung des Feeds, kann optional die Feed-Sprache im language-Tag über die erlauben Sprachenkürzel und ein Logo im image-Tag angegeben werden. Nach diesen allgemeinen Informationen zum Feed an sich, folgt der eigentliche Inhalt. Dieser muss aus einem Titel, einer kurzen und möglichst prägnaten Beschrebung und einem Link zum vollen Artikel bestehen. Zusätzlich muss noch das Veröffentlichungsdatum angegeben werden. All diese Informationen werden im item-Tag zusammengefasst. Dieser entspricht einem Artikel.

Wer die bisherigen Teile dieser Artikelserie gelesen hat, dem sollte bereits bekannt sein, dass ich all diese Informationen bereits durch ein Python-Script automatisch aus den Webseiten extrahiert habe. Die Daten liegen also schon vor, sie müssen nur noch richtig in der Textdatei zusammengestellt werden. Der nachfolgende Python-Code zeigt die Erstellung dieser XML-Datei in der Funktion generateRssXmlFeedByName, die eine Liste von Dictionaries enthält, in dem alle Informationen zu den Artikeln enthalten sind, die auch im Feed enthalten sein sollen.

import xml.etree.cElementTree as xml # xml processing
				
def fixHtmlSpecialCharacters2(content):
    content = content.replace("ä","ae")
    content = content.replace("ö","oe")
    content = content.replace("ü","ue")
    content = content.replace("ß","ss")
    content = content.replace("Ä","Ae")
    content = content.replace("Ö","Oe")
    content = content.replace("Ü","Ue")
    content = content.replace("\"","")
    content = content.replace("°","grad")
    
    return content
    
def generateRssXmlFeedByName(list, name, description, targetdir, url, title):
    locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
    root = xml.Element("rss", version="2.0")
    channel = xml.SubElement(root, "channel")

    xml.SubElement(channel, "title").text = process.fixHtmlSpecialCharacters2(name)
    xml.SubElement(channel, "link").text = "https://www.redpiramide.de"
    xml.SubElement(channel, "description").text = process.fixHtmlSpecialCharacters2(description)
    xml.SubElement(channel, "language").text = "de-de"
    xml.SubElement(channel, "copyright").text = "2015 by Markus Meyer"
    image = xml.SubElement(channel, "image")
    xml.SubElement(image, "title").text = "redpiramide.de Logo"
    xml.SubElement(image, "url").text = "https://www.redpiramide.de/img/logo/footer-logo.png"
    xml.SubElement(image, "link").text = "https://www.redpiramide.de"
    
    for entry in list:
        if len(entry["teaser_text"]) > 0:
            item = xml.SubElement(channel, "item")
            xml.SubElement(item, "title").text = process.fixHtmlSpecialCharacters2(entry["title"])
            temp = datetime.datetime(int(entry["year"]), int(monthToNum(entry["month"])), int(entry["day"]))
            xml.SubElement(item, "pubDate").text = temp.strftime("%a, %d %b %Y 17:00:00 GMT")
            xml.SubElement(item, "link").text = "https://www.redpiramide.de" + entry["url"]
            xml.SubElement(item, "description").text = process.fixHtmlSpecialCharacters2(entry["teaser_text"])
    newItem = { "name" : title , "url" : url }

    tree = xml.ElementTree(root)
    tree.write(targetdir + url, encoding="utf-8", xml_declaration=True)   
    print "\t%s created." % (targetdir + url)    
    return newItem

Um das Schreiben der XML-Datei deutlich zu vereinfachen, importiere ich das Standardpaket xml.etree.cElementTree. Damit können über die Methode SubElement neue Elemente eingefügt und mit Text gefüllt werden. Um das Schließen des Tags muss man sich nicht mehr kümmern. Zunächst werden die allgemeinen Informationen gefüllt. Danach folgen die Artikel in der for-Schleife. Für jeden Artikel, der einen Teaser besitzt, wird ein neuen item-Tag angelegt und mit den bereits vorher geparsten Informationen gefüllt. Zum Abschluss wird eine Datei mit der Funktion xml.ElementTree.write() erzeugt. Name und URL dieser Datei sind die Rückgabeparameter der Funktion.

Bei meinen ersten Tests mit dem, in Firefox integrierten, RSS-Reader sowie mit Thunderbird kamen Probleme mit Sonderzeichen auf, die nicht im Ascci-Breich liegen. Dazu zählen auch die deutschen Umlaute, die doch recht oft vorkommen. Über die Kodierung war leider keine Lösung zu finden, weshalb ich aus rein pragmatischen Gründen die Funktion fixHtmlSpecialCharacters2 nutze, die diese Sonderzeichen durch ein Äquivalent mit Ascii-Zeichen ersetzt.

Die oben gezeigte Funktion wird eingesetzt um RSS-Feeds mit allen Artikeln sowie mit einem Subset an Artikeln nach Kategorie oder Ordner zu erstellen.

import os       # directory access
rssFileList = []
rssFileList.append(generateRssXmlFeedByName(listOfParsedData,
                                            "RSS-Feed: Alle Artikel", "Dieser Feed enthält alle Artikel, die auf
                                            www.redpiramide.de veröffentlicht werden.", targetDir, os.sep +
                                            "rss.xml" , "Alle Artikel"))

# feed for each category
for cat in listOfCat:
    tempList = []
    for entry in listOfParsedData:
        for aCat in entry["category"]:
            if aCat == cat["name"]:
                tempList.append(entry)  
    rssFileList.append(generateRssXmlFeedByName(tempList, 
                       "RSS-Feed: Alle Artikel der Kategorie " + cat["name"], 
                       "Dieser Feed enthält alle Artikel der Kategorie " + cat["name"] + ", die auf
                        www.redpiramide.de veröffentlicht werden.", targetDir, os.sep + categorySubfolder +
                        os.sep +  "rss_" + cat["name"].lower() + ".xml", "Artikel der Kategorie " + cat["name"]))

# feed for each folder
for folder in listOfArticleFolders:
    tempList = []
    for entry in listOfParsedData:
        if entry["folder"] == folder["name"]:
            tempList.append(entry)   
    rssFileList.append(generateRssXmlFeedByName(tempList,
                       "RSS-Feed: Alle Artikel im Hauptordner " + folder["name"],
                       "Dieser Feed enthält alle Artikel im Hauptordner " + folder["name"] + ", die auf
                       www.redpiramide.de veröffentlicht werden.", targetDir, os.sep + folder["name"] +
                       os.sep +  "rss.xml", "Artikel im Hauptordner " + folder["name"]))

Der erste Abschnitt nutzt die Sequenz listOfParsedData in der alle Artikel enthalten sind um einen RSS-Feed zu erstellen, der auch alle Artikel enthält. Im zweiten Abschnitt erfolgt die Erstellung eines RSS-Feeds für jede Kategorie. Die Sequenz listOfCat enthält alle Kategorien, die während des Parsens der Artikel gefunden wurden. Um die Funktion generateRssXmlFeedByName zu nutzen, wird eine Sequenz benötigt, die nur Artikel der jeweiligen Kategorie enthält. Diese wird mit der for-Schleife erzeugt. Bei den Ordnern funktioniert es ebenso nur mit dem Unterschied, dass ein Artikel in mehreren Kategorien enthalten sein kann, wofür eine weitere for-Schleife benötigt wird.

Durch die Verwendung des Skriptes werden die RSS-Feeds “nebenbei” erzeugt. Beim Hinzufügen von neuen Artikeln wird der oder die RSS-Feeds automatisch aktualisiert – und bei der Veröffentlichung der Webseiten erhalten alle Abonnenten die Meldung zu den neuen Artikeln. Mit dem Skript ist dieses Feature auch mit einer statischen Webseite ohne Datenbank oder Content-Management-System möglich! Im nächsten und letzten Teil dieser Artikelserie geht es um das Veröffentlichen der Webseiten bzw. das Synchronisieren mit dem FTP-Server. Vielen Dank fürs Lesen!

Vorheriger Beitrag der ReiheNächster Beitrag der Reihe

zurück

Teile deinen Kommentar oder Feedback zu diesem Beitrag.

Die Angabe der E-Mail-Adresse ist optional und wird nicht veröffentlicht. Sie wird zur Einbindung deines Gravatars und ggf. zur Kontaktaufnahme verwendet. Erforderliche Felder sind mit * markiert.

*

*

*

Durch Betätigen von "Kommentar abschicken", stimmen Sie der Veröffentlichung der eingegebenen Daten zu. Weitere Informationen finden Sie in der Datenschutzerklärung.
Sie können Quellcode über <pre><code>Quellcode</code></pre> in Kommentare einfügen.