Pobieranie obrazów i innych plików z sieci w Pythonie (pojedynczo lub partiami)

Biznes

Poniżej wyjaśniono, jak w Pythonie określić adres URL obrazu, pliku ZIP, PDF lub innego pliku z sieci, pobrać go i zapisać jako plik lokalny.

  • Pobierz obrazy, podając adres URL.
    • Przykład kodu
    • urllib.request.urlopen():Otwórz URL
    • open():Zapis do pliku w trybie binarnym
    • Prostszy przykład kodu
  • Pobieranie plików ZIP, plików PDF itp.
  • Wyodrębnij adres URL obrazu na stronie internetowej.
    • Jeśli numer jest sekwencyjny
    • Ekstrakt z Pięknej Zupy
  • Wsadowe pobieranie wielu obrazów z listy adresów URL

Pobierz obrazy, podając adres URL.

Biblioteki standardowej można używać tylko do pobierania pojedynczych plików, podając ich adresy URL; nie jest wymagana żadna dodatkowa instalacja.

Przykład kodu

Poniżej znajduje się przykład funkcji, która pobiera i zapisuje plik poprzez określenie adresu URL i ścieżki docelowej, oraz jej użycie. Ten kod jest nieco obszerny dla wyjaśnienia. Prosty przykład jest podany poniżej.

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)

Aby określić katalog docelowy i zapisać plik z nazwą pliku URL, wykonaj następujące czynności

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)

Wyodrębnia on nazwę pliku z adresu URL za pomocą os.path.basename() i łączy ją z katalogiem określonym za pomocą os.path.join() w celu wygenerowania ścieżki docelowej.

W kolejnych rozdziałach opisano część dotyczącą akwizycji danych oraz część dotyczącą zapisu danych do pliku.

urllib.request.urlopen(): Otwórz URL

Użyj urllib.request.urlopen(), aby otworzyć URL i pobrać dane. Zauważ, że urllib.urlopen() jest przestarzały w Pythonie 2.6 i wcześniejszych. urllib.request.urlretrieve() nie jest jeszcze przestarzały, ale może być w przyszłości.

Aby uniknąć zatrzymania, gdy wystąpi wyjątek, złap błąd za pomocą try i except.

W przykładzie, urllib.error jest zaimportowany i tylko urllib.error.URLError jest jawnie przechwytywany. Komunikat o błędzie zostanie wyświetlony, gdy adres URL pliku nie istnieje.

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

Jeśli chcesz również wyłapać wyjątki (FileNotFoundError, itp.) podczas zapisywania lokalnie, wykonaj następujące czynności.
(urllib.error.URLError, FileNotFoundError)

Możliwe jest również użycie biblioteki Requests innej firmy zamiast standardowej biblioteki urllib do otwarcia adresu url i uzyskania danych.

Zapis do pliku w trybie binarnym w open()

Dane, które można uzyskać za pomocą urllib.request.urlopen() to ciąg bajtów (typ bytes).

Open() z mode='wb' jako drugim argumentem zapisuje dane jako binarne. w oznacza write, a b oznacza binary.

Prostszy przykład kodu

Zagnieżdżone instrukcje with mogą być pisane jednocześnie, oddzielone przecinkami.

Korzystając z tego, możemy napisać, co następuje.

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)

Pobieranie plików ZIP, plików PDF itp.

Dotychczasowe przykłady dotyczą pobierania i zapisywania plików graficznych, ale ponieważ po prostu otwieramy plik w sieci i zapisujemy go jako plik lokalny, te same funkcje mogą być używane dla innych typów plików.

Możesz pobierać i zapisywać pliki, określając adres URL.

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)

Zauważ, że adres URL podany w tej funkcji musi być linkiem do samego pliku.

Na przykład, w przypadku pliku repozytorium GitHub, następujący adres URL ma rozszerzenie pdf, ale w rzeczywistości jest stroną html. Jeśli ten adres URL zostanie określony w powyższej funkcji, zostanie pobrane źródło html.

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

Link do encji pliku to następujący adres URL, który musisz podać, jeśli chcesz pobrać i zapisać plik.

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

Istnieją również przypadki, w których dostęp jest ograniczony przez agenta użytkownika, referrer, itp. co uniemożliwia pobranie pliku. Nie gwarantujemy, że wszystkie pliki zostaną pobrane.

Łatwo jest użyć Requests do zmiany lub dodania nagłówków żądania, takich jak agent użytkownika.

Wyodrębnij adres URL obrazu na stronie internetowej.

Aby pobrać wszystkie obrazy na stronie naraz, najpierw wyodrębnij adresy URL obrazów i utwórz listę.

Jeśli numer jest sekwencyjny

Jeśli adres URL obrazu, który chcesz pobrać jest prostym numerem sekwencyjnym, jest to łatwe. Jeśli adresy URL są nie tylko numerami sekwencyjnymi, ale także mają pewną regularność, łatwiej jest zrobić listę adresów URL zgodnie z regułami, niż skrobać z Beautiful Soup (patrz poniżej).

Stosuj notację zrozumiałą dla list.

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

W powyższym przykładzie, {:03} jest użyte dla 3-cyfrowej liczby sekwencyjnej z wypełnieniem zerowym; {} jest użyte, gdy wypełnienie zerowe nie jest konieczne, a {:05} jest użyte dla liczby 5-cyfrowej zamiast 3-cyfrowej. Więcej informacji na temat metody format w string str można znaleźć w poniższym artykule.

Ponadto, tutaj używamy pprint, aby ułatwić odczytanie danych wyjściowych.

Ekstrakt z Pięknej Zupy

Aby wyodrębnić adresy URL obrazów ze stron internetowych w dużych ilościach, użyj Beautiful Soup.

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

from bs4 import BeautifulSoup

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

W przykładzie wyodrębniony jest adres URL miniaturki obrazu tej strony.

Struktura ta różni się w zależności od strony internetowej, ale zasadniczo otrzymuje się ją w następujący sposób.

  • Uzyskaj listę obiektów tagu <img> określając klasę, id, itp. bloku zawierającego wiele obrazów, które chcesz pobrać.
    • soup.find(class_='list').find_all('img')
  • Uzyskaj adres URL obrazu z elementu src lub elementu data-src znacznika <img>.
    • img.get('data-src')

Powyższy przykładowy kod jest tylko przykładem i nie gwarantuje, że będzie działać.

Wsadowe pobieranie wielu obrazów z listy adresów URL

Jeśli masz listę adresów URL, możesz po prostu włączyć ją w pętlę for i wywołać funkcję, aby pobrać i zapisać plik z pierwszym wyświetlonym adresem URL. Ze względu na tymczasową listę URL, wywołanie funkcji pobierz_image_dir() jest tutaj wykomentowane.

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

Aby nie przeciążać serwera, używam time.sleep() do tworzenia czasu oczekiwania na pobranie każdego obrazka. Jednostka jest w sekundach, więc w powyższym przykładzie moduł time jest importowany i używany.

Przykład dotyczy plików graficznych, ale inne typy plików również mogą być pobierane razem, pod warunkiem, że są wymienione na liście.