Vergleich von Hyperlinks bzw. abändern falscher Links

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
Snakepit
User
Beiträge: 12
Registriert: Samstag 12. Juli 2014, 20:08

Hallo liebe Boradmitglieder!

Ich lese aktuell mit BeautifulSoup4 und Requests HTML Seiten aus und hole mir hier dann Bilder und Dokumente.
Dies klappt auch soweit ganz gut! Manche der Hyperlinks haben aber einen falschen Aufbau. Diese sind mit "//subdomain.domanin.de" angegeben.
Diese werden aber nicht verarbeitet, da anscheinend "requests" nichts damit anfangen kann.

Jetzt würde ich gern die zwei "//" am Anfang entfernen, oder mit "https:" ergänzen.

Theoretisch könnte ich jetzt ja den String zerlegen und prüfen, ob die ersten zwei Zeichen davon // entsprechen, jedoch soll das Programm auch vielleicht mal erweitert werden und dann können hier noch komplett andere Zeichenfolgen stehen.

Gibt es eine einfache Möglichkeit, bestimmte Teilbereiche von Strings zu vergleichen, ohne den Umweg über die Zerlegung des Strings zu gehen?
Vielleicht hab ich nur die falschen Suchbegriffe, aber aktuell steh ich da vor einer Mauer.

Folgendes wäre erlaubt:
http://subdomain.domain.de
http://domain.de
https://subdomain.domain.de
https://domain.de

Verboten bzw. abzuändern:
//subdomain.domain.de
//domain.de

Ich hoffe ich bin nicht einfach zu faul und es muss wirklich so gemacht werden!? Wäre super, wenn jemand eine Idee hat.
Später würde ich gerne eine Indexierung bestimmter Domains machen. Also nur alle Links behalten und verfolgen, die auf die jeweilige Domain laufen. Alle externen Links sollen verworfen, oder in einer externen Liste gespeichert werden.

Vielen Dank schon mal an alle, die helfen können!
Benutzeravatar
sparrow
User
Beiträge: 4184
Registriert: Freitag 17. April 2009, 10:28

Ein URL für eine Web-Ressource beginnend mit // ist nicht "falsch" sondern laut Spezifikationen gültig.
Webbrowser setzen als Protokoll, soweit ich weiß, das Protokoll, mit dem die Seite aufgerufen wurde.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Hyperlinks gibt es in relativ und absolut.
Und relative Links mußt Du mit der Dokumenten-URL zu absoluten Pfade wandeln, bevor Du sie mit requests verwenden kannst.
Dazu benutzt man urllib.parse.join.

Code: Alles auswählen

urllib.parse.urljoin('https://abc.de/ein/pfad/a.html', '//bilder.abc.de/b.gif')                                                                                                
# 'https://bilder.abc.de/b.gif'
Snakepit
User
Beiträge: 12
Registriert: Samstag 12. Juli 2014, 20:08

Danke schon mal für die Hilfe!
urllib.parse.join schau ich mir auf jeden Fall mal an.

Aktuell habe ich mir mit try/except beholfen. Die URL die mit // anfängt und nicht aufgelöst werden konnte, soll sowieso erst einmal ignoriert werden.
Ist jetzt sicherlich nicht sauber umgesetzt, aber es funktioniert schon mal ;-).

Wünsche euch noch einen schönen Sonntag!
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Man könnte das auch mit urlparse() lösen:

Code: Alles auswählen

from urllib.parse import urlparse

def get_url(link, domain="", default_protocol="https"):
    replacements = {}
    parsed = urlparse(link)
    if not parsed.scheme:
        replacements["scheme"] = default_protocol
    if not parsed.netloc:
        replacements["netloc"] = domain
    return parsed._replace(**replacements).geturl()

def main():
    print(get_url("//subdomain.domain.de"))
    base = "www.test.de"
    for link in ("aktuelles.php", "fotos.php", "about.html"):
        print(get_url(link, base))

if __name__ == "__main__":
    main()
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: warum sollte man das so kompliziert machen, wenn es mit urljoin schon eine fertige Funktion gibt, die auch alle anderen Fälle von relativen URLs abdeckt?
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Snakepit hat geschrieben: Freitag 16. April 2021, 17:37 Später würde ich gerne eine Indexierung bestimmter Domains machen. Also nur alle Links behalten und verfolgen, die auf die jeweilige Domain laufen. Alle externen Links sollen verworfen, oder in einer externen Liste gespeichert werden.
Hier mit dem "Same Origin"-Ansatz:

Code: Alles auswählen

from urllib.parse import urlparse

def get_url(link, origin="", default_protocol="https"):
    replacements = {}
    parsed = urlparse(link)
    if origin and parsed.netloc not in ("", origin):
        raise ValueError(f"External URL: {link}")
    if not parsed.scheme:
        replacements["scheme"] = default_protocol
    if not parsed.netloc:
        replacements["netloc"] = origin
    return parsed._replace(**replacements).geturl()

def main():
    print(get_url("//subdomain.domain.de"))
    origin = "www.test.de"
    for link in ("aktuelles.php", "fotos.php", "about.html"):
        print(get_url(link, origin))
    print(get_url("https://www.spam.com/img56744.jpg", origin))

if __name__ == "__main__":
    main()
Anstelle des ValueErrors würde man im tatsächlichen Programm wahrscheinlich eine eigene Exception verwenden und bei deren Auftreten entsprechend den externen Link verwerfen bzw gesondert speichern. Das wirst du aber sicher auch alleine hinkriegen. ;)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sirius3 hat geschrieben: Sonntag 18. April 2021, 11:02 @snafu: warum sollte man das so kompliziert machen, wenn es mit urljoin schon eine fertige Funktion gibt, die auch alle anderen Fälle von relativen URLs abdeckt?
Weil hier mehr gefordert ist als das stumpfe Zusammensetzen von URL-Fragmenten. Man muss sich ja erstmal die Bestandteile näher anschauen und dann ggf Ersetzungen vornehmen. Letzteres wäre auch mit urljoin() möglich, aber dann würde jemand anders wahrscheinlich fragen, warum extra urljoin() bemüht wird, wenn man schon den geparsten Zwischenstand hat...
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum sollte man eine Funktion benutzen, die gut getestet ist und alle Fälle abdeckt?

Code: Alles auswählen

from urllib.parse import urlparse, urljoin

def get_links():
    """ hier ein paar Beispiellinks """
    return [
        "http://test.de/static/bilder/hund.jpg",
        "//example.com/external/url.html",
        "/same_url/index.html",
        "../relative/index.html",
    ]

def main():
    document_url = "https://test.de/pfad/zu/document.html"
    origin = urlparse(document_url).netloc
    for link in get_links():
        absolute_link = urljoin(document_url, link)
        is_external = urlparse(absolute_link).netloc != origin
        print(absolute_link, "external" if is_external else "internal")

if __name__ == "__main__":
    main()
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Okay, du hast mich überzeugt: urljoin() tut, was es soll. :)
Antworten