Ladda ner bilder och andra filer från webben i Python (enskilt eller i omgångar)

Företag

Följande förklarar hur du i Python anger URL:en för en bild, ZIP-, PDF- eller annan fil på webben, laddar ner den och sparar den som en lokal fil.

  • Ladda ner bilder genom att ange webbadressen.
    • Exempel på kod
    • urllib.request.urlopen():Öppna URL
    • open():Skriva till en fil i binärt läge
    • Ett enklare kodexempel
  • Ladda ner ZIP-filer, PDF-filer osv.
  • Extrahera webbadressen till bilden på webbsidan.
    • Om numret är sekventiellt
    • Utdrag med Beautiful Soup
  • Ladda ner flera bilder i batch från en lista med URL:er

Ladda ner bilder genom att ange webbadressen.

Du kan använda standardbiblioteket endast för att hämta enskilda filer genom att ange deras URL:er; ingen ytterligare installation krävs.

Exempel på kod

Nedan följer ett exempel på en funktion som hämtar och sparar en fil genom att ange URL och destinationssökväg samt dess användning. Den här koden är lite väl utförlig för förklarings skull. Ett enkelt exempel ges nedan.

import os
import pprint
import time
import urllib.error
import urllib.request

def download_file(url, dst_path):
    try:
        with urllib.request.urlopen(url) as web_file:
            data = web_file.read()
            with open(dst_path, mode='wb') as local_file:
                local_file.write(data)
    except urllib.error.URLError as e:
        print(e)
url = 'https://www.python.org/static/img/python-logo.png'
dst_path = 'data/temp/py-logo.png'
download_file(url, dst_path)

Gör följande om du vill ange destinationskatalogen och spara filen med URL-filnamnet

def download_file_to_dir(url, dst_dir):
    download_file(url, os.path.join(dst_dir, os.path.basename(url)))

dst_dir = 'data/temp'
download_file_to_dir(url, dst_dir)

Den extraherar filnamnet från URL:en med os.path.basename() och förenar det med den katalog som anges med os.path.join() för att skapa destinationssökvägen.

Följande avsnitt beskriver den del av datainsamlingen och den del av datalagringen som sparas som en fil.

urllib.request.urlopen(): Öppna URL

Använd urllib.request.urlopen() för att öppna URL:en och hämta data. Observera att urllib.urlopen() är föråldrad i Python 2.6 och tidigare. urllib.request.urlretrieve() är inte föråldrad ännu, men kan bli det i framtiden.

Om du vill undvika att stanna när ett undantag inträffar kan du fånga upp felet med try och except.

I exemplet importeras urllib.error och endast urllib.error.URLError fångas explicit. Felmeddelandet visas när filens URL inte finns.

url_error = 'https://www.python.org/static/img/python-logo_xxx.png'
download_file_to_dir(url_error, dst_dir)
# HTTP Error 404: Not Found

Om du också vill fånga upp undantag (FileNotFoundError etc.) när du sparar lokalt gör du följande.
(urllib.error.URLError, FileNotFoundError)

Det är också möjligt att använda tredjepartsbiblioteket Requests i stället för standardbiblioteket urllib för att öppna webbadressen och hämta data.

Skriv till en fil i binärt läge i open()

De data som kan hämtas med urllib.request.urlopen() är en bytesträng (typ bytes).

Open() med mode='wb' som andra argument skriver data som binära data. w betyder skriva och b betyder binär.

Ett enklare kodexempel

Nested with-satser kan skrivas på en gång, separerade med kommatecken.

Med hjälp av detta kan vi skriva följande.

def download_file(url, dst_path):
    try:
        with urllib.request.urlopen(url) as web_file, open(dst_path, 'wb') as local_file:
            local_file.write(web_file.read())
    except urllib.error.URLError as e:
        print(e)

Ladda ner ZIP-filer, PDF-filer osv.

Exemplen hittills handlar om att ladda ner och spara bildfiler, men eftersom vi bara öppnar en fil på webben och sparar den som en lokal fil kan samma funktioner användas för andra typer av filer.

Du kan hämta och spara filer genom att ange webbadressen.

url_zip = 'https://from-locas.com/sample_header.csv.zip'
download_file_to_dir(url_zip, dst_dir)

url_xlsx = 'https://from-locas/sample.xlsx'
download_file_to_dir(url_xlsx, dst_dir)

url_pdf = 'https://from-locas/sample1.pdf'
download_file_to_dir(url_pdf, dst_dir)

