Aufbau von Listen nach unterschiedlichen Gesichtspunkten

Veröffentlicht: 20. Dezember 2015 (Zuletzt geändert am 19. Juli 2019)
0 Kommentare
Enthalten in 2 Kategorien: Programmierung, Website
Teil 5 von 10 in der Beitragsreihe 'Automatisierung statischer Webseiten'

Aktualisierung vom 19.07.2019: Diese Webseiten werden nicht mehr mit dem Python-Script erzeugt, das hier beschrieben wird. Stattdessen nutze ich jetzt Worldpress, dass diese Listen nach sog. Taxonomies vollkommen automatisch aus der Datenbank erzeugen kann. Das Skript selbst funktioniert aber weiterhin problemlos für statische Webseiten.

Im dritten Teil dieser Artikelreihe wurden allgemeine Informationen zu jedem Artikel gesammelt. In diesem Teil nutzen wir diese Informationen um die Menge an Artikeln zu organisieren. Neben extra Webseiten für jede Kategorie und dem Veröffentlichungsjahr ermöglichen uns diese Informationen auch das automatische Erzeugen einer Sitemap. Folgende Informationen stehen uns für jeden Artikel zur Verfügung:

  • Die ID des Artikels,
  • die URL,
  • den Dateiordner bzw. Dateipfad,
  • den Artikelteaser,
  • die Kategorie und
  • das Veröffentlichungsdatum.

Theoretisch könnten für jeden dieser Punkte Listen erstellt werden, sinnvoll ist es aber nur bei dem Dateiordner, der Kategorie und dem Veröffentlichungsjahr. Die ID ist eine eindeutige Identifikationsnummer für jeden Artikel innerhalb der Webseite und wird mit jedem neuen Artikel inkrementiert. Sie dient neben dem Veröffentlichungsdatum später zur Sortierung der Artikelteaser. Ein Artikel kann mehr als einer Kategorie zugeordnet sein. Der erste Schritt besteht nun darin, eine Liste mit allen in den Artikel genutzten Ordnern, Kategorien und Veröffentlichungsjahren zu erhalten. Ein Element in dieser Liste soll den Namen der Eigenschaft, die Anzahl der Artikel, die diese Eigenschaft haben sowie die URL zu einer Index-Webseite enthalten in der später die Teaser eingefügt werden.

Ermittlung verwendeter Ordner, Kategorien und Veröffentlichungsjahren

Der nachfolgende Python-Code untersucht die Kategorien aller Artikel und erstellt eine Liste mit allen gefundenen Kategorien und der jeweiligen Artikelanzahl. Für jeder Kategorie wird eine URL zu der späteren Index-Seite erzeugt. Für Ordner und Jahr läuft dieser Prozess ähnlich, nur etwas einfacher ab, da ein Artikel nur einem einzelnem Ordner bzw. Jahr zugeordnet werden kann. Ein Artikel kann aber mehreren Kategorien angehören.

# global lists of dictionaries
listOfParsedData = []   # Liste mit Daten aller geparsten html Seiten
listOfCat = []          # Liste aller gefundenen Kategorien
categorySubfolder = "category"

def getListForIndexList(listOfParsedData, name, subfolder):
    """Search for special article properties inside a list of all html files
    provides in listOfParsedData. Returns a list for this property with name, index file and
    count"""
    list = []
    for data in listOfParsedData:        
        for j in range(len(data[name])):
            
            dataname = data[name][j]
            
            if len(dataname) > 0:
                
                for i in list:
                    if i["name"] == dataname:
                        i["count"]+=1
                        break
                else:
                    newItem = {"filepath" : "" , "name" : "" , "count" : 0, "type" : "0" }
                    newItem["name"] = dataname
                    if len(subfolder) > 0:
                        newItem["filepath"] = subfolder + os.sep + dataname.lower() + ".html"
                    else:
                        newItem["filepath"] = dataname.lower() + os.sep + "index.html"
                    newItem["count"] = 1
                    newItem["type"] = name
                    list.append(newItem)
                
    return list

# get list of all used categories, number of articles per category and a file path to html file
listOfCat = getListForIndexList(listOfParsedData, "category", categorySubfolder)

