在Python中从网上下载图像和其他文件(单独或成批)。

商业

下面解释了如何在Python中指定网络上的图像、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()可以获得的数据是一个字节字符串(字节类型)。

Open()的第二个参数是mode='wb',将数据写成二进制。

一个更简单的代码例子

嵌套的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,如果你想下载和保存文件,你需要指定这个URL。

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

还有一些情况是,访问受到用户代理、推荐人等的限制,使其无法下载。我们不保证所有文件都能被下载。

使用Request来改变或添加请求头,如用户代理,是很容易的。

提取网页上的图像的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://cn.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被提取出来。

其结构因网页而异,但基本上是按以下方式获得。

  • 通过指定包含你想下载的多张图片的块的类别、ID等,获得一个<img>标签对象的列表。
    • soup.find(class_='list').find_all('img')
  • 从<img>标签的src元素或data-src元素获取图片的URL。
    • 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()为每个图片下载创建一个等待时间。单位是秒,所以在上面的例子中,时间模块被导入并使用。

这个例子是针对图像文件的,但其他类型的文件也可以一起下载,只要它们被列出。