# 下载和处理文件和图像
> 译者:[OSGeo 中国](https://www.osgeo.cn/)
废料可重复使用 [item pipelines](item-pipeline.html) 用于下载附加到特定项目的文件(例如,当您抓取产品并希望在本地下载其图像时)。这些管道共享一些功能和结构(我们将它们称为媒体管道),但通常您可以使用文件管道或图像管道。
两条管道都实现了以下功能:
* 避免重新下载最近下载的媒体
* 指定存储媒体的位置(文件系统目录、AmazonS3存储桶、Google云存储桶)
图像管道有一些用于处理图像的额外功能:
* 将所有下载的图像转换为通用格式(JPG)和模式(RGB)
* 缩略图生成
* 检查图像的宽度/高度以确保它们满足最小限制
这些管道还保留当前正在计划下载的媒体URL的内部队列,并将到达的包含相同媒体的响应连接到该队列。这样可以避免在多个项目共享同一媒体时多次下载同一媒体。
## 使用文件管道
使用时的典型工作流 `FilesPipeline` 像这样:
1. 在spider中,您抓取一个项目并将所需的URL放入 `file_urls` 字段。
2. 该项从spider返回并转到项管道。
3. 当项目到达 `FilesPipeline` ,中的URL `file_urls` 使用标准的Scrapy计划程序和下载程序(这意味着计划程序和下载程序中间软件被重用)来计划下载字段,但具有更高的优先级,在其他页面被抓取之前对其进行处理。该项在特定管道阶段保持“锁定”,直到文件完成下载(或由于某种原因失败)。
4. 下载文件时,另一个字段( `files` )将用结果填充。此字段将包含一个包含有关下载文件的信息的dict列表,例如下载路径、原始的scraped url(取自 `file_urls` 字段)和文件校验和。列表中的文件 `files` 字段将保留与原始字段相同的顺序 `file_urls` 字段。如果某些文件下载失败,将记录一个错误,并且该文件不会出现在 `files` 字段。
## 使用图像管道
使用 [`ImagesPipeline`](#scrapy.pipelines.images.ImagesPipeline "scrapy.pipelines.images.ImagesPipeline") 很像使用 `FilesPipeline` ,但使用的默认字段名不同:您使用 `image_urls` 对于项目的图像URL,它将填充 `images` 有关下载图像的信息字段。
使用 [`ImagesPipeline`](#scrapy.pipelines.images.ImagesPipeline "scrapy.pipelines.images.ImagesPipeline") 对于图像文件,您可以配置一些额外的功能,如生成缩略图和根据图像大小过滤图像。
图像管道使用 [Pillow](https://github.com/python-pillow/Pillow) 对于缩略图和将图像规范化为jpeg/rgb格式,因此需要安装此库才能使用它。 [Python Imaging Library](http://www.pythonware.com/products/pil/) (PIL)在大多数情况下也应该有效,但它在某些设置中会引起问题,因此我们建议使用 [Pillow](https://github.com/python-pillow/Pillow) 而不是皮尔。
## 启用媒体管道
要启用媒体管道,必须首先将其添加到项目中 [`ITEM_PIPELINES`](settings.html#std:setting-ITEM_PIPELINES) 设置。
对于图像管道,请使用:
```py
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}
```
对于文件管道,请使用:
```py
ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}
```
注解
您还可以同时使用文件和图像管道。
然后,将目标存储设置配置为用于存储下载的图像的有效值。否则,管道将保持禁用状态,即使将其包含在 [`ITEM_PIPELINES`](settings.html#std:setting-ITEM_PIPELINES) 设置。
对于文件管道,设置 [`FILES_STORE`](#std:setting-FILES_STORE) 设置:
```py
FILES_STORE = '/path/to/valid/dir'
```
对于图像管道,设置 [`IMAGES_STORE`](#std:setting-IMAGES_STORE) 设置:
```py
IMAGES_STORE = '/path/to/valid/dir'
```
## 支持的存储
文件系统目前是唯一官方支持的存储,但也支持将文件存储在 [Amazon S3](https://aws.amazon.com/s3/) 和 [Google Cloud Storage](https://cloud.google.com/storage/) .
### 文件系统存储
文件使用 [SHA1 hash](https://en.wikipedia.org/wiki/SHA_hash_functions) 文件名的URL。
例如,以下图像URL::
```py
http://www.example.com/image.jpg
```
谁的 `SHA1 hash` 是::
```py
3afec3b4765f8f0a07b78f98c07b83f013567a0a
```
将下载并存储在以下文件中::
```py
<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg
```
在哪里?
* `<IMAGES_STORE>` 目录是否在中定义? [`IMAGES_STORE`](#std:setting-IMAGES_STORE) 图像管道的设置。
* `full` 是一个子目录,用于从缩略图中分离完整图像(如果使用)。有关详细信息,请参阅 [图像的缩略图生成](#topics-images-thumbnails) .
### Amazon S3存储
[`FILES_STORE`](#std:setting-FILES_STORE) 和 [`IMAGES_STORE`](#std:setting-IMAGES_STORE) 可以表示AmazonS3存储桶。Scrapy会自动将文件上传到bucket。
例如,这是一个有效的 [`IMAGES_STORE`](#std:setting-IMAGES_STORE) 价值:
```py
IMAGES_STORE = 's3://buckimg'
```
您可以修改用于存储文件的访问控制列表(ACL)策略,该策略由 [`FILES_STORE_S3_ACL`](#std:setting-FILES_STORE_S3_ACL) 和 [`IMAGES_STORE_S3_ACL`](#std:setting-IMAGES_STORE_S3_ACL) 设置。默认情况下,acl设置为 `private` . 要使文件公开,请使用 `public-read` 政策:
```py
IMAGES_STORE_S3_ACL = 'public-read'
```
有关详细信息,请参阅 [canned ACLs](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) 在AmazonS3开发者指南中。
因为下流的东西 `boto` / `botocore` 在内部,您还可以使用其他S3,如存储。像自我托管这样的存储 [Minio](https://github.com/minio/minio) 或 [s3.scality](https://s3.scality.com/) . 您所需要做的就是在Scrapy设置中设置端点选项:
```py
AWS_ENDPOINT_URL = 'http://minio.example.com:9000'
```
对于自托管,您可能觉得不需要使用SSL,也不需要验证SSL连接::
```py
AWS_USE_SSL = False # or True (None by default)
AWS_VERIFY = False # or True (None by default)
```
### 谷歌云存储
[`FILES_STORE`](#std:setting-FILES_STORE) 和 [`IMAGES_STORE`](#std:setting-IMAGES_STORE) 可以表示一个谷歌云存储桶。Scrapy会自动将文件上传到bucket。(需要 [google-cloud-storage](https://cloud.google.com/storage/docs/reference/libraries#client-libraries-install-python) )
例如,这些是有效的 [`IMAGES_STORE`](#std:setting-IMAGES_STORE) 和 [`GCS_PROJECT_ID`](#std:setting-GCS_PROJECT_ID) 设置::
```py
IMAGES_STORE = 'gs://buckimg/'
GCS_PROJECT_ID = 'project_id'
```
有关身份验证的信息,请参见 [documentation](https://cloud.google.com/docs/authentication/production) .
您可以修改用于存储文件的访问控制列表(ACL)策略,该策略由 [`FILES_STORE_GCS_ACL`](#std:setting-FILES_STORE_GCS_ACL) 和 [`IMAGES_STORE_GCS_ACL`](#std:setting-IMAGES_STORE_GCS_ACL) 设置。默认情况下,acl设置为 `''` (空字符串)这意味着云存储将bucket的默认对象acl应用于该对象。要使文件公开,请使用 `publicRead` 政策:
```py
IMAGES_STORE_GCS_ACL = 'publicRead'
```
有关详细信息,请参阅 [Predefined ACLs](https://cloud.google.com/storage/docs/access-control/lists#predefined-acl) 在谷歌云平台开发者指南中。
## 使用实例
为了首先使用媒体管道, [enable it](#topics-media-pipeline-enabling) .
然后,如果spider返回带有urls键的dict( `file_urls` 或 `image_urls` ,对于文件或图像管道,管道将把结果放在各自的键下。( `files` 或 `images` )
如果您喜欢使用 [`Item`](items.html#scrapy.item.Item "scrapy.item.Item") ,然后使用必要的字段定义自定义项,如本示例中的图像管道:
```py
import scrapy
class MyItem(scrapy.Item):
# ... other item fields ...
image_urls = scrapy.Field()
images = scrapy.Field()
```
如果要对URL键或结果键使用其他字段名,也可以重写它。
对于文件管道,设置 [`FILES_URLS_FIELD`](#std:setting-FILES_URLS_FIELD) 和/或 [`FILES_RESULT_FIELD`](#std:setting-FILES_RESULT_FIELD) 设置::
```py
FILES_URLS_FIELD = 'field_name_for_your_files_urls'
FILES_RESULT_FIELD = 'field_name_for_your_processed_files'
```
对于图像管道,设置 [`IMAGES_URLS_FIELD`](#std:setting-IMAGES_URLS_FIELD) 和/或 [`IMAGES_RESULT_FIELD`](#std:setting-IMAGES_RESULT_FIELD) 设置::
```py
IMAGES_URLS_FIELD = 'field_name_for_your_images_urls'
IMAGES_RESULT_FIELD = 'field_name_for_your_processed_images'
```
如果您需要更复杂的内容,并且想要覆盖自定义管道行为,请参见 [扩展媒体管道](#topics-media-pipeline-override) .
如果有多个图像管道继承自ImagePipeline,并且希望在不同的管道中具有不同的设置,则可以设置以管道类的大写名称开头的设置键。例如,如果您的管道名为mypipeline,并且您希望有自定义图像URL字段,那么您可以定义设置mypipeline图像URL字段,并且将使用自定义设置。
## 其他功能
### 文件过期
图像管道避免下载最近下载的文件。要调整此保留延迟,请使用 [`FILES_EXPIRES`](#std:setting-FILES_EXPIRES) 设置(或) [`IMAGES_EXPIRES`](#std:setting-IMAGES_EXPIRES) ,对于图像管道),指定延迟天数:
```py
# 120 days of delay for files expiration
FILES_EXPIRES = 120
# 30 days of delay for images expiration
IMAGES_EXPIRES = 30
```
两种设置的默认值都是90天。
如果您有子类filespine的管道,并且希望对其进行不同的设置,则可以设置以大写类名开头的设置键。例如,给定名为MyPipeline的管道类,您可以设置设置键:
> mypipeline_files_expires=180
管道类MyPipeline的过期时间设置为180。
### 图像的缩略图生成
图像管道可以自动创建下载图像的缩略图。
要使用此功能,必须设置 [`IMAGES_THUMBS`](#std:setting-IMAGES_THUMBS) 到一个字典,其中键是缩略图名称,值是它们的尺寸。
例如::
```py
IMAGES_THUMBS = {
'small': (50, 50),
'big': (270, 270),
}
```
使用此功能时,图像管道将使用以下格式创建每个指定大小的缩略图:
```py
<IMAGES_STORE>/thumbs/<size_name>/<image_id>.jpg
```
在哪里?
* `<size_name>` 是在 [`IMAGES_THUMBS`](#std:setting-IMAGES_THUMBS) 字典键( `small` , `big` 等)
* `<image_id>` 是 [SHA1 hash](https://en.wikipedia.org/wiki/SHA_hash_functions) 图像URL的
存储图像文件的示例 `small` 和 `big` 缩略图名称:
```py
<IMAGES_STORE>/full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/small/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/big/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
```
第一个是从网站下载的完整图像。
### 过滤掉小图像
使用图像管道时,可以删除过小的图像,方法是在 [`IMAGES_MIN_HEIGHT`](#std:setting-IMAGES_MIN_HEIGHT) 和 [`IMAGES_MIN_WIDTH`](#std:setting-IMAGES_MIN_WIDTH) 设置。
例如::
```py
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110
```
注解
大小约束根本不影响缩略图的生成。
可以只设置一个大小约束或同时设置两个大小约束。当同时设置这两种尺寸时,将只保存满足这两种最小尺寸的图像。对于上面的示例,大小为(105 x 105)或(105 x 200)或(200 x 105)的图像都将被删除,因为至少有一个维度比约束短。
默认情况下,没有大小约束,因此所有图像都会被处理。
### 允许重定向
默认情况下,媒体管道忽略重定向,即HTTP重定向到媒体文件URL请求将意味着媒体下载失败。
要处理媒体重定向,请将此设置设置为 `True` ::
```py
MEDIA_ALLOW_REDIRECTS = True
```
## 扩展媒体管道
请参见以下自定义文件管道中可以重写的方法:
```py
class scrapy.pipelines.files.FilesPipeline
```
```py
get_media_requests(item, info)
```
如工作流上所示,管道将从项目中获取要下载的图像的URL。为此,可以重写 [`get_media_requests()`](#scrapy.pipelines.files.FilesPipeline.get_media_requests "scrapy.pipelines.files.FilesPipeline.get_media_requests") 方法并返回每个文件的请求URL::
```py
def get_media_requests(self, item, info):
for file_url in item['file_urls']:
yield scrapy.Request(file_url)
```
这些请求将由管道处理,下载完成后,结果将发送到 [`item_completed()`](#scrapy.pipelines.files.FilesPipeline.item_completed "scrapy.pipelines.files.FilesPipeline.item_completed") 方法,作为2元素元组的列表。每个元组将包含 `(success, file_info_or_error)` 在哪里?
* `success` 是一个布尔值,它是 `True` 如果图像下载成功或 `False` 如果因为某种原因失败了
* `file_info_or_error` 是包含以下键的dict(如果成功是 `True` 或A [Twisted Failure](https://twistedmatrix.com/documents/current/api/twisted.python.failure.Failure.html) 如果有问题的话。
* `url` -从中下载文件的URL。这是从 [`get_media_requests()`](#scrapy.pipelines.files.FilesPipeline.get_media_requests "scrapy.pipelines.files.FilesPipeline.get_media_requests") 方法。
* `path` -路径(相对于 [`FILES_STORE`](#std:setting-FILES_STORE) )文件的存储位置
* `checksum` -A [MD5 hash](https://en.wikipedia.org/wiki/MD5) 图像内容的
接收的元组列表 [`item_completed()`](#scrapy.pipelines.files.FilesPipeline.item_completed "scrapy.pipelines.files.FilesPipeline.item_completed") 保证保留从 [`get_media_requests()`](#scrapy.pipelines.files.FilesPipeline.get_media_requests "scrapy.pipelines.files.FilesPipeline.get_media_requests") 方法。
以下是 `results` 论点:
```py
[(True,
{'checksum': '2b00042f7481c7b056c4b410d28f33cf',
'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg',
'url': 'http://www.example.com/files/product1.pdf'}),
(False,
Failure(...))]
```
默认情况下 [`get_media_requests()`](#scrapy.pipelines.files.FilesPipeline.get_media_requests "scrapy.pipelines.files.FilesPipeline.get_media_requests") 方法返回 `None` 这意味着该项目没有可下载的文件。
```py
item_completed(results, item, info)
```
这个 [`FilesPipeline.item_completed()`](#scrapy.pipelines.files.FilesPipeline.item_completed "scrapy.pipelines.files.FilesPipeline.item_completed") 当单个项的所有文件请求都已完成时调用的方法(要么已完成下载,要么由于某种原因失败)。
这个 [`item_completed()`](#scrapy.pipelines.files.FilesPipeline.item_completed "scrapy.pipelines.files.FilesPipeline.item_completed") 方法必须返回将发送到后续项管道阶段的输出,因此必须返回(或删除)该项,就像在任何管道中一样。
下面是一个 [`item_completed()`](#scrapy.pipelines.files.FilesPipeline.item_completed "scrapy.pipelines.files.FilesPipeline.item_completed") 方法,将下载的文件路径(传入结果)存储在 `file_paths` 项目字段,如果该项目不包含任何文件,则将其删除::
```py
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
```
默认情况下, [`item_completed()`](#scrapy.pipelines.files.FilesPipeline.item_completed "scrapy.pipelines.files.FilesPipeline.item_completed") 方法返回项。
请参见以下自定义图像管道中可以覆盖的方法:
```py
class scrapy.pipelines.images.ImagesPipeline
```
> 这个 [`ImagesPipeline`](#scrapy.pipelines.images.ImagesPipeline "scrapy.pipelines.images.ImagesPipeline") 是 `FilesPipeline` ,自定义字段名并为图像添加自定义行为。
```py
get_media_requests(item, info)
```
工作方式与 `FilesPipeline.get_media_requests()` 方法,但对图像URL使用不同的字段名。
必须返回每个图像URL的请求。
```py
item_completed(results, item, info)
```
这个 [`ImagesPipeline.item_completed()`](#scrapy.pipelines.images.ImagesPipeline.item_completed "scrapy.pipelines.images.ImagesPipeline.item_completed") 当一个项目的所有图像请求都已完成时(要么已完成下载,要么由于某种原因失败),将调用方法。
工作方式与 `FilesPipeline.item_completed()` 方法,但使用不同的字段名存储图像下载结果。
默认情况下, [`item_completed()`](#scrapy.pipelines.images.ImagesPipeline.item_completed "scrapy.pipelines.images.ImagesPipeline.item_completed") 方法返回项。
## 自定义图像管道示例
下面是一个完整的图像管道示例,其方法如上所示:
```py
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
```
- 简介
- 第一步
- Scrapy at a glance
- 安装指南
- Scrapy 教程
- 实例
- 基本概念
- 命令行工具
- Spider
- 选择器
- 项目
- 项目加载器
- Scrapy shell
- 项目管道
- Feed 导出
- 请求和响应
- 链接提取器
- 设置
- 例外情况
- 内置服务
- Logging
- 统计数据集合
- 发送电子邮件
- 远程登录控制台
- Web服务
- 解决具体问题
- 常见问题
- 调试spiders
- Spider 合约
- 常用做法
- 通用爬虫
- 使用浏览器的开发人员工具进行抓取
- 调试内存泄漏
- 下载和处理文件和图像
- 部署 Spider
- AutoThrottle 扩展
- Benchmarking
- 作业:暂停和恢复爬行
- 延伸 Scrapy
- 体系结构概述
- 下载器中间件
- Spider 中间件
- 扩展
- 核心API
- 信号
- 条目导出器
- 其余所有
- 发行说明
- 为 Scrapy 贡献
- 版本控制和API稳定性