In der Sequenz listOfParsedData befinden sich für jeden Artikel die oben aufgelisteten Informationen als Dictionary. Die geparsten Kategorien eines Artikel befinden sich in einer Sequenz, die unter dem Key category abgelegt ist. Dieser Key wird der Funktion getListForIndexList als name übergeben. Die Funktion besteht als drei for-Schleifen. Die erste iteriert durch alle Artikelinformationen, die zweite durch alle Kategorien eines Artikels und die dritte sucht ob diese Kategorie bereits in der Ergebnissequenz vorhanden ist. Ist dies der Fall, so wird der Wert hinter dem Key count für die Kategorie um eins inkrementiert. Ist die Kategorie nicht bereits vorhanden, so wird ein neues Dictionary für diese Kategorie angelegt und der Ergebnissequenz hinzugefügt. In Python kann eine for-Schleife mit einem else-Block versehen werden. Dieser wird immer dann ausgeführt, wenn die Schleife beendet, aber kein break ausgelöst wurde.

Mit der Liste vorhandener Kategorien und Jahre kann ein Teil einer Seitenleiste (section) in HTML erzeugt werden, die auf die entsprechenden Listen verlinkt. Dies zeigt das nachfolgende Code-Beispiel.

# create aside section for article categories and article age
content = ""
content += "<section>\n"
content += "<h2>Artikel nach Kategorie:</h2>\n<ul>\n"
for cat in listOfCat:
    content += "\t\t<li><a href=\"/" + cat["filepath"] + "\">" + cat["name"] + " (%d)" % (cat["count"]) + "</a></li>\n"
content += "</ul>\n\n"

content += "<h2>Artikel nach Jahr:</h2>\n<ul>\n"
for age in listOfAge:
    content += "\t\t<li><a href=\"/" + age["filepath"] + "\">" + age["name"] + " (%d)" % (age["count"]) + "</a></li>\n"
content += "</ul>\n"
content += "</section>\n"

Die Seitenleiste wird als Template abgelegt und später durch das Skript automatisch als Dateiinhalt in jede HTML-Seite eingefügt, die das Template inkludiert.

Erstellen von Artikellisten nach Kategorie- bzw. Veröffentlichungsjahr

Als nächstes wird der Inhalt der Index-Listen zusammengestellt. Dabei handelt es sich um eine HTML-Seite, die eine Liste von Teasern für jeden Artikel enthält. Der Besucher kann bequem durchscrollen und den interessanten Artikel über einen Link öffnen. Dazu muss zunächst manuell ein HTML-Template für jede neue Index-Seite für jede Kategorie bzw. jedes Jahr oder jeden Ordner angelegt werden. Das Template enthält nur den typischen HTML-Aufbau mit Metainformationen, Überschriften und Datei-Inkludes für Seitenleiste oder Footer. Der eigentliche Inhalt ist nur mit dem Schlüsselwort < !--$INCLUDE_ARTICLE_TEASERS--> gefüllt. Dies wird später durch das Skript gefunden und mit den Teasern der passenden Artikel gefüllt. Der folgende Python-Code liest die Templatedatei ein, fügt die passenden Teaser aus dem Pool aller Artikel ein und kopiert die fertige HTML-Datei in den Ziel-Ordner.

noArticles = "<article>\n\t<header>\n\t<i class=\"fa fa-exclamation-triangle \" "
noArticles += "style=\"font-size:6em\"></i>\n\t<h2>Keine Artikel vorhanden.</h2>\n\t"
noArticles += "<h3>Zum momentanen Zeitpunkt existieren keine Artikel, die der ausgewählten "
noArticles += "Anfrage bzw. Kategorie entsprechen.</h3>\n</header>"
				
def includeArticleTeasers(listOfParsedData, type, name, content, limit=99999):
    """Include all article teasers that match with the property name into a given html content"""
    # listOfParsedData, entry["type"], entry["name"], content
    # category[] , category or year, Software or 2015, content
    # order list by id or date, ascending
    teaserText = ""
    count = 0
    for articleData in listOfParsedData:
        if count > limit:
            break
        if len(name) == 0:  #empty name means include all teasers
            if len(articleData["teaser"]) > 0:
                teaserText += articleData["teaser"] + "\n"
                count += 1
        else:
            if type == "category": # category ist eine Liste
                if len(articleData["teaser"]) > 0: 
                    for aD in articleData[type]:
                        if aD == name:
                            teaserText += articleData["teaser"] + "\n"
                            count += 1
            else:
                if len(articleData["teaser"]) > 0 and articleData[type] == name:
                    teaserText += articleData["teaser"] + "\n"
                    count += 1
    if len(teaserText) == 0:
        teaserText = noArticles
    content = content.replace("<!--$INCLUDE_ARTICLE_TEASERS-->", teaserText)
    print "\t<!--$INCLUDE_ARTICLE_TEASERS %s %s-->" % (type,name)
    return content
    
