Скачивание и обработка файлов и изображений

Scrapy предоставляет переиспользуемые конвейеры элементов для загрузки файлов, прикрепленных к определённому элементу (например, когда вы очищаете продукты, а также хотите загрузить их изображения локально). Данные конвейеры имеют общие функциональные возможности и структуру (мы называем их конвейерами мультимедиа), но обычно вы будете использовать конвейер файлов или конвейер изображений.

Оба конвейера реализуют данные функции:

  • Избегайте повторной загрузки медиафайлов, которые были загружены недавно

  • Указание места для хранения мультимедиа (каталог файловой системы, FTP-сервер, корзина Amazon S3, корзина Google Cloud Storage)

В конвейере изображений есть несколько дополнительных функций для обработки изображений:

  • Преобразовать все загруженные изображения в общий формат (JPG) и режим (RGB)

  • Создание эскизов

  • Проверить ширину и высоту изображений, чтобы убедиться, что они соответствуют минимальным ограничениям

Конвейеры также хранят внутреннюю очередь тех URL-адресов мультимедиа, которые в настоящее время планируются для загрузки, и подключают к этой очереди те ответы, которые поступают, содержащие тот же носитель. Это позволяет избежать загрузки одного и того же медиафайла более одного раза, если он используется несколькими объектами.

Использование файлового конвейера

Типичный рабочий процесс при использовании FilesPipeline выглядит следующим образом:

  1. В Spider вы очищаете элемент и помещаете желаемые URL-адреса в поле file_urls.

  2. Элемент возвращается от паука и попадает в конвейер элемента.

  3. Когда элемент достигает FilesPipeline, URL-адреса в поле file_urls планируются для загрузки с использованием стандартного планировщика и загрузчика Scrapy (что означает, что промежуточное ПО планировщика и загрузчика повторно используется), но с более высоким приоритетом, обрабатывая их до очистки других страниц. Элемент остаётся «заблокированным» на этом конкретном этапе конвейера до тех пор, пока файлы не загрузятся (или не завершатся ошибкой по какой-либо причине).

  4. Когда файлы будут загружены, другое поле (files) будет заполнено результатами. Это поле будет содержать список dicts с информацией о загруженных файлах, такой как загруженный путь, исходный очищенный URL (взятый из поля file_urls), контрольная сумма файла и статус файла. Файлы в списке поля files сохранят тот же порядок, что и исходное поле file_urls. Если не удалось загрузить какой-либо файл, в журнал будет записана ошибка, и файл не будет отображаться в поле files.

Использование конвейера изображений

Использование ImagesPipeline во многом похоже на использование FilesPipeline, за исключением того, что используемые по умолчанию имена полей отличаются: вы используете image_urls для URL-адресов изображений элемента, и он заполняет поле images для информации о загруженных изображениях.

Преимущество использования ImagesPipeline для файлов изображений заключается в том, что вы можете настроить некоторые дополнительные функции, такие как создание эскизов и фильтрация изображений на основе их размера.

Для конвейера изображений требуется Pillow 4.0.0 или более поздней версии. Он используется для миниатюр и нормализации изображений в формат JPEG/RGB.

Включение вашего медиа-конвейера

Чтобы включить конвейер мультимедиа, сначала необходимо добавить его в настройку проекта ITEM_PIPELINES.

Для конвейера изображений используйте:

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

Для файлового конвейера используйте:

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

Примечание

Вы также можете использовать конвейер файлов и изображений одновременно.

Затем настраивает параметр целевого хранилища на допустимое значение, которое будет использоваться для хранения загруженных изображений. В противном случае конвейер останется отключенным, даже если вы включает его в настройку ITEM_PIPELINES.

Для конвейера файлов устанавливает параметр FILES_STORE:

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

Для конвейера изображений устанавливает параметр IMAGES_STORE:

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

Именование файлов

Именование файлов по умолчанию

По умолчанию файлы хранятся с использованием SHA-1 хэша их URL-адресов для имён файлов.

Например, следующий URL-адрес изображения:

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

Чей SHA-1 хэш следующий:

3afec3b4765f8f0a07b78f98c07b83f013567a0a

