Download billeder og andre filer fra nettet i Python (enkeltvis eller i grupper)

Forretning

I det følgende forklares det, hvordan du i Python kan angive URL-adressen for et billede, en ZIP-fil, en PDF-fil eller en anden fil på nettet, downloade den og gemme den som en lokal fil.

  • Download billeder ved at angive URL'en.
    • Kodeeksempel
    • urllib.request.urlopen():Åbn URL-adressen
    • open():Skriv til en fil i binær tilstand
    • Et mere simpelt kodeeksempel
  • Download ZIP-filer, PDF-filer osv.
  • Udtræk URL-adressen til billedet på websiden.
    • Hvis der er tale om et fortløbende nummer
    • Uddrag med Beautiful Soup
  • Batch-download af flere billeder fra en liste over URL'er

Download billeder ved at angive URL'en.

Du kan kun bruge standardbiblioteket til at downloade individuelle filer ved at angive deres URL'er; der er ikke behov for yderligere installation.

Kodeeksempel

Følgende er et eksempel på en funktion, der downloader og gemmer en fil ved at angive URL- og destinationsstien og dens anvendelse. Denne kode er en smule mundret af hensyn til forklaringen. Et simpelt eksempel er givet nedenfor.

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ølgende for at angive destinationsmappen og gemme filen med URL-filnavnet

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 udtrækker filnavnet fra URL'en med os.path.basename() og sammenføjer det med den mappe, der er angivet med os.path.join(), for at generere destinationsstien.

I de følgende afsnit beskrives den del af dataindsamlingen og den del af datalagringen som en fil.

urllib.request.urlopen(): Åbn URL-adressen

Brug urllib.request.urlopen() til at åbne URL'en og hente dataene. Bemærk, at urllib.urlopen() er blevet forældet i Python 2.6 og tidligere. urllib.request.urlretrieve() er ikke blevet forældet endnu, men kan blive det i fremtiden.

Hvis du vil undgå at stoppe, når der opstår en undtagelse, skal du opfange fejlen med try og except.

I eksemplet er urllib.error importeret, og kun urllib.error.URLError er eksplicit registreret. Fejlmeddelelsen vises, når filens URL ikke findes.

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

Hvis du også ønsker at opfange undtagelser (FileNotFoundError osv.), når du gemmer lokalt, skal du gøre følgende.
(urllib.error.URLError, FileNotFoundError)

Det er også muligt at bruge tredjepartsbiblioteket Requests i stedet for standardbiblioteket urllib til at åbne url'en og hente dataene.

Skriv til en fil i binær tilstand i open()

De data, der kan hentes med urllib.request.urlopen(), er en byte-streng (bytes-type).

Open() med mode='wb' som andet argument skriver dataene som binære. w betyder skrivning og b betyder binær.

Et mere simpelt kodeeksempel

Inlejrede with-meddelelser kan skrives på én gang, adskilt af kommaer.

På denne måde kan vi skrive følgende.

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)

Download ZIP-filer, PDF-filer osv.

Eksemplerne indtil videre er eksempler på downloading og lagring af billedfiler, men da vi blot åbner en fil på nettet og gemmer den som en lokal fil, kan de samme funktioner bruges til andre typer filer.

Du kan downloade og gemme filer ved at angive URL'en.

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)

Bemærk, at den URL, der angives i denne funktion, skal være et link til selve filen.

I tilfælde af en GitHub-repository-fil har følgende URL f.eks. en pdf-udvidelse, men er faktisk en html-side. Hvis denne URL angives i funktionen ovenfor, downloades html-kilden.

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

Linket til filenheden er følgende URL, som du skal angive, hvis du ønsker at downloade og gemme filen.

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

Der er også tilfælde, hvor adgangen er begrænset af brugeragent, referrer osv., hvilket gør det umuligt at downloade. Vi garanterer ikke, at alle filer vil blive downloadet.

Det er nemt at bruge Requests til at ændre eller tilføje request headere som f.eks. brugeragent.

Udtræk URL-adressen til billedet på websiden.

Hvis du vil downloade alle billederne på en side på én gang, skal du først udtrække billedernes URL'er og oprette en liste.

Hvis der er tale om et fortløbende nummer

Hvis URL-adressen til det billede, du vil downloade, er et simpelt fortløbende nummer, er det nemt. Hvis URL'erne ikke kun er fortløbende numre, men også har en vis regelmæssighed, er det lettere at lave en liste over URL'er efter reglerne end at skrabe med Beautiful Soup (se nedenfor).

Brug listeforståelsesnotationen.

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 ovenstående eksempel bruges {:03} til et 3-cifret sekventielt nummer med nuludfyldning; {} bruges, når nuludfyldning ikke er nødvendig, og {:05} bruges til et 5-cifret nummer i stedet for 3 cifre. Du kan finde flere oplysninger om formatmetoden for streng str i følgende artikel.

Her bruger vi også pprint for at gøre det lettere at læse resultatet.

Uddrag med Beautiful Soup

Hvis du vil udtrække billed-URL'er fra websider i massevis, kan du bruge Beautiful Soup.

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

from bs4 import BeautifulSoup

url = 'https://da.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 eksemplet udvindes URL'en for miniaturebilledet på dette websted.

Strukturen varierer afhængigt af websiden, men grundlæggende opnås den på følgende måde.

  • Hent en liste over <img> tag-objekter ved at angive klassen, id osv. for den blok, der indeholder de flere billeder, du vil hente.
    • soup.find(class_='list').find_all('img')
  • Hent billedets URL-adresse fra src-elementet eller data-src-elementet i <img>-tagget.
    • img.get('data-src')

Ovenstående kodeeksempel er blot et eksempel og er ikke garanteret at virke.

Batch-download af flere billeder fra en liste over URL'er

Hvis du har en liste over URL'er, kan du bare lave den om til en for-loop og kalde funktionen til at downloade og gemme filen med den første viste URL. På grund af den midlertidige URL-liste er funktionskaldet download_image_dir() kommenteret ud her.

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

For ikke at overbelaste serveren bruger jeg time.sleep() til at skabe en ventetid for hver download af billeder. Enheden er i sekunder, så i eksemplet ovenfor er time-modulet importeret og brugt.

Eksemplet er for billedfiler, men andre typer filer kan også downloades sammen, så længe de er opført på listen.