# merge lists for special sites to process them all in one loop
mergedList = listOfCat + listOfAge + listOfArticleFolders
for entry in mergedList:
    print "processing %s ..." % (entry["filepath"])
    try:
        file = open(entry["filepath"], "r")
    except IOError:
       print "Error open file", file.name
       sys.exit()
    content = file.read();
    file.close()

    # insert teasers (from new to old)
    content = includeArticleTeasers(listOfParsedData, entry["type"], entry["name"], content)

     # include html files
    content = includeFileContents(content)   

    # copy to target directory
    targetFileName = targetDir + os.sep + entry["filepath"]
    createFile(targetFileName, content)

Zunächt werden die drei Listen für alle vorhandenen Kategorien, Veröffentlichungsjahr und Artikelordner zusammengefasst. In Python kann man Sequenzen dazu einfach mit dem Additionsoperator verknüpfen. Danach wird für jeden Eintrag in der Liste die vorher angelegte Index-Seite geöffnet und eingelesen. Die Funktion includeArticleTeasers fügt nun die passenden Artikelteaser ein. Dazu muss der Funktion der Typ (z.B. Jahr, Kategorie etc.) und der Name (z.B. HTML5, 2015, Blog etc.) des Eintrages übergeben. Die Funktion durchsucht nun die Liste mit den Daten zu allen Artikeln. Der übergebene Typ muss dabei einem der Keys im Dictionary der geparsten Artikelinformationen entsprechen. Der Name dieses Typs ist dann das Kriterium ob ein Teaser hinzugefügt wird oder nicht. Für die Kategorie muss eine Ausnahme gemacht werden, da ein Artikel zu mehr als einer Kategorie zugeordnet werden kann. Dazu wird eine zusätzliche for-Schleife benötigt. Alle Teaser der Artikel die dem Kriterium entsprechen, werden im String teaserText gesammelt. Am Ende wird das, im übergebenen Dateiinhalt enthaltenen, Schlüsselwort < !--$INCLUDE_ARTICLE_TEASERS--> mit diesem String ersetzt. Entspricht kein Artikel dem Kriterium so wird ein Standarttext eingefügt, der eben auf diesem Umstand hinweist.
In gleicher Art und Weise wird übrigens auch die Hauptseite mit den neusten Artikeln gefüllt. Der Aufruf der Funktion hat hier nur andere Übergabeparameter. Die Sortierung wurde vorher nach Datum durchgeführt, wobei der neuste Artikel als erstes in der Sequenz listOfParsedData vorhanden ist. Die Sortierung wird mit dem Operator itemgetter nach dem Key date ausgeführt.

listOfParsedData = sorted(listOfParsedData, key=itemgetter("date"), reverse=True)
content = includeArticleTeasers(listOfParsedData, "url", "", content, 10)

Ist der als name übergebene Parameter leer, so werden alle Teaser eingefügt. Hier sollte man aber von der Artikelbegrenzung, die als limit übergeben wird, Gebrauch machen. Eine Funktion zur Erweiterung des Skriptes wäre hier das automatische Erzeugen von mehreren Seiten, die die Liste aufteilen, aber untereinander verlinkt sind.

Erzeugen einer Sitemap

Neben den Index-Seiten für ein Kriterium ist es mit den gesammelten Informationen auch mögliche eine Sitemap zu erzeugen, die nach diesen Kriterien gruppiert werden kann. Dazu nutze ich den folgenden Python-Code, der meine Sitemap erzeugt.

# insert sitemap
sitemapText = "<div class='articleteaser clearfix'>\n\t<article>\n\t\t"
sitemapText += "<header>\n\t<i class=\"fa fa-sitemap\" ></i>\n\t<h2>Seitenübersicht</h2>\n"
sitemapText += "\t<h3>Überblick über alle momentan verfügbaren Seiten unter der Domain https://www.redpiramide.de</h3>\n"
sitemapText += "<p class=\"info\"><b>Stand</b>: " + time.strftime("%d. %B %Y") + "   "
sitemapText += "<b>Verfasser</b>: <a href=\"/about/about.html\">M. Meyer</a></p>"
sitemapText += "</header>\n"
sitemapText += "<h3>Basis:</h3>\n\t\t"
sitemapText += "<ul style=\"padding-left:15px\">\n\t\t"
sitemapText += "<li><a href=\"/index.html\">Startseite</a></li>\n\t\t"
for entry in listOfParsedData:
    if entry["folder"] == "about":
        sitemapText += "<li><a href=\"" + entry["url"] + "\">" + entry["title"] + "</a></li>\n\t\t"