Будет загружено и сохранено с использованием выбранного вами методе хранилища и следующего имени файла:

3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg

Пользовательское именование файлов

Вы можете использовать другое вычисленное имя файла для сохраненных файлов. Например, классификация изображения путём включения мета в имя файла.

Настраивает имена файлов, переопределив метод file_path вашего конвейера мультимедиа.

Например, конвейер изображений с URL-адресом изображения:

http://www.example.com/product/images/large/front/0000000004166

Может быть преобразовано в имя файла с сокращенным хешем и перспективой front:

00b08510e4_front.jpg

Переопределив file_path вот так:

import hashlib
from os.path import splitext

def file_path(self, request, response=None, info=None, *, item=None):
    image_url_hash = hashlib.shake_256(request.url.encode()).hexdigest(5)
    image_perspective = request.url.split('/')[-2]
    image_filename = f'{image_url_hash}_{image_perspective}.jpg'

    return image_filename

Предупреждение

Если ваша настраиваемая схема имён файлов опирается на метаданные, которые могут различаться в зависимости от очистки, это может привести к неожиданной повторной загрузке существующих носителей с использованием новых имён файлов.

Например, если в вашей пользовательской схеме имён файлов используется название продукта, а сайт меняет название продукта между циклами очистки, Scrapy повторно загрузит тот же носитель, используя обновленные имена файлов.

Дополнительные сведения о методе file_path см. в разделе Расширение медиа-конвейеров.

Поддерживаемое хранилище

Хранилище файловой системы

Хранилище файловой системы сохранит файлы по следующему пути:

<IMAGES_STORE>/full/<FILE_NAME>

Где:

  • <IMAGES_STORE> — это каталог, определённый в настройке IMAGES_STORE для конвейера изображений.

  • full — это подкаталог для отделения полных изображений от эскизов (если они используются). Для получения дополнительной информации см. Создание эскизов изображений.

  • <FILE_NAME> — это имя файла, присвоенное файлу. Для получения дополнительной информации см. Именование файлов.

Хранилище FTP-сервера

Добавлено в версии 2.0.

FILES_STORE и IMAGES_STORE могут указывать на FTP-сервер. Scrapy автоматически загрузит файлы на сервер.

FILES_STORE и IMAGES_STORE следует записать в одной из следующих форм:

ftp://username:password@address:port/path
ftp://address:port/path

Если username и password не указаны, они берутся из настроек FTP_USER и FTP_PASSWORD соответственно.

FTP поддерживает два разных режима подключения: активный и пассивный. Scrapy по умолчанию использует пассивный режим подключения. Чтобы вместо этого использовать активный режим подключения, устанавливает для параметра FEED_STORAGE_FTP_ACTIVE значение True.

Хранилище Amazon S3

Если установлен botocore >= 1.4.87, FILES_STORE и IMAGES_STORE могут представлять корзину Amazon S3. Scrapy автоматически загрузит файлы в корзину.

Например, это допустимое значение IMAGES_STORE:

IMAGES_STORE = 's3://bucket/images'

Вы можете изменить политику списка управления доступом (ACL), используемую для сохраненных файлов, которая определяется параметрами FILES_STORE_S3_ACL и IMAGES_STORE_S3_ACL. По умолчанию для ACL задано значение private. Чтобы сделать файлы общедоступными, используйте политику public-read:

IMAGES_STORE_S3_ACL = 'public-read'

Дополнительные сведения см. в разделе canned ACLs в Руководстве разработчика Amazon S3.

Вы также можете использовать другие хранилища типа S3. Такие хранилища, как Minio или s3.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

FILES_STORE и IMAGES_STORE могут представлять корзину Google Cloud Storage. Scrapy автоматически загрузит файлы в корзину. (требуется Google Cloud Storage)

Например, это допустимые настройки IMAGES_STORE и GCS_PROJECT_ID:

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

Для получения информации об аутентификации см. данной документации.

