Képek és egyéb fájlok letöltése az internetről Pythonban (egyenként vagy kötegekben)

Üzleti

Az alábbiakban ismertetjük, hogyan lehet Pythonban megadni egy kép, ZIP, PDF vagy más webes fájl URL-címét, letölteni és helyi fájlként elmenteni.

  • Képek letöltése az URL megadásával.
    • Kódpélda
    • urllib.request.urlopen():URL megnyitása
    • open():Írás fájlba bináris módban
    • Egy egyszerűbb kódpélda
  • ZIP-fájlok, PDF-fájlok stb. letöltése.
  • A weboldalon található kép URL-címének kivonása.
    • Ha a szám folyamatos
    • Kivonat gyönyörű levessel
  • Több kép kötegelt letöltése URL-ek listájáról

Képek letöltése az URL megadásával.

A szabványos könyvtárat csak az egyes fájlok letöltésére használhatja azok URL-címének megadásával; további telepítésre nincs szükség.

Kódpélda

A következő példa egy olyan függvényre, amely egy fájlt tölt le és ment el az URL és a cél elérési útvonalának megadásával, valamint annak felhasználásával. Ez a kód a magyarázat kedvéért kissé bőbeszédű. Az alábbiakban egy egyszerű példát mutatunk be.

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)

A célkönyvtár megadásához és a fájl URL-fájlnévvel történő mentéséhez tegye a következőket

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)

Az os.path.basename() segítségével kivonja a fájlnevet az URL-ből, majd az os.path.join() segítségével megadott könyvtárral egyesíti azt a cél elérési útvonal létrehozásához.

A következő szakaszok az adatgyűjtés és az adatok fájlba mentésének részét ismertetik.

urllib.request.urlopen(): URL megnyitása

Az urllib.request.urlopen() segítségével nyissa meg az URL-címet és kérje le az adatokat. Vegyük figyelembe, hogy az urllib.urlopen() a Python 2.6 és korábbi verziókban már elavult. Az urllib.request.urlretrieve() még nem elavult, de a jövőben lehet, hogy az lesz.

Hogy elkerülje a megállást egy kivétel bekövetkezésekor, a hibát próbával és kivételekkel kell elkapni.

A példában az urllib.error importálva van, és csak az urllib.error.URLError van explicit módon rögzítve. A hibaüzenet akkor jelenik meg, ha a fájl URL címe nem létezik.

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

Ha a helyi mentés során is szeretne kivételeket (FileNotFoundError stb.) fogni, tegye a következőket.
(urllib.error.URLError, FileNotFoundError)

Lehetőség van arra is, hogy a szabványos urllib könyvtár helyett a harmadik fél Requests könyvtárát használjuk az url megnyitásához és az adatok kinyeréséhez.

Fájlba írás bináris módban az open() függvényben

Az urllib.request.urlopen() segítségével megszerezhető adatok egy byte string (bytes típusú).

Az Open() a mode='wb' második argumentummal bináris formában írja az adatokat. w azt jelenti, hogy write, b pedig bináris.

Egy egyszerűbb kódpélda

A beágyazott with utasításokat egyszerre lehet írni, vesszővel elválasztva.

Ezt felhasználva a következőket írhatjuk.

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)

ZIP-fájlok, PDF-fájlok stb. letöltése.

Az eddigi példák a képfájlok letöltésére és mentésére vonatkoznak, de mivel egyszerűen csak megnyitunk egy fájlt a weben, és helyi fájlként mentjük, ugyanazok a funkciók más típusú fájlokra is használhatók.

Az URL megadásával tölthet le és menthet le fájlokat.

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)

Vegye figyelembe, hogy a funkcióban megadott URL-címnek magára a fájlra mutató hivatkozásnak kell lennie.

Például egy GitHub-tárhely fájl esetében a következő URL-címnek pdf kiterjesztése van, de valójában egy html oldal. Ha ezt az URL-t adja meg a fenti függvényben, akkor a html forrás lesz letöltve.

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

A fájlegységre mutató link a következő URL, amelyet meg kell adnia, ha le szeretné tölteni és menteni a fájlt.

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

Vannak olyan esetek is, amikor a hozzáférést felhasználói ügynök, hivatkozó stb. alapján korlátozzák, és így lehetetlenné teszik a letöltést. Nem garantáljuk, hogy minden fájl letöltésre kerül.

A Requests segítségével könnyen módosíthatja vagy hozzáadhatja a kérés fejléceit, például a felhasználói ügynököt.

A weboldalon található kép URL-címének kivonása.

Ha egyszerre szeretné letölteni az összes képet egy oldalon, először vegye ki a képek URL-címét, és hozzon létre egy listát.

Ha a szám folyamatos

Ha a letölteni kívánt kép URL-címe egy egyszerű sorszám, akkor könnyű dolga van. Ha az URL-ek nem csak sorszámok, hanem valamilyen szabályszerűséggel is rendelkeznek, akkor a Beautiful Soup (lásd alább) segítségével történő kaparás helyett egyszerűbb a szabályok szerinti URL-ek listáját összeállítani.

Használja a lista megértésének jelölését.

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']

A fenti példában a {:03} a 3 számjegyű, nullával kitöltött sorszámot jelöli; a {} akkor használatos, ha a nullával való kitöltés nem szükséges, a {:05} pedig a 3 számjegy helyett 5 számjegyű számot jelöli. A string str formázási módszeréről bővebben a következő cikkben olvashat.

Itt is a pprintet használjuk, hogy a kimenet könnyebben olvasható legyen.

Kivonat gyönyörű levessel

A Beautiful Soup segítségével nagy mennyiségben kinyerheti a kép-URL-eket weboldalakról.

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

from bs4 import BeautifulSoup

url = 'https://hu.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')]

A példában a weboldal miniatűr képének URL-címe kerül kinyerésre.

A szerkezet a weboldaltól függően változik, de alapvetően a következőképpen alakul.

  • A <img> tag objektumok listájának lekérése a letölteni kívánt több képet tartalmazó blokk osztályának, azonosítójának stb. megadásával.
    • soup.find(class_='list').find_all('img')
  • A kép URL-címét a <img> tag src eleméből vagy data-src eleméből kapja meg.
    • img.get('data-src')

A fenti mintakód csak egy példa, és nem garantáltan működik.

Több kép kötegelt letöltése URL-ek listájáról

Ha van egy URL-ek listája, akkor csak egy for ciklusba kell kapcsolni, és meghívni a függvényt, hogy letöltse és mentse a fájlt az első megjelenő URL-címmel. Az ideiglenes URL-lista miatt a download_image_dir() függvényhívás itt ki van kommentálva.

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

Annak érdekében, hogy ne terheljem túl a szervert, time.sleep() segítségével minden egyes képletöltésnél létrehozok egy várakozási időt. Az egység másodpercben van megadva, ezért a fenti példában a time modult importáljuk és használjuk.

A példa a képfájlokra vonatkozik, de más típusú fájlok is letölthetők együtt, ha szerepelnek a listában.