用 Python 從網上下載圖片和其他文件(單獨或批量下載)

商業

下面介紹如何在Python中指定Web上的圖像、ZIP、PDF或其他文件的URL,下載並保存為本地文件。

  • 通過指定 URL 下載圖像。
    • 代碼示例
    • urllib.request.urlopen()打開網址
    • 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)

它使用 os.path.basename() 從 URL 中提取文件名,並將其與 os.path.join() 指定的目錄連接以生成目標路徑。

下面分別介紹數據採集的部分和數據保存為文件的部分。

urllib.request.urlopen():打開網址

使用 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() 可以獲得的數據是一個字節串(bytes 類型)。

以 mode=’wb’ 作為第二個參數的 Open() 將數據寫入二進制。 w 表示寫入,b 表示二進制。

一個更簡單的代碼示例

嵌套語句可以一次寫入,用逗號分隔。

使用它,我們可以編寫以下內容。

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 文件等。

到目前為止的示例是下載和保存圖像文件,但由於我們只是在 Web 上打開一個文件並將其保存為本地文件,因此相同的功能可以用於其他類型的文件。

您可以通過指定 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

也有被user agent、referrer等限制訪問,導致無法下載的情況。我們不保證會下載所有文件。

很容易使用 Requests 來更改或添加請求標頭,例如用戶代理。

提取網頁上圖像的 URL。

要一次下載頁面中的所有圖像,首先提取圖像的 URL 並創建一個列表。

如果數字是連續的

如果您要下載的圖像的 URL 是一個簡單的序列號,則很容易。如果 URL 不僅是序列號,而且還具有一定的規律性,那麼按照規則製作 URL 列表比用 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} 用於 3 位零填充的序列號; {} 用於不需要填零時,{:05} 用於 5 位數字而不是 3 位數字。關於字符串str的格式化方法的更多信息,請參見下面的文章。

此外,這裡我們使用 pprint 使輸出更易於閱讀。

用美麗的湯提取

要從網頁中批量提取圖像 URL,請使用 Beautiful Soup。

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

from bs4 import BeautifulSoup

url = 'https://tw.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')
  • 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() 為每個圖像下載創建一個等待時間。單位為秒,因此在上面的示例中,導入並使用了時間模塊。

這個例子是針對圖像文件的,但其他類型的文件也可以一起下載,只要它們被列出。