Вы можете изменить политику списка управления доступом (ACL), используемую для сохраненных файлов, которая определяется параметрами FILES_STORE_GCS_ACL и IMAGES_STORE_GCS_ACL. По умолчанию для ACL задано значение '' (пустая строка), что означает, что Cloud Storage применяет к объекту ACL объекта по умолчанию для сегмента. Чтобы сделать файлы общедоступными, используйте политику publicRead:

IMAGES_STORE_GCS_ACL = 'publicRead'

Дополнительные сведения см. в разделе Предопределённые ACL в Руководстве разработчика Google Cloud Platform.

Пример использования

Чтобы использовать конвейер мультимедиа, сначала включите его.

Затем, если паук возвращает объект элемента с полем URL-адресов (file_urls или image_urls для конвейера файлов или изображений соответственно), конвейер поместит результаты в соответствующее поле (files или images).

При использовании типов элемента, для которого заранее определены поля, необходимо определить как поле URL-адресов, так и поле результатов. Например, при использовании конвейера изображений элементы должны определять как поле image_urls, так и поле images. Например, используя класс 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, и будут использоваться ваши пользовательские настройки.

Дополнительные возможности

Срок действия файла

Конвейер изображений избегает загрузки файлов, которые были загружены недавно. Чтобы настроить эту задержку хранения, используйте параметр 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.

Время последнего изменения из файла используется для определения возраста файла в днях, который затем сравнивается с установленным сроком действия, чтобы определить, истек ли срок действия файла.

Создание эскизов изображений

Конвейер изображений может автоматически создавать эскизы загруженных изображений.

Чтобы использовать эту функцию, вы должны установить IMAGES_THUMBS в словарь, где ключи — это имена миниатюр, а значения — их размеры.

Например:

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

Когда вы используете эту функцию, конвейер изображений будет создавать миниатюры каждого указанного размера в этом формате:

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

Где:

  • <size_name> — это тот, который указан в ключах словаря IMAGES_THUMBS (small, big и т. Д.)

  • <image_id> — это SHA-1 hash URL-адреса изображения

Пример файлов изображений, хранящихся с именами эскизов small и big:

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

Первый — это полный образ, скачанный с сайта.

Фильтрация небольших изображений

При использовании конвейера изображений вы можете отбрасывать слишком маленькие изображения, указав минимально допустимый размер в настройках IMAGES_MIN_HEIGHT и IMAGES_MIN_WIDTH.

Например:

IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

Примечание

Ограничения размера вообще не влияют на создание эскизов.

Можно установить только одно ограничение размера или оба. При установке обоих из них будут сохранены только изображения, удовлетворяющие обоим минимальным размерам. В приведённом выше примере изображения размеров (105 x 105), (105 x 200) или (200 x 105) будут отброшены, потому что по крайней мере одно измерение короче ограничения.

По умолчанию ограничений по размеру нет, поэтому обрабатываются все изображения.

Разрешение перенаправления

По умолчанию конвейеры мультимедиа игнорируют перенаправления, т. е. перенаправление HTTP на URL-запрос мультимедийного файла будет означать, что загрузка мультимедиа считается неудачной.

Для обработки перенаправления мультимедиа устанавливает для этого параметра значение True:

MEDIA_ALLOW_REDIRECTS = True

Расширение медиа-конвейеров

См. здесь методы, которые можно переопределить в пользовательском конвейере файлов:

class scrapy.pipelines.files.FilesPipeline
file_path(self, request, response=None, info=None, *, item=None)

Данный метод вызывается один раз для каждого загруженного элемента. Он возвращает путь загрузки файла, происходящего из указанного response.

Помимо response, данный метод получает исходные request, info и item

Вы можете переопределить данный метод, чтобы настроить путь загрузки каждого файла.

Например, если 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=None, info=None, *, item=None):
        return 'files/' + os.path.basename(urlparse(request.url).path)

Точно так же вы можете использовать item для определения пути к файлу на основе некоторого свойства элемента.

По умолчанию метод file_path() возвращает full/<request URL hash>.<extension>.

Добавлено в версии 2.4: Параметр item.

get_media_requests(item, info)

Как видно из рабочего процесса, конвейер получит URL-адреса изображений для загрузки из элемента. Для этого вы можете переопределить метод get_media_requests() и возвращает запрос для каждого URL-адреса файла:

from itemadapter import ItemAdapter

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

Данные запросы будут обрабатываться конвейером, и по завершении загрузки результаты будут отправлены методу item_completed() в виде списка двухэлементных кортежей. Каждый кортеж будет содержать (success, file_info_or_error), где:

  • success — это логическое значение True, если изображение было загружено успешно, или False, если по какой-то причине произошёл сбой

  • file_info_or_error — это словарь, содержащий следующие ключи (в случае успеха True) или Failure, если возникла проблема.

    • url — URL-адрес, с которого был скачан файл. Это URL-адрес запроса, возвращённого методом get_media_requests().

    • path — путь (относительно FILES_STORE), где хранился файл

    • checksum — MD5 хэш содержимого изображения

    • status — индикация состояния файла.

      Добавлено в версии 2.2.

      Это может быть одно из следующих:

      • downloaded — файл скачан.

      • uptodate — файл не был загружен, поскольку он был загружен недавно, согласно политике истечения срока действия файла.

      • cached — файл уже был запланирован для загрузки другим элементом, использующим тот же файл.

Список кортежей, полученных item_completed(), гарантированно сохранит тот же порядок запросов, возвращаемых методом get_media_requests().

Вот типичное значение аргумента results:

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

По умолчанию метод get_media_requests() возвращает None, что означает, что для элемента нет файлов для загрузки.

item_completed(results, item, info)

Метод FilesPipeline.item_completed() вызывается, когда все файловые запросы для одного элемента завершены (либо загрузка завершена, либо по какой-то причине не удалось).

Метод item_completed() должен возвращать выходные данные, которые будут отправлены на последующие этапы конвейера элементов, поэтому вы должны возвращает (или отбросить) элемент, как и в любом конвейере.

Вот пример метода item_completed(), в котором мы сохраняем загруженные пути к файлам (переданные в результатах) в поле элемента file_paths и отбрасываем элемент, если он не содержит файлов:

from itemadapter import ItemAdapter
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")
    adapter = ItemAdapter(item)
    adapter['file_paths'] = file_paths
    return item

По умолчанию метод item_completed() возвращает элемент.

См. здесь методы, которые можно переопределить в настраиваемом конвейере изображений:

class scrapy.pipelines.images.ImagesPipeline

ImagesPipeline является расширением FilesPipeline, настраивая имена полей и добавляя настраиваемое поведение для изображений.

file_path(self, request, response=None, info=None, *, item=None)

Данный метод вызывается один раз для каждого загруженного элемента. Он возвращает путь загрузки файла, происходящего из указанного response.

Помимо response, данный метод получает исходные request, info и item

Вы можете переопределить данный метод, чтобы настроить путь загрузки каждого файла.

Например, если 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=None, info=None, *, item=None):
        return 'files/' + os.path.basename(urlparse(request.url).path)

Точно так же вы можете использовать item для определения пути к файлу на основе некоторого свойства элемента.

По умолчанию метод file_path() возвращает full/<request URL hash>.<extension>.

Добавлено в версии 2.4: Параметр item.

get_media_requests(item, info)

Работает так же, как метод FilesPipeline.get_media_requests(), но использует другое имя поля для URL-адресов изображений.

Должен возвращать запрос для каждого URL изображения.

item_completed(results, item, info)

Метод ImagesPipeline.item_completed() вызывается, когда все запросы изображений для одного элемента завершены (либо загрузка завершена, либо по какой-либо причине произошла ошибка).

Работает так же, как метод FilesPipeline.item_completed(), но использует другие имена полей для хранения результатов загрузки изображений.

По умолчанию метод item_completed() возвращает элемент.

Пример конвейера пользовательских изображений

Вот полный пример конвейера изображений, методы которого приведены выше:

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

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")
        adapter = ItemAdapter(item)
        adapter['image_paths'] = image_paths
        return item

Чтобы включить пользовательский компонент конвейера мультимедиа, необходимо добавить путь импорта его класса в параметр ITEM_PIPELINES, как в следующем примере:

ITEM_PIPELINES = {
    'myproject.pipelines.MyImagesPipeline': 300
}