Элементы
Основная цель парсинга — извлечь структурированные данные из неструктурированных источников, обычно веб-страниц. Пауки могут возвращать извлечённые данные как «элементы», объекты Python, которые определяют пары ключ-значение.
Scrapy поддерживает несколько типов элементов. Создавая элемент, вы можете использовать любой тип элемента, который захотите. Когда вы пишете код, который получает элемент, ваш код должен работать для любого типа элемента.
Типы элементов
Scrapy поддерживает следующие типы элементов через библиотеку itemadapter: словари, Объекты-элементы, объекты класса данных и атрибуты объектов.
Словари
dict
удобен и привычен как тип элемента.
Объекты элементов
Item
предоставляет API, аналогичный dict
, плюс дополнительные функции, которые делают его наиболее полнофункциональным типом элементов:
- class scrapy.item.Item([arg])
- class scrapy.Item([arg])
Объекты
Item
копируют стандартный APIdict
, включая его метод__init__
.Item
позволяет определять имена полей, так что:KeyError
возникает при использовании неопределенных имён полей (т. е. предотвращает то, что опечатки остаются незамеченными)Экспортеры элементов могут экспортировать все поля по умолчанию, даже если первый извлеченный объект не имеет значений для всех из них
Item
также позволяет определять метаданные поля, которые можно использовать для настройки сериализации.trackref
отслеживает объектыItem
, чтобы помочь найти утечки памяти (см. Отладка утечек памяти с помощью trackref).Объекты
Item
также предоставляют следующие дополнительные методы API:- fields
Словарь, содержащий все объявленные поля для этого элемента, а не только для заполненных. Ключи — это имена полей, а значения — это объекты
Field
, используемые в Объявление элемента.
Пример:
from scrapy.item import Item, Field
class CustomItem(Item):
one_field = Field()
another_field = Field()
Объекты класса данных
Добавлено в версии 2.2.
dataclass()
позволяет определять классы элементов с именами полей, так что экспортеры элементов может экспортировать все поля по умолчанию, даже если первый извлеченный объект не имеет значений для всех из них.
Кроме того, элементы dataclass
также позволяют:
определить тип и значение по умолчанию для каждого определенного поля.
определить метаданные настраиваемого поля через
dataclasses.field()
, который можно использовать для настройки сериализации.
Они изначально работают в Python 3.7 или новее или используют бэкпорт классов данных в Python 3.6.
Пример:
from dataclasses import dataclass
@dataclass
class CustomItem:
one_field: str
another_field: int
Примечание
Типы полей не применяются во время выполнения.
attr.s объектов
Добавлено в версии 2.2.
attr.s()
позволяет определять классы элементов с именами полей, так что экспортеры элементов может экспортировать все поля по умолчанию, даже если первый извлеченный объект не имеет значений для всех из них.
Кроме того, элементы attr.s
также позволяют:
определить тип и значение по умолчанию для каждого определенного поля.
определяет настраиваемое поле metadata, которое можно использовать для настройки сериализации.
Для использования этого типа необходимо установить attrs пакет.
Пример:
import attr
@attr.s
class CustomItem:
one_field = attr.ib()
another_field = attr.ib()
Работа с объектами элемента
Объявление подклассов элементов
Подклассы элемента объявляются с использованием простого синтаксиса определения класса и объектов Field
. Вот пример:
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
tags = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
Примечание
Те, кто знаком с Django, заметят, что элементы Scrapy объявлены аналогично Django моделям, за исключением того, что элементы Scrapy намного проще, поскольку нет концепции различных типов полей.
Объявление полей
Объекты Field
используются для указания метаданных для каждого поля. Например, функция сериализатора для поля last_updated
, показанная в примере выше.
Вы можете указать любые метаданные для каждого поля. Нет ограничений на значения, принимаемые объектами Field
. По этой же причине нет справочного списка всех доступных ключей метаданных. Каждый ключ, определенный в объектах Field
, может использоваться другим компонентом, и только данные компоненты знают об этом. Вы также можете определить и использовать любой другой ключ Field
в своём проекте для собственных нужд. Основная цель объектов Field
— предоставить способ определения всех метаданных поля в одном месте. Обычно те компоненты, поведение которых зависит от каждого поля, используют определённые ключи полей для настройки этого поведения. Вы должны обратиться к их документации, чтобы узнать, какие ключи метаданных используются каждым компонентом.
Важно отметить, что объекты Field
, используемые для объявления элемента, не остаются назначенными как атрибуты класса. Вместо этого к ним можно получить доступ через атрибут Item.fields
.
- class scrapy.item.Field([arg])
- class scrapy.Field([arg])
Класс
Field
— это просто псевдоним встроенного классаdict
и не предоставляет никаких дополнительных функций или атрибутов. Другими словами, объектыField
— это старые добрые словари Python. Отдельный класс используется для поддержки синтаксиса объявления элемента на основе атрибутов класса.
Примечание
Метаданные поля также могут быть объявлены для элементов dataclass
и attrs
. Дополнительную информацию см. в документации для dataclasses.field и attr.ib.
Работа с объектами элемента
Вот несколько примеров общих задач, выполняемых с элементами, с использованием элемента Product
заявленого выше. Вы заметите, что API очень похож на API dict
.
Создание элементов
>>> product = Product(name='Desktop PC', price=1000)
>>> print(product)
Product(name='Desktop PC', price=1000)
Получение значений полей
>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC
>>> product['price']
1000
>>> product['last_updated']
Traceback (most recent call last):
...
KeyError: 'last_updated'
>>> product.get('last_updated', 'not set')
not set
>>> product['lala'] # getting unknown field
Traceback (most recent call last):
...
KeyError: 'lala'
>>> product.get('lala', 'unknown field')
'unknown field'
>>> 'name' in product # is name field populated?
True
>>> 'last_updated' in product # is last_updated populated?
False
>>> 'last_updated' in product.fields # is last_updated a declared field?
True
>>> 'lala' in product.fields # is lala a declared field?
False
Установка значений полей
>>> product['last_updated'] = 'today'
>>> product['last_updated']
today
>>> product['lala'] = 'test' # setting unknown field
Traceback (most recent call last):
...
KeyError: 'Product does not support field: lala'
Доступ ко всем заполненным значениям
Чтобы получить доступ ко всем заполненным значениям, просто используйте типичный API dict
:
>>> product.keys()
['price', 'name']
>>> product.items()
[('price', 1000), ('name', 'Desktop PC')]
Копирование элементов
Чтобы скопировать элемент, вы должны сначала решить, хотите ли вы мелкую копию или глубокую копию.
Если ваш элемент содержит изменяемые значения, такие как списки или словари, неглубокая копия сохранит ссылки на одни и те же изменяемые значения во всех разных копиях.
Например, если у вас есть элемент со списком тегов, и вы создаёте неглубокую копию этого элемента, и исходный элемент, и копия имеют один и тот же список тегов. Добавление тега к списку одного из элементов добавит тег и к другому элементу.
Если это нежелательное поведение, используйте вместо этого глубокую копию.
См. copy
для получения дополнительной информации.
Чтобы создать неглубокую копию элемента, вы можете либо вызвать copy()
для существующего элемента (product2 = product.copy()
), либо создать экземпляр класса элемента из существующего элемента (product2 = Product(product)
).
Чтобы создать полную копию, вместо этого вызовите deepcopy()
(product2 = product.deepcopy()
).
Другие общие задачи
Создание словарей из элементов:
>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}
Создание элементов из словарей:
>>> Product({'name': 'Laptop PC', 'price': 1500})
Product(price=1500, name='Laptop PC')
>>> Product({'name': 'Laptop PC', 'lala': 1500}) # warning: unknown field in dict
Traceback (most recent call last):
...
KeyError: 'Product does not support field: lala'
Расширение подклассов элементов
Вы можете расширить Items (чтобы добавить больше полей или изменить некоторые метаданные для некоторых полей), объявив подкласс исходного элемента.
Например:
class DiscountedProduct(Product):
discount_percent = scrapy.Field(serializer=str)
discount_expiration_date = scrapy.Field()
Вы также можете расширить метаданные поля, используя метаданные предыдущего поля и добавив дополнительные значения или изменив существующие значения, как это:
class SpecificProduct(Product):
name = scrapy.Field(Product.fields['name'], serializer=my_serializer)
Это добавляет (или заменяет) ключ метаданных serializer
для поля name
, сохраняя все ранее существовавшие значения метаданных.
Поддержка всех типов элементов
В коде, который получает элемент, например в методах конвейеры элементов или ПО промежуточного слоя паука, рекомендуется использовать класс ItemAdapter
и функцию is_item()
для написания кода, который работает для любого поддерживаемого типа элемента: