2

Python Tutorial: Dateien von URL’s herunterladen

In diesem kurzen Python-Tutorial geht es darum, wie du mit Python Dateien von URLs schnell und schmerzlos herunterladen kannst.

Ich werde dafür die beliebte Bibliothek Requests verwenden. Ich werde dir Methoden zeigen, um Binärdateien von URLs herunterzuladen und ihre Dateinamen festzulegen.

Requests Bibliothek installieren

Wenn du die requests Bibliothek noch nicht installiert hast, dann öffne deine Kommandozeile. Führe dort folgenden Befehl aus:

pip install requests

Sobald die requests-Bibliothek installiert ist, können wir damit arbeiten.

Bilder mit Python herunterladen

Fangen wir Schritt-für-Schritt an, um mit Hilfe von Python-Requests Dateien herunterzuladen:

import requests

url = 'https://www.python.org/favicon.ico'
r = requests.get(url, allow_redirects=True)
open('meinfavicon.ico', 'wb').write(r.content)

Der oben gezeigte Code wird das Favicon (Bild) von https://www.python.org/favicon.ico herunterladen und als "meinfavicon.ico" abspeichern.

Geht es auch mit anderen Medien?

Videos mit Python herunterladen (z.B. YouTube)

Jetzt schauen wir uns folgenden YouTube-Link an: https://www.youtube.com/watch?v=TTAfn22UF70

Was glaubst du, was passiert, wenn wir den YouTube-Link in unseren Code einfügen?

Wenn du glaubst, dass er einfach die HTML-Seite herunterlädt, ohne die Video-Datei zu berücksichtigen, liegst du goldrichtig!

Wenn ein Link (bzw. eine URL) auf eine Webseite, anstatt auf eine Binärdatei (wie z.B. ein MP4-Video), dann wird einfach nur die HTML-Seite heruntergeladen. Doch wie kommen wir jetzt an die Videodatei? Um das zu lösen, habe ich mir die Header der URL angeschaut. Üblicherweise haben Header einen "Content-Type" Parameter. Dieser Parameter sagt uns auf welchen Datentypen die URL verlinkt. Hier ist der Code:

r = requests.get(url, allow_redirects=True)
print(r.headers.get('content-type'))

Probiere den o.g. Code einfach aus. Es ist nicht wirklich optimal, da immer noch die HTML-Datei heruntergeladen wird, um den Header herauszufinden.

Wenn es sich um eine große Datei (wie bei einem langen Video) handelt, dann verschwendest du eine Menge Datenvolumen.

Doch es gibt einen eleganteren Weg an den Header zu kommen, ohne die HTML-Datei herunterladen zu müssen. Die Lösung dafür habe ich in der Request-Dokumentation gefunden. Hier der Code:

import requests

def herunterladen(url):

    # Pruefe den content-type der URL 
    x = requests.head(url, allow_redirects=True)
    header = x.headers
    content_type = header.get('content-type')
    if 'text' in content_type.lower():
        return False
    if 'html' in content_type.lower():
        return False
    return True

print(herunterladen('https://www.youtube.com/watch?v=TTAfn22UF70')) # Gibt "False" aus
print(herunterladen('https://www.python.org/favicon.ico')) # Gibt "True" aus

Es bietet sich noch an, die Dateigröße zu beschränken. Dafür benutzen wir die "Content-Length" im Header.

content_length = header.get('content-length', None)
if content_length and content_length > 2e8:  # Das sind ca. 200 MB
  return False

Super! Mit unserer Funktion können wir URL's überspringen, die nicht auf Medien verlinken.

Jetzt brauchen wir noch den Dateinamen der URL, der auf unsere Medien verlinkt.

Dateinamen aus URL's auslesen / parsen mit Python

Wir können mit Python den Dateinamen aus einer URL parsen.

Als Beispiel nehmen wir unser LerneProgrammieren-Logo: https://lerneprogrammieren.de/wp-content/uploads/Logo-blau3.png

Die einfachste Lösung dafür ist es, alle Zeichen nach dem letzten Slash (/) zu parsen, also alles hinter uploads/.

url = 'https://lerneprogrammieren.de/wp-content/uploads/Logo-blau3.png' 
if url.find('/'):
    print(url.rsplit('/', 1)[1])

In einigen Fällen wird der Dateiname durch diesen Code korrekt erfasst. Es gibt jedoch Fälle, in denen die Informationen zum Dateinamen nicht in der URL stehen.

Beispiel: http://test.com/download

Wie können wir dieses Problem lösen? Der Header "Content-Disposition" kommt hier zur Rettung. So ruft man den Dateinamen elegant ab:

import requests
import re

def dateiname_content_dispo(cd):
    if not cd:
        return None
    dname = re.findall('dateiname=(.+)', cd)
    if len(dname) == 0:
        return None
    return dname[0]

url = 'https://lerneprogrammieren.de/wp-content/uploads/Logo-blau3.png'
req = requests.get(url, allow_redirects=True)
dateiname = dateiname_content_dispo(req.headers.get('content-disposition'))
open(dateiname, 'wb').write(req.content)

Die obige Methode funktioniert in den meisten Fällen um den Dateinamen aus dem "Content-Disposition" Header auszulesen. So kannst du jedenfalls ganz einfach Dateien in Python 3 herunterladen.

(Tipp: Wenn du Python lernen möchtest, schau dir auch unseren ausführlichen Ratgeber an)

Teste den fertigen Code selbst aus und sag mir, was du davon hältst. Vielleicht hast du ja auch noch eine Idee, den Python-Code zu verbessern?

  • 16. März 2020
Click Here to Leave a Comment Below 2 comments
Clemens - 7. April 2020

Hallo,

danke für deine Webseite – alles sehr interessant.
Ich habe versucht, die Bibliothek „requests“ zu benutzen und musste nach mehrmaligem Versuchen feststellen, dass man die Bibliothek zuerst installieren muss.
Darf ich als Verbesserungsvorschlag anbringen, dass es schön wäre, wenn es einen Hinweis drauf gibt, dass zuerst eine Installation notwendig ist und wie man das macht? Gerade für uns Anfänger ist es sonst echt schwierig den Fehler zu finden, wenn etwas nicht geht…

Nun aber zu meinem Hauptanliegen:
Ich bekomme das Script mit dem automatischen Filenamen nicht zum laufen und werde auch aus dem ausgegebenen Fehler nicht recht schlau:

============ RESTART: D:/Eigene Dateien/Clemens/Python_36/Url.py ============
Traceback (most recent call last):
File „D:/Eigene Dateien/Clemens/Python_36/Url.py“, line 15, in
open(dateiname, ‚wb‘).write(req.content)
TypeError: expected str, bytes or os.PathLike object, not NoneType

Kann mir da jemand weiterhelfen? Sorry aber ich bin noch Anfänger…

Danke im Voraus!

Reply
Moabiter - 13. Juni 2022

@Clemens:
Ich kann Deine Fehlermeldung nachvollziehen. Der Request gibt nur ein OKAY (response 200) zurück, jedoch nicht den Dateinamen.
Da im header aber kein ‚content-disposition‘ enthalten ist, und die Fehlermeldung (z.B. kein ‚content-disposition‘ enthalten) vorhanden ist, gitb es diese Fehlermeldung.

Gruß Moabiter

Reply

Leave a Reply:

🎄 Weihnachts-Special: 15% Rabatt auf ALLE Kurse - Achtung: Nur noch wenige Tage ⏰ Deals sichern
+