sitemapText += "<li><a href=\"/sitemap.html\">Sitemap</a></li>\n\t\t"
sitemapText += "</ul>\n\t\t"

sitemapText += "<h3>Artikel:</h3>\n\t\t"
sitemapText += "<ul style=\"padding-left:15px\">\n\t\t"

sitemapText += "<li>Blog</li>\n\t\t"
sitemapText += "<ul style=\"margin-left:2em\">\n\t\t\t"
for entry in listOfParsedData:
    if entry["folder"] == "blog":
        sitemapText += "<li><a href=\"" + entry["url"] + "\">" + entry["title"] + "</a></li>\n\t\t"
sitemapText += "</ul>\n\t\t"
sitemapText += "<li>Projekte</li>\n\t\t"
sitemapText += "<ul style=\"margin-left:2em\">\n\t\t\t"
for entry in listOfParsedData:
    if entry["folder"] == "projects":
        sitemapText += "<li><a href=\"" + entry["url"] + "\">" + entry["title"] + "</a></li>\n\t\t"
sitemapText += "</ul>\n\t\t"
sitemapText += "<li>Home Automation</li>\n\t\t"
sitemapText += "<ul style=\"margin-left:2em\">\n\t\t\t"
for entry in listOfParsedData:
    if entry["folder"] == "homeautomation":
        sitemapText += "<li><a href=\"" + entry["url"] + "\">" + entry["title"] + "</a></li>\n\t\t"
sitemapText += "</ul>\n\t\t"

sitemapText += "</ul>\n\t\t"

sitemapText += "<h3>Gallerien:</h3>\n\t\t"
sitemapText += "<ul style=\"padding-left:15px\">\n\t\t"
for entry in listOfParsedData:
    if entry["folder"] == "gallery":
        sitemapText += "<li><a href=\"" + entry["url"] + "\">" + entry["title"] + "</a></li>\n\t\t"
sitemapText += "</ul>\n\t\t"

sitemapText += "<h3>Kategorien:</h3>\n\t\t"
sitemapText += "<ul style=\"padding-left:15px\">\n\t\t"
for entry in listOfCat:
    sitemapText += "<li><a href=\"/" + entry["filepath"] + "\">" + entry["name"] + "</a></li>\n\t\t"
sitemapText += "</ul>\n\t\t"

sitemapText += "<h3>Archiv:</h3>\n\t\t"
sitemapText += "<ul style=\"padding-left:15px\">\n\t\t"
for entry in listOfAge:
    sitemapText += "<li><a href=\"/" + entry["filepath"] + "\">" + entry["name"] + "</a></li>\n\t\t"
sitemapText += "</ul>\n\t\t"

sitemapText += "<p style=\"clear:both;\"><a class='less' href=\"##\" onClick=\"history.go(-1); "
sitemapText += "return false;\">zurück</a></p>"

sitemapText += "</article>\n\t</div>\n"

Hierzu gibt es eigentlich nicht viel zu sagen. In der Variable sitemapText wird ein Artikel in HTML erzeugt. Danach wird eine Aufzählung für jeden Artikelordner (z.B. Blog, Projekte etc.) angelegt und mit den Links zu den passenden Artikeln gefüllt. Zusätzlich werden die Index-Seiten für jede Kategorie und jedes Veröffentlichungsjahr angegeben.

Erstellen von RSS-Feed für jede Kategorie

Natürlich ist es auch möglich die geparsten Informationen und erstellten Listen zur Erstellung eines RSS-Feeds für die Hauptseite und für jede Kategorie zu nutzen. Dies ist aber der Inhalt des 9. Teils dieser Artikelserie und wird dort auch ausführlich beschrieben.

Obwohl es ohne PHP und Datenbank nicht möglich ist, die hier Listen komplett dynamisch zu erzeugen, bietet die hier vorgestellte Methode für rein statische Webseiten dafür eine gute Alternative. Der Unterschied besteht darin, dass diese Listen zwar automatisch erstellt werden, dies aber nicht zur Laufzeit geschieht. Dennoch braucht man sich in der Regel nicht um diese Listen zu kümmern, das Skript übernimmt diese Aufgabe vollständig, so dass man sich auf das Verfassen des eigentlichen Artikels konzentrieren kann. In den nächsten Artikeln dieser Serie werde ich näher auf mein Galeriesystem, den Panormaviewer und das Inkludieren von Quellcode eingehen. 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.