Downloading and processing files and images

Scrapy提供了可重用的项目管道,用于下载附加到特定项目的文件(例如,当您抓取产品并且还想在本地下载其图像时). 这些管道共享一些功能和结构(我们将它们称为媒体管道),但是通常您将使用"文件管道"或"图像管道".

这两个管道都实现了以下功能:

  • 避免重新下载最近下载的媒体
  • 指定存储媒体的位置(文件系统目录,Amazon S3存储桶,Google Cloud Storage存储桶)

图片管道具有一些额外的功能来处理图片:

  • 将所有下载的图像转换为通用格式(JPG)和模式(RGB)
  • 缩图产生
  • 检查图像的宽度/高度,以确保它们满足最小限制

管道还保留了那些当前计划下载的媒体URL的内部队列,并将到达的包含相同媒体的响应连接到该队列. 当多个项目共享同一媒体时,这避免了多次下载同一媒体.

Using the Files Pipeline

使用FilesPipeline时,典型的工作流程如下:

  1. 在Spider中,您file_urls一个项目并将所需的URL放入file_urls字段中.
  2. 物料从蜘蛛网返回并进入物料管道.
  3. 当项目到达FilesPipeline ,将使用标准的Scrapy调度程序和下载程序(这意味着调度程序和下载程序中间件被重用)来调度file_urls字段中的URL的下载,但具有较高的优先级,请先对其进行处理,然后再抓取其他页面. 该项目在该特定管道阶段保持"锁定"状态,直到文件完成下载(或由于某些原因失败)为止.
  4. 下载files ,结果中将填充另一个字段( files ). 该字段将包含字典列表,其中包含有关下载文件的信息,例如下载路径,原始file_urls url(从file_urls字段获取)以及文件校验和. "文件"字段列表中的files将保留与原始file_urls字段相同的顺序. 如果某些文件下载失败,将记录错误,并且files字段中将不存在该files .

Using the Images Pipeline

使用ImagesPipeline与使用FilesPipeline非常相似,除了使用的默认字段名称有所不同:您将image_urls用作项目的图像URL,它将填充images字段以获取有关已下载图像的信息.

对图像文件使用ImagesPipeline的优点是,您可以配置一些额外的功能,例如生成缩略图和根据图像的大小过滤图像.

图片管道使用Pillow将图片缩略图化和标准化为JPEG / RGB格式,因此您需要安装此库才能使用它. Python Imaging Library (PIL)在大多数情况下也应该工作,但是已知在某些设置中会引起麻烦,因此我们建议使用Pillow而不是PIL.

Enabling your Media Pipeline

要启用媒体管道,必须首先将其添加到项目ITEM_PIPELINES设置中.

对于图像管道,请使用:

ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}

对于文件管道,请使用:

ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}

Note

您也可以同时使用"文件"和"图像"管道.

然后,将目标存储设置配置为将用于存储下载的图像的有效值. 否则,即使将管道包括在ITEM_PIPELINES设置中,管道也将保持禁用ITEM_PIPELINES .

对于文件管道,设置FILES_STORE设置:

FILES_STORE = '/path/to/valid/dir'

对于图像管道,设置IMAGES_STORE设置:

IMAGES_STORE = '/path/to/valid/dir'

Supported Storage

文件系统是当前唯一受官方支持的存储,但是还支持在Amazon S3Google Cloud Storage中存储文件.

File system storage

使用文件名称的URL的SHA1哈希存储文件.

例如,以下图像URL:

http://www.example.com/image.jpg

Whose SHA1 hash is:

3afec3b4765f8f0a07b78f98c07b83f013567a0a

将下载并存储在以下文件中:

<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg

Where:

  • <IMAGES_STORE>是在IMAGES_STORE设置中为图像管道定义的目录.
  • full是一个子目录,用于将完整图像与缩略图(如果使用)分开. 有关更多信息,请参见为图像生成缩略图 .

Amazon S3 storage

FILES_STOREIMAGES_STORE可以代表Amazon S3存储桶. Scrapy将自动将文件上传到存储桶.

例如,这是一个有效的IMAGES_STORE值:

IMAGES_STORE = 's3://bucket/images'

您可以修改用于存储文件的访问控制列表(ACL)策略,该策略由FILES_STORE_S3_ACLIMAGES_STORE_S3_ACL设置定义. 默认情况下,ACL设置为private . 要使文件公开可用,请使用public-read策略:

IMAGES_STORE_S3_ACL = 'public-read'

有关更多信息,请参阅Amazon S3开发人员指南中的固定ACL .

由于Scrapy内部使用boto / botocore您也可以使用其他类似S3的存储. 储存像自托管Minios3.scality . 您需要做的就是在Scrapy设置中设置终结点选项:

AWS_ENDPOINT_URL = 'http://minio.example.com:9000'

