Liste splitten ohne Umwandlung in STR

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Hallo Zusammen,

vielleicht könnt ihr mir bei einem Problem helfen, wo ich gerade nicht weiterkomme: Ich habe eine unbestimmte Menge an eingegebenen URLS (in diesem Fall sind es zwei), von denen ich nun alle sublinks (<a.href...>) extrahiere und in die Liste "liste_html_home_sublinks" speichere. Das funktioniert soweit.

Jede einzelne Liste hat einen Seperator in Form von "split_list = ["ENDE_DER_LISTE"], anhand dessen ich die gesamte Liste splitten möchte und einzelnt abspeichern zu können.

Das sieht dann also so aus:
Liste html_home_sublinks:
https://www.kenfm.de/impressum
https://www.kenfm.de/heute-unter-gleichgesinnten
....
"ENDE DER LISTE"
https://www.br.de/in-der-welt
https://www.br.de/alles-ausser-bayern
...
"ENDE_DER_LISTE

Mein Ziel wäre:
html_home_sublinks_seperate[0] --> Alle sublinks von https://www.kenfm.de
html_home_sublinks_seperate[1] --> Alle sublinks von https://www.br.de

Versucht habe ich es mit folgendem, leider erfolglos:

Code: Alles auswählen

# using itertools.chain() + zip() to perform custom list split
            temp = zip(chain[0], split_list), chain(split_list, [None]))
            res = list(html_home_sublinks[i : j] for i, j in temp)
Die methode .split habe ich bisher nur bei Daten vom Typ str benutzt. Ich muss natürlich im Endergebnis die Liste iterieren können, was bei vorheriger Konvertierung der Liste zu str nicht mehr so recht funktioniert.

Also die Sache wäre an sich nicht so schwer, aber ich kann keine statische Anzahl von Listen für die separierten sublinks "hardcoden", da je nach Eingabe mal 5, mal 2, mal 7 URLs eingegeben werden. Ich müsste also irgendwie eine variable Zahl von Listen erzeugen, je nach Eingabe des Nutzers. Wenn er 7 Urls eingeben möchte, müssten 7 Listen erzeugt werden...keine Ahnung wie das geht :oops:

Hier mein Quellcode dazu:

Code: Alles auswählen

# html_home_seperate[0] --> https://www.kenfm.de 
        # html_home_seperate[1] --> https://www.br.de
        c = 0

        liste_html_home_sublinks=[]         # Speicherung der Sublinks aller URLs
        split_list = ["ENDE_DER_LISTE"]     # Seperator
        html_home_sublinks_seperate = []

        for link in self.stored_data_url:

            # Öffne HTML Seite BeautifulSoup
            soup = BeautifulSoup(html_home_seperate[c], 'html.parser')
        
            # Extrahiere alle Sublinks in eine Liste
            html_home_sublinks =  [a['href'] for a in soup.find_all('a', href=True) if a.text] 

            # Entferne alle Dopplungen in der Liste
            html_home_sublinks = set(html_home_sublinks) 
            html_home_sublinks = list(html_home_sublinks)

            # Ende der Liste einfügen für spätere Separierung
            html_home_sublinks = html_home_sublinks + split_list

            # List Comprehension
            for item in html_home_sublinks:
                liste_html_home_sublinks.append(item)
                
            c += 1
Wie immer wäre ich sehr dankbar, wenn ihr mir Tips geben könnt, wie mein Problem gelöst werden kann. Die Suche im Internet war bisher leider nicht erfolgreich :-/

Besten Gruß,
Marc
Benutzeravatar
kbr
User
Beiträge: 1508
Registriert: Mittwoch 15. Oktober 2008, 09:27

Vielleicht hilft Dir das weiter:

Code: Alles auswählen

html_home_links = [
    'https://www.kenfm.de/impressum',
    'https://www.kenfm.de/heute-unter-gleichgesinnten',
    'ENDE_DER_LISTE',
    'https://www.br.de/in-der-welt',
    'https://www.br.de/alles-ausser-bayern',
    'ENDE_DER_LISTE'
]

separator = 'ENDE_DER_LISTE'
separated_links = []
links = []
for link in html_home_links:
    if link == separator:
        separated_links.append(links)
        links = []
    else:
        links.append(link)
        
print(separated_links[0])
print(separated_links[1])
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Danke vielmals!! Das ist genau das, was ich gesucht habe. LG
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@m.g.o.d: Ich würde ja schon vorher ansetzen, denn das Problem das Du da lösen willst, verursachst Du ja *selbst*. Wenn Du die Links nicht selbst in *eine* Liste mit einem Separator stecken würdest, bräuchtest Du sie hinterher auch nicht wieder aufteilen.

Im ersten gezeigten Code ist die ``for``-Schleife extrem schräg und verwirrend. Die iteriert über `self.stored_data_url`, aber die Laufvariable `link` wird überhaupt gar nicht verwendet. Dafür wird vor der Schleife ein `c` mit 0 initialisiert, in der Schleife als Index in `html_home_seperate` verwendet und am Ende der Schleife um 1 erhöht. Ansonsten wird `c` nicht verwendet Also ist das eigentlich eine sehr umständlich und versteckt ausgedrückte Schleife über `html_home_seperate`. Das `c` braucht man dafür nicht. (Warum überhaupt `c`? Das ist kein guter Name für einen Index.)

Ein paar von den Kommentaren sind falsch. `BeautifulSoup` „öffnet“ keine HTML-Seite sondern parst den HTML-Quelltext. Wo der Kommentar etwas vom Seperator am Ende einfügen sagt, wird gar nichts am Ende ein- oder angefügt, sondern eine neue Liste mit ``+`` erstellt. Einfacher und effizienter wäre es tatsächlich einfach nur das *eine* Element mit `append()` an die bestehende Liste anzufügen. Nach dem Kommentar „List Comprehension“ kommt gar keine, sondern eine normale Schleife. Die durch die `extend()`-Methode überflüssig würde.

Ausserdem sollten Kommentare nicht beschreiben *was* der Code macht, denn das steht da ja bereits als Code, sondern warum der Code das so macht. Sofern das nicht offensichtlich ist. Wobei in aller Regel offensichtlich ist was in der Dokumentation der Programmiersprache und der verwendeten Bibliotheken steht, denn sonst würde man ja die Dokumentation von dort noch mal im Quelltext wiederholen.

Man muss nicht jedes Zwischenergebnis (erneut) an Namen binden.

Grunddatentypen haben nichts in Namen verloren. Das ändert sich im Laufe der Programmentwicklung gerne mal, weil man beispielsweise einen spezialisierteren Datentyp verwenden will, und dann hat man irreführende, falsche Namen im Programm, oder muss die überall an den betroffenen Stellen ändern. Wenn `split_list` den Namen `separator` hätte, müsste man das nicht im Kommentar erklären. Da der Separator anscheinend später noch an anderer Stelle verwendet wird, würde ich den auch als Konstante definieren. (Beziehungsweise gar nicht erst so eine Liste mit Separatoren erstellen, wie weiter oben schon geschrieben.)

Die `html_home_sublinks` braucht man auch nicht wirklich. Man kann ziemlich einfach direkt die `liste_html_home_sublinks` um die neuen Links erweitern.

„Separate“ schreibt man mit einem „a“ nach dem „p“.

`html_home_sublinks_separate` wird definiert, aber nirgends verwendet.

Beim Separator ist der Name auch falsch. Ein Separator steht *zwischen* Dingen und separiert die. Hier steht aber ein spezieller Wert *hinter* *jedem* Satz an URLs und *beendet* die, das wäre also sprachlich ein Terminator und kein Separator.

Von dem Code bleibt am Ende das hier übrig:

Code: Alles auswählen

        html_home_sublinks = []
        for html_source in html_home_separate:
            soup = BeautifulSoup(html_source, "html.parser")
            html_home_sublinks.extend(
                set(a["href"] for a in soup("a", href=True) if a.text)
            )
            html_home_sublinks.append(TERMINATOR)
Was hier noch unschön ist, neben dem Separator, ist das die Daten nicht vollständig sind, weil man die ursprüngliche URL der Seite braucht wenn man die sicherlich mindestens teilweise relativen Links verwenden will. Zusammengehörende Daten sollten nicht in ”parallelen” Datenstrukturen vorgehalten werden. Das macht am Ende nur mehr Arbeit und ist fehleranfälliger. Das Problem existiert ja schon einen Schritt davor, denn `html_home_separate` enthält ja nur den HTML-Quelltext, ohne die Information über welche URL der abgerufen wurde.

Obwohl unwahrscheinlich: Sollte irgendwann mal eine Seite dabei sein, die "ENDE_DER_LISTE" als "href"-Attributwert hat, fällt das Programm an der Stelle böse auf die Nase. `None` würde sich vielleicht anbieten. Wobei, ich weiss nicht ob ich das schon mal erwähnt habe, diese Liste mit Separatoren/Terminatoren sowieso blöd ist, weil man dann ja irgendwo wieder Code schreiben muss um die zu trennen.

Zumindest beim gezeigten Ausschnitt ist fraglich ob das tatsächlich eine Methode ist, oder nicht nur eine Funktion die in eine Klasse gesteckt wurde.

Wenn das Kind schon in den Brunnen gefallen ist, also Listen mit Terminatoren bestehen, verwende ich ja lieber eine Bibliothek statt das Rad neu zu erfinden. Also in diesem Fall beispielweise `more_itertools.split_at()`:

Code: Alles auswählen

#!/usr/bin/env python3
from more_itertools import split_at

TERMINATOR = "ENDE_DER_LISTE"


def main():
    html_home_links = [
        "https://www.kenfm.de/impressum",
        "https://www.kenfm.de/heute-unter-gleichgesinnten",
        TERMINATOR,
        "https://www.br.de/in-der-welt",
        "https://www.br.de/alles-ausser-bayern",
        TERMINATOR,
    ]

    separated_links = list(
        split_at(html_home_links, lambda link: link == TERMINATOR)
    )
    #
    # Don't remove last group if the last `TERMINATOR` was missing.
    #
    if not separated_links[-1]:
        separated_links.pop()

    print(separated_links)


if __name__ == "__main__":
    main()
Die Behandlung eines fehlenden letzten Terminators kann man natürlich auch anders behandeln. Beispielsweise als Fehler/Ausnahme.

Alternativ könnte man auch `split_after()` verwenden, dann ist aber in jeder Gruppe am Ende noch der `TERMINATOR` drin. Ausser in der letzten, falls er vergessen wurde.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten