# 下載和處理文件和圖像
> 譯者:[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穩定性