对于自托管,您还可能会觉得不需要使用SSL或不验证SSL连接:

AWS_USE_SSL = False # or True (None by default)
AWS_VERIFY = False # or True (None by default)

Google Cloud Storage

FILES_STOREIMAGES_STORE可以代表Google Cloud Storage存储桶. Scrapy将自动将文件上传到存储桶. (需要google-cloud-storage

例如,以下是有效的IMAGES_STOREGCS_PROJECT_ID设置:

IMAGES_STORE = 'gs://bucket/images/'
GCS_PROJECT_ID = 'project_id'

有关身份验证的信息,请参阅本文档 .

您可以修改用于存储文件的访问控制列表(ACL)策略,该策略由FILES_STORE_GCS_ACLIMAGES_STORE_GCS_ACL设置定义. 默认情况下,ACL设置为'' (空字符串),这意味着Cloud Storage将存储桶的默认对象ACL应用于该对象. 要使文件公开可用,请使用publicRead策略:

IMAGES_STORE_GCS_ACL = 'publicRead'

有关更多信息,请参阅《 Google Cloud Platform开发人员指南》中的预定义ACL .

Usage example

为了首先使用媒体管道,请启用它 .

然后,如果蜘蛛程序用URLs关键字返回dict( file_urlsimage_urls ,分别用于Files或Images Pipeline),则管线会将结果放在相应的key( filesimages )下.

如果您更喜欢使用Item ,则使用必要的字段定义一个自定义项目,例如在此示例中的图像管道:

import scrapy

class MyItem(scrapy.Item):

    # ... other item fields ...
    image_urls = scrapy.Field()
    images = scrapy.Field()

如果要对URL关键字或结果关键字使用另一个字段名,则也可以覆盖它.

对于文件管道,设置FILES_URLS_FIELD和/或FILES_RESULT_FIELD设置:

FILES_URLS_FIELD = 'field_name_for_your_files_urls'
FILES_RESULT_FIELD = 'field_name_for_your_processed_files'

对于图像管道,设置IMAGES_URLS_FIELD和/或IMAGES_RESULT_FIELD设置:

IMAGES_URLS_FIELD = 'field_name_for_your_images_urls'
IMAGES_RESULT_FIELD = 'field_name_for_your_processed_images'

如果您需要更复杂的东西并想覆盖自定义管道的行为,请参阅扩展媒体管道 .

如果您有多个继承自ImagePipeline的图像管道,并且希望在不同的管道中具有不同的设置,则可以设置设置键,并以管道类的大写名称开头. 例如,如果您的管道名为MyPipeline,并且您想要自定义IMAGES_URLS_FIELD,则可以定义设置MYPIPELINE_IMAGES_URLS_FIELD,然后将使用您的自定义设置.

Additional features

File expiration

图像管道避免下载最近下载的文件. 要调整此保留延迟,请使用FILES_EXPIRES设置(或在IMAGES_EXPIRES ,对于"图像管道"而言),该设置指定延迟天数:

# 120 days of delay for files expiration
FILES_EXPIRES = 120

# 30 days of delay for images expiration
IMAGES_EXPIRES = 30

两种设置的默认值为90天.

如果您的管道具有子类FilesPipeline的子类,并且您希望对其进行其他设置,则可以设置设置键,并以大写类名开头. 例如,给定名为MyPipeline的管道类,您可以设置设置键:

MYPIPELINE_FILES_EXPIRES = 180

并且管道类MyPipeline的到期时间将设置为180.

Thumbnail generation for images

图片管道可以自动创建下载图片的缩略图.

为了使用此功能,必须将IMAGES_THUMBS设置为字典,其中键是缩略图名称,值是尺寸.

For example:

IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
}

使用此功能时,图像管道会使用以下格式创建每个指定大小的缩略图:

<IMAGES_STORE>/thumbs/<size_name>/<image_id>.jpg

Where:

  • <size_name>IMAGES_THUMBS字典键( smallbig等)中指定的一个
  • <image_id>是图像URL的SHA1哈希

Example of image files stored using small and big thumbnail names:

<IMAGES_STORE>/full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/small/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/big/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg

第一个是从站点下载的完整图像.

Filtering out small images

使用图像管道时,可以通过在IMAGES_MIN_HEIGHTIMAGES_MIN_WIDTH设置中指定最小允许尺寸来删除太小的图像.

例如:

IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

Note

大小限制根本不影响缩略图的生成.

可以仅设置一个大小约束,也可以设置两个约束. 同时设置它们时,将仅保存同时满足两个最小尺寸的图像. 对于上面的示例,大小为(105 x 105)或(105 x 200)或(200 x 105)的图像都将被丢弃,因为至少一个尺寸比约束要短.

By default, there are no size constraints, so all images are processed.

Allowing redirections

默认情况下,媒体管道会忽略重定向,即,对媒体文件URL请求的HTTP重定向将意味着媒体下载被视为失败.

要处理媒体重定向,请将此设置设置为True

MEDIA_ALLOW_REDIRECTS = True

Extending the Media Pipelines

在此处查看可在自定义文件管道中覆盖的方法:

class scrapy.pipelines.files.FilesPipeline
file_path(request, response, info)

每个下载的项目调用一次此方法. 它返回源自指定response的文件的下载路径.

除了response ,此方法还接收原始requestinfo .

您可以重写此方法以自定义每个文件的下载路径.

例如,如果文件URL以常规路径结尾(例如https://example.com/a/b/c/foo.png ),则可以使用以下方法将所有文件及其原始文件名下载到files夹中(例如files/foo.png ):

import os
from urllib.parse import urlparse

from scrapy.pipelines.files import FilesPipeline

class MyFilesPipeline(FilesPipeline):

    def file_path(self, request, response, info):
        return 'files/' + os.path.basename(urlparse(request.url).path)

默认情况下, file_path()方法返回full/<request URL hash>.<extension> .

get_media_requests(item, info)

从工作流程中可以看出,管道将获取图像的URL,以便从项目中下载. 为此,您可以覆盖get_media_requests()方法并为每个文件URL返回一个请求:

def get_media_requests(self, item, info):
    for file_url in item['file_urls']:
        yield scrapy.Request(file_url)

这些请求将由管道处理,完成下载后,结果将作为2元素元组的列表发送到item_completed()方法. 每个元组将包含(success, file_info_or_error) ,其中:

  • success是一个布尔值,如果图像下载成功,则为True如果由于某种原因失败,则为False
  • file_info_or_error是包含以下键(如果成功为True )的dict,或者是有问题的Twisted Failure .

保证item_completed()接收到的元组列表保持与get_media_requests()方法返回的请求的顺序相同.

Here’s a typical value of the results argument:

[(True,
  {'checksum': '2b00042f7481c7b056c4b410d28f33cf',
   'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg',
   'url': 'http://www.example.com/files/product1.pdf'}),
 (False,
  Failure(...))]

默认情况下, get_media_requests()方法返回None ,这意味着没有要下载的文件.

item_completed(results, item, info)

当对单个项目的所有文件请求均已完成(下载完成或由于某些原因失败FilesPipeline.item_completed()时,将调用FilesPipeline.item_completed()方法.

item_completed()方法必须返回将发送到后续项目管道阶段的输出,因此您必须像在任何管道中一样返回(或删除)该项目.

这是item_completed()方法的示例,其中我们将下载的文件路径(传递给结果)存储在file_paths项字段中,如果该项不包含任何文件,则将其删除:

from scrapy.exceptions import DropItem

def item_completed(self, results, item, info):
    file_paths = [x['path'] for ok, x in results if ok]
    if not file_paths:
        raise DropItem("Item contains no files")
    item['file_paths'] = file_paths
    return item

By default, the item_completed() method returns the item.

请在此处查看可在自定义图像管道中覆盖的方法:

class scrapy.pipelines.images.ImagesPipeline
ImagesPipeline是的扩展FilesPipeline ,自定义字段名称和添加自定义行为的图像.
file_path(request, response, info)

每个下载的项目调用一次此方法. 它返回源自指定response的文件的下载路径.

除了response ,此方法还接收原始requestinfo .

您可以重写此方法以自定义每个文件的下载路径.

例如,如果文件URL以常规路径结尾(例如https://example.com/a/b/c/foo.png ),则可以使用以下方法将所有文件及其原始文件名下载到files夹中(例如files/foo.png ):

import os
from urllib.parse import urlparse

from scrapy.pipelines.images import ImagesPipeline

class MyImagesPipeline(ImagesPipeline):

    def file_path(self, request, response, info):
        return 'files/' + os.path.basename(urlparse(request.url).path)

默认情况下, file_path()方法返回full/<request URL hash>.<extension> .

get_media_requests(item, info)

FilesPipeline.get_media_requests()方法相同,但对图像URL使用不同的字段名称.

必须为每个图像URL返回一个请求.

item_completed(results, item, info)

当对单个项目的所有图像请求均已完成(下载完成或由于某些原因而失败ImagesPipeline.item_completed()将调用ImagesPipeline.item_completed()方法.

FilesPipeline.item_completed()方法的工作方式相同,但是使用不同的字段名称来存储图像下载结果.

默认情况下, item_completed()方法返回该项目.

Custom Images pipeline example

Here is a full example of the Images Pipeline whose methods are examplified above:

import scrapy
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem

class MyImagesPipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            yield scrapy.Request(image_url)

    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")
        item['image_paths'] = image_paths
        return item