Observera att den URL som anges i denna funktion måste vara en länk till själva filen.

När det gäller en GitHub-förvaringsfil har följande URL till exempel ett pdf-tillägg men är egentligen en html-sida. Om denna URL anges i funktionen ovan kommer html-källan att hämtas.

  • https://github.com/from-locals/python-snippets/blob/master/notebook/data/src/pdf/sample1.pdf

Länken till filentiteten är följande URL, som du måste ange om du vill ladda ner och spara filen.

  • https://github.com/from-locals/python-snippets/raw/master/notebook/data/src/pdf/sample1.pdf

Det finns också fall där åtkomsten begränsas av användaragent, referrer etc., vilket gör det omöjligt att ladda ner. Vi garanterar inte att alla filer kommer att laddas ner.

Det är enkelt att använda Requests för att ändra eller lägga till begärsrubriker som t.ex. användaragent.

Extrahera webbadressen till bilden på webbsidan.

Om du vill hämta alla bilder på en sida på en gång, ska du först hämta ut bildernas webbadresser och skapa en lista.

Om numret är sekventiellt

Om webbadressen till den bild du vill ladda ner är ett enkelt löpnummer är det enkelt. Om webbadresserna inte bara är löpnummer utan också har en viss regelbundenhet är det lättare att göra en lista över webbadresser enligt reglerna än att skrapa med Beautiful Soup (se nedan).

Använd en notation för att förstå en lista.

url_list = ['https://example.com/basedir/base_{:03}.jpg'.format(i) for i in range(5)]
pprint.pprint(url_list)
# ['https://example.com/basedir/base_000.jpg',
#  'https://example.com/basedir/base_001.jpg',
#  'https://example.com/basedir/base_002.jpg',
#  'https://example.com/basedir/base_003.jpg',
#  'https://example.com/basedir/base_004.jpg']

I exemplet ovan används {:03} för ett 3-siffrigt sekventiellt nummer som fylls med noll; {} används när nollfyllning inte är nödvändig och {:05} används för ett 5-siffrigt nummer i stället för 3 siffror. Mer information om formatmetoden för sträng str finns i följande artikel.

Här använder vi också pprint för att göra resultatet lättare att läsa.

Utdrag med Beautiful Soup

Använd Beautiful Soup för att extrahera bild-URL:er från webbsidor i stora mängder.

import os
import time
import urllib.error
import urllib.request

from bs4 import BeautifulSoup

url = 'https://sv.from-locals.com/'
ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) '\
     'AppleWebKit/537.36 (KHTML, like Gecko) '\
     'Chrome/55.0.2883.95 Safari/537.36 '

req = urllib.request.Request(url, headers={'User-Agent': ua})
html = urllib.request.urlopen(req)

soup = BeautifulSoup(html, "html.parser")

url_list = [img.get('data-src') for img in soup.find(class_='list').find_all('img')]

I exemplet extraheras URL:en för miniatyrbilden på denna webbplats.

Strukturen varierar beroende på vilken webbsida det gäller, men i princip erhålls den på följande sätt.

  • Hämta en lista med <img>-taggobjekt genom att ange klass, id osv. för blocket som innehåller flera bilder som du vill hämta.
    • soup.find(class_='list').find_all('img')
  • Hämta bildens URL från src-elementet eller data-src-elementet i <img>-taggens <img>-tagg.
    • img.get('data-src')

Ovanstående exempelkod är bara ett exempel och det är inte garanterat att den fungerar.

Ladda ner flera bilder i batch från en lista med URL:er

Om du har en lista med webbadresser kan du bara göra en for-slinga och anropa funktionen för att ladda ner och spara filen med den första webbadressen som visas. På grund av den tillfälliga URL-listan kommenteras funktionsanropet download_image_dir() här.

download_dir = 'data/temp'
sleep_time_sec = 1

for url in url_list:
    print(url)
#     download_file_dir(url, download_dir)
    time.sleep(sleep_time_sec)
# https://example.com/basedir/base_000.jpg
# https://example.com/basedir/base_001.jpg
# https://example.com/basedir/base_002.jpg
# https://example.com/basedir/base_003.jpg
# https://example.com/basedir/base_004.jpg

För att inte överbelasta servern använder jag time.sleep() för att skapa en väntetid för varje bildnedladdning. Enheten är i sekunder, så i exemplet ovan importeras och används time-modulen.

Exemplet gäller bildfiler, men även andra typer av filer kan laddas ner tillsammans, så länge de finns med i listan.