【Scrapy 框架】「版本2.4.0源码」物品加载(Item Loaders)详解篇

全部源码解析文章索引目录传送门

【Scrapy 框架】版本 2.4.0 源码篇:全部配置目录索引

内容介绍

本章节介绍源码中的案例,个人感觉处理数据的操作比较繁琐,将数据处理的流程简化到最简的内容在专栏中的爬虫示例中,如果觉得文章中数据处理繁琐的小伙伴可以跳转过去直接看示例。

Item Loader为我们提供了生成Item的相当便利的方法。Item为抓取的数据提供了容器,而Item Loader可以让我们非常方便的将输入填充到容器中。

ItemLoader 参数定义

item:加载器解析的Item对象。

context:当内容的ItemLoader。

default_item_class:实例化__init__方法。

default_input_processor:指定字段的默认输入处理器。

default_output_processor:指定字段的默认输出处理器。

default_selector_class:用于构造selector其中ItemLoader。如果在__init__方法存在则忽略此属性。此属性有时在子类中被重写。

selector:这个Selector对象中提取数据。

add_css(field_name, CSS, *processors, **kw):
类似于ItemLoader.add_value()但是接收一个css选择器,而不是一个值,该值用于从与此相关的选择器中提取Unicode字符串列表。

# HTML snippet: <p class="product-name">Color TV</p>
loader.add_css('name', 'p.product-name')
# HTML snippet: <p id="price">the price is $1200</p>
loader.add_css('price', 'p#price', re='the price is (.*)')

add_value(field_name, value, *processors, **kw):处理添加给定的value对于给定的字段。get_value()通过给processors和kwargs,添加数据使用

loader.add_value('name', 'Color TV')
loader.add_value('colours', ['white', 'blue'])
loader.add_value('length', '100')
loader.add_value('name', 'name: foo', TakeFirst(), re='name: (.+)')
loader.add_value(None, {
    
    'name': 'foo', 'sex': 'male'})

add_xpath(field_name, value, *processors, **kw):
类似于ItemLoader.add_value()但是接收一个XPath,而不是一个值,该值用于从与此相关的选择器中提取字符串列表。

# HTML snippet: <p class="product-name">Color TV</p>
loader.add_xpath('name', '//p[@class="product-name"]')
# HTML snippet: <p id="price">the price is $1200</p>
loader.add_xpath('price', '//p[@id="price"]', re='the price is (.*)')

get_collected_values(field_name):返回字段的收集值。

get_css(CSS, *processors, **kw):类似于ItemLoader.get_value()但是接收一个css选择器,而不是一个值,该值用于从与此相关的选择器中提取Unicode字符串列表。

# HTML snippet: <p class="product-name">Color TV</p>
loader.get_css('p.product-name')
# HTML snippet: <p id="price">the price is $1200</p>
loader.get_css('p#price', TakeFirst(), re='the price is (.*)')

get_output_value(field_name):返回使用输出处理器为给定字段解析的收集值。

get_value(value, *processors, **kw):处理给定的value被给予processors关键字参数。

>>> from itemloaders import ItemLoader
>>> from itemloaders.processors import TakeFirst
>>> loader = ItemLoader()
>>> loader.get_value('name: foo', TakeFirst(), str.upper, re='name: (.+)')
'FOO'

get_xpath(XPath, *processors, **kw):类似于ItemLoader.get_value()但是接收一个XPath,而不是一个值,该值用于从与此相关的选择器中提取Unicode字符串的列表。

# HTML snippet: <p class="product-name">Color TV</p>
loader.get_xpath('//p[@class="product-name"]')
# HTML snippet: <p id="price">the price is $1200</p>
loader.get_xpath('//p[@id="price"]', TakeFirst(), re='the price is (.*)')

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()但取代收集的数据而不是添加数据。

数据处理示例

定义Items

import scrapy
 
class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)

Spider中加载填充数据

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)

声明Item Loader

通过_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定义中声明输入/输出处理器

直接在Field中定义添加输入/输出处理器非常方便。

  1. 在Item Loader中定义的field_in和field_out
  2. Filed元数据(input_processor和output_processor关键字)
  3. Item Loader中的默认的
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上下文

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')

重写和拓展 Item Loaders

用于大规模代码的维护操作,使用公共的设置进行操作。

操作删除指定符号,建议在后期的数据清洗部分处理。

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)

猜你喜欢

转载自blog.csdn.net/qq_20288327/article/details/113494261
今日推荐