开发环境为 Python3.6,Scrapy 版本 2.4.x ,爬虫项目全部内容索引目录
看懂Python爬虫框架,所见即所得一切皆有可能
本章带你学习基于 Python3 的 Scrapy 爬虫框架 中数据爬取过程中Item为抓取的数据提供的容器,使用Item Loader可以让我们非常方便的将输入填充到容器中。
代码内容基于「Scrapy 爬虫框架」源码版本 2.4.0 ,更新内容会进行标记说明对应版本。
"""
# item:加载器解析的Item对象。
# context:ItemLoader的内容。
# default_item_class:实例化__init__方法。
# default_input_processor:指定字段的默认输入处理器。
# default_output_processor:指定字段的默认输出处理器。
# default_selector_class:用于构造selector其中ItemLoader。
# selecto :在Selector对象中提取数据。
# add_css (field_name, CSS, *processors, **kw):用于提取数据,通过processors和kwargs的处理后,存储到field_name
# add_value(field_name, value, *processors, **kw):处理添加给定的value对于给定的字段。get_value()通过给processors和kwargs,添加数据使用。
# add_xpath(field_name, value, *processors, **kw):用于从与此相关的选择器中提取字符串列表。
# get_collected_values**(field_name):返回字段的收集值。
# get_css (CSS, *processors, **kw):用于从与此相关的选择器中提取Unicode字符串列表。
# get_output_value(field_name):返回使用输出处理器为给定字段解析的收集值。
# get_value(value, *processors, **kw):处理给定的value被给予processors关键字参数。
# get_xpath**(XPath, *processors, **kw):用于从与此相关的选择器中提取Unicode字符串的列表。
# load_item():收集的数据填充并返回。
# nested_css(CSS, **context):使用CSS选择器创建嵌套加载器。提供的选择器相对于与ItemLoader。
# nested_xpath (XPath, **context):使用XPath选择器创建嵌套加载器。提供的选择器相对于与ItemLoader。
# replace_css(field_name, CSS, *processors, **kw):类似于add_css()但取代收集的数据而不是添加数据。
# replace_value(field_name, 价值, *processors, **kw):类似于add_value()但是将收集到的数据替换为新值,而不是添加它。
# replace_xpath(field_name, XPath, *processors, **kw):类似于add_xpath()但取代收集的数据而不是添加数据。
"""
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
from scrapy.loader import ItemLoader
from myproject.items import Product
def parse(self, response):
l = ItemLoader(item=Product(), response=response)
l.add_xpath('name', '//div[@class="product_name"]')
l.add_xpath('name', '//div[@class="product_title"]')
l.add_xpath('price', '//p[@id="price"]')
l.add_css('stock', 'p#stock]')
l.add_value('last_updated', 'today') # you can also use literal values
return l.load_item()
# 定义数据的字段类型和默认值。
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class InventoryItem:
name: Optional[str] = field(default=None)
price: Optional[float] = field(default=None)
stock: Optional[int] = field(default=None)
# 每个Item Loader对每个Field都有一个输入处理器和一个输出处理器。
# 输入处理器在数据被接受到时执行,当数据收集完后调用ItemLoader.load_item()时再执行输出处理器,返回最终结果。
l = ItemLoader(Product(), some_selector)
l.add_xpath('name', xpath1) # (1)
l.add_xpath('name', xpath2) # (2)
l.add_css('name', css) # (3)
l.add_value('name', 'test') # (4)
return l.load_item() # (5)
# 通过_in和_out后缀来定义输入和输出处理器,
# 并且还可以定义默认的ItemLoader.default_input_processor和ItemLoader.default_input_processor。
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join
class ProductLoader(ItemLoader):
default_output_processor = TakeFirst()
name_in = MapCompose(unicode.title)
name_out = Join()
price_in = MapCompose(unicode.strip)
# ...
# 直接在Field中定义添加输入/输出处理器非常方便。
# 1. 在Item Loader中定义的field_in和field_out
# 2. Filed元数据(input_processor和output_processor关键字)
import scrapy
from scrapy.loader.processors import Join, MapCompose, TakeFirst
from w3lib.html import remove_tags
def filter_price(value):
if value.isdigit():
return value
class Product(scrapy.Item):
name = scrapy.Field(
input_processor=MapCompose(remove_tags),
output_processor=Join(),
)
price = scrapy.Field(
input_processor=MapCompose(remove_tags, filter_price),
output_processor=TakeFirst(),
)
# Item Loader上下文被所有输入/输出处理器共享。
def parse_length(text, loader_context):
unit = loader_context.get('unit', 'm')
# ... 这里写入长度解析代码 ...
return parsed_length
# 初始化和修改上下文的值
loader = ItemLoader(product)
loader.context['unit'] = 'cm'
loader = ItemLoader(product, unit='cm')
class ProductLoader(ItemLoader):
length_out = MapCompose(parse_length, unit='cm')
# 用于大规模代码的维护操作,使用公共的设置进行操作。
# 操作删除指定符号,建议在后期的数据清洗部分处理。
from itemloaders.processors import MapCompose
from myproject.ItemLoaders import ProductLoader
def strip_dashes(x):
return x.strip('-')
class SiteSpecificLoader(ProductLoader):
name_in = MapCompose(strip_dashes, ProductLoader.name_in)
from itemloaders.processors import MapCompose
from myproject.ItemLoaders import ProductLoader
from myproject.utils.xml import remove_cdata
class XmlProductLoader(ProductLoader):
name_in = MapCompose(remove_cdata, ProductLoader.name_in)