Λήψη εικόνων και άλλων αρχείων από τον ιστό στην Python (μεμονωμένα ή σε παρτίδες)

Επιχείρηση

Παρακάτω εξηγείται πώς να καθορίσετε τη διεύθυνση URL μιας εικόνας, ενός ZIP, ενός PDF ή άλλου αρχείου στο Web στην Python, να το κατεβάσετε και να το αποθηκεύσετε ως τοπικό αρχείο.

  • Κατεβάστε εικόνες καθορίζοντας τη διεύθυνση URL.
    • Παράδειγμα κώδικα
    • urllib.request.urlopen():Άνοιγμα URL
    • open():Εγγραφή σε ένα αρχείο σε δυαδική λειτουργία
    • Ένα απλούστερο παράδειγμα κώδικα
  • Λήψη αρχείων ZIP, αρχείων PDF κ.λπ.
  • Εξάγετε τη διεύθυνση URL της εικόνας στην ιστοσελίδα.
    • Εάν ο αριθμός είναι διαδοχικός
    • Απόσπασμα με όμορφη σούπα
  • Μαζική λήψη πολλαπλών εικόνων από μια λίστα διευθύνσεων URL

Κατεβάστε εικόνες καθορίζοντας τη διεύθυνση URL.

Μπορείτε να χρησιμοποιήσετε την τυπική βιβλιοθήκη μόνο για τη λήψη μεμονωμένων αρχείων καθορίζοντας τις διευθύνσεις URL τους- δεν απαιτείται πρόσθετη εγκατάσταση.

Παράδειγμα κώδικα

Ακολουθεί παράδειγμα συνάρτησης που πραγματοποιεί λήψη και αποθήκευση ενός αρχείου καθορίζοντας τη διεύθυνση URL και τη διαδρομή προορισμού, καθώς και τη χρήση του. Αυτός ο κώδικας είναι λίγο φλύαρος για λόγους επεξήγησης. Ένα απλό παράδειγμα δίνεται παρακάτω.

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)

Για να καθορίσετε τον κατάλογο προορισμού και να αποθηκεύσετε το αρχείο με το όνομα αρχείου URL, κάντε τα εξής

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)

Εξάγει το όνομα αρχείου από τη διεύθυνση URL με την os.path.basename() και το ενώνει με τον κατάλογο που καθορίζεται με την os.path.join() για να δημιουργήσει τη διαδρομή προορισμού.

Οι ακόλουθες ενότητες περιγράφουν το μέρος της απόκτησης δεδομένων και το μέρος της αποθήκευσης δεδομένων σε αρχείο.

urllib.request.urlopen(): Άνοιγμα URL

Χρησιμοποιήστε την urllib.request.urlopen() για να ανοίξετε τη διεύθυνση URL και να ανακτήσετε τα δεδομένα. Σημειώστε ότι η urllib.urlopen() έχει καταργηθεί στην Python 2.6 και νωρίτερα. Η urllib.request.urlretrieve() δεν έχει καταργηθεί ακόμα, αλλά μπορεί να καταργηθεί στο μέλλον.

Για να αποφύγετε τη διακοπή κατά την εμφάνιση μιας εξαίρεσης, πιάστε το σφάλμα με try και except.

Στο παράδειγμα, το urllib.error εισάγεται και μόνο το urllib.error.URLError συλλαμβάνεται ρητά. Το μήνυμα σφάλματος θα εμφανίζεται όταν η διεύθυνση URL του αρχείου δεν υπάρχει.

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

Αν θέλετε επίσης να συλλαμβάνετε εξαιρέσεις (FileNotFoundError κ.λπ.) κατά την τοπική αποθήκευση, κάντε τα εξής.
(urllib.error.URLError, FileNotFoundError)

Είναι επίσης δυνατό να χρησιμοποιήσετε τη βιβλιοθήκη Requests τρίτου μέρους αντί της τυπικής βιβλιοθήκης urllib για να ανοίξετε το url και να λάβετε τα δεδομένα.

Εγγραφή σε ένα αρχείο σε δυαδική λειτουργία στην open()

Τα δεδομένα που μπορούν να ληφθούν με την urllib.request.urlopen() είναι μια συμβολοσειρά byte (τύπου bytes).

Η Open() με mode='wb' ως δεύτερο όρισμα γράφει τα δεδομένα σε δυαδική μορφή. w σημαίνει write και b σημαίνει binary.

Ένα απλούστερο παράδειγμα κώδικα

Οι εμφωλευμένες δηλώσεις with μπορούν να γραφτούν ταυτόχρονα, χωρισμένες με κόμμα.

Χρησιμοποιώντας αυτό, μπορούμε να γράψουμε τα εξής.

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, αρχείων PDF κ.λπ.

Τα μέχρι τώρα παραδείγματα αφορούν τη λήψη και την αποθήκευση αρχείων εικόνας, αλλά δεδομένου ότι απλώς ανοίγουμε ένα αρχείο στο διαδίκτυο και το αποθηκεύουμε ως τοπικό αρχείο, οι ίδιες λειτουργίες μπορούν να χρησιμοποιηθούν και για άλλους τύπους αρχείων.

Μπορείτε να κατεβάσετε και να αποθηκεύσετε αρχεία καθορίζοντας τη διεύθυνση 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)

Σημειώστε ότι η διεύθυνση URL που καθορίζεται σε αυτή τη συνάρτηση πρέπει να είναι ένας σύνδεσμος προς το ίδιο το αρχείο.

Για παράδειγμα, στην περίπτωση ενός αρχείου αποθετηρίου GitHub, η ακόλουθη διεύθυνση URL έχει επέκταση pdf, αλλά στην πραγματικότητα είναι μια σελίδα html. Εάν αυτή η διεύθυνση URL καθοριστεί στην παραπάνω συνάρτηση, θα γίνει λήψη της πηγής html.

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

Ο σύνδεσμος προς την οντότητα αρχείου είναι η ακόλουθη διεύθυνση URL, την οποία πρέπει να καθορίσετε εάν θέλετε να κατεβάσετε και να αποθηκεύσετε το αρχείο.

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

Υπάρχουν επίσης περιπτώσεις όπου η πρόσβαση περιορίζεται από τον πράκτορα χρήστη, τον παραπέμποντα κ.λπ., καθιστώντας αδύνατη τη λήψη. Δεν εγγυόμαστε ότι όλα τα αρχεία θα μεταφορτωθούν.

Είναι εύκολο να χρησιμοποιήσετε το Requests για να αλλάξετε ή να προσθέσετε επικεφαλίδες αιτήσεων, όπως ο πράκτορας χρήστη.

Εξάγετε τη διεύθυνση URL της εικόνας στην ιστοσελίδα.

Για να κατεβάσετε όλες τις εικόνες μιας σελίδας ταυτόχρονα, εξάγετε πρώτα τις διευθύνσεις URL των εικόνων και δημιουργήστε μια λίστα.

Εάν ο αριθμός είναι διαδοχικός

Αν η διεύθυνση URL της εικόνας που θέλετε να κατεβάσετε είναι ένας απλός αύξων αριθμός, είναι εύκολο. Αν οι διευθύνσεις URL δεν είναι μόνο διαδοχικοί αριθμοί αλλά έχουν και κάποια κανονικότητα, είναι ευκολότερο να φτιάξετε μια λίστα διευθύνσεων URL σύμφωνα με τους κανόνες παρά να κάνετε scraping με το Beautiful Soup (βλ. παρακάτω).

Χρησιμοποιήστε συμβολισμό κατανόησης λίστας.

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

Στο παραπάνω παράδειγμα, το {:03} χρησιμοποιείται για έναν τριψήφιο διαδοχικό αριθμό με μηδενική συμπλήρωση- το {} χρησιμοποιείται όταν η συμπλήρωση μηδενικής συμπλήρωσης δεν είναι απαραίτητη και το {:05} χρησιμοποιείται για έναν πενταψήφιο αριθμό αντί για 3 ψηφία. Για περισσότερες πληροφορίες σχετικά με τη μέθοδο μορφοποίησης της συμβολοσειράς str, ανατρέξτε στο ακόλουθο άρθρο.

Επίσης, εδώ χρησιμοποιούμε το pprint για να κάνουμε την έξοδο πιο ευανάγνωστη.

Απόσπασμα με όμορφη σούπα

Για να εξαγάγετε μαζικά τις διευθύνσεις URL εικόνων από ιστοσελίδες, χρησιμοποιήστε το Beautiful Soup.

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

from bs4 import BeautifulSoup

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

Στο παράδειγμα, εξάγεται η διεύθυνση URL της μικρογραφίας της εικόνας αυτού του ιστότοπου.

Η δομή ποικίλλει ανάλογα με την ιστοσελίδα, αλλά βασικά προκύπτει ως εξής.

  • Λήψη μιας λίστας αντικειμένων της ετικέτας <img> καθορίζοντας την κλάση, το id κ.λπ. του μπλοκ που περιέχει τις πολλαπλές εικόνες που θέλετε να κατεβάσετε.
    • soup.find(class_='list').find_all('img')
  • Λαμβάνετε τη διεύθυνση URL της εικόνας από το στοιχείο src ή το στοιχείο data-src της ετικέτας <img>.
    • img.get('data-src')

Το παραπάνω δείγμα κώδικα είναι απλώς ένα παράδειγμα και δεν εγγυάται ότι θα λειτουργήσει.

Μαζική λήψη πολλαπλών εικόνων από μια λίστα διευθύνσεων URL

Αν έχετε μια λίστα με διευθύνσεις URL, μπορείτε απλά να την μετατρέψετε σε έναν βρόχο for και να καλέσετε τη συνάρτηση για να κατεβάσετε και να αποθηκεύσετε το αρχείο με την πρώτη διεύθυνση URL που εμφανίζεται. Λόγω της προσωρινής λίστας URL, η κλήση της συνάρτησης download_image_dir() σχολιάζεται εδώ.

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

Για να μην υπερφορτώσω τον διακομιστή, χρησιμοποιώ την time.sleep() για να δημιουργήσω ένα χρόνο αναμονής για κάθε λήψη εικόνας. Η μονάδα είναι σε δευτερόλεπτα, οπότε στο παραπάνω παράδειγμα εισάγεται και χρησιμοποιείται η μονάδα time.

Το παράδειγμα αφορά αρχεία εικόνων, αλλά και άλλοι τύποι αρχείων μπορούν να μεταφορτωθούν μαζί, αρκεί να αναφέρονται.