【Scrapy 框架翻譯】物品載入(Item Loaders)詳解篇
技術標籤:# Scrapy 資料採集pythonscrapy爬蟲原始碼Item Loaders
版本號:Scrapy 2.4
文章目錄
內容介紹
本章節介紹原始碼中的案例,個人感覺處理資料的操作比較繁瑣,將資料處理的流程簡化到最簡的內容在專欄中的爬蟲示例中,如果覺得文章中資料處理繁瑣的小夥伴可以跳轉過去直接看示例。
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):
# 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中定義新增輸入/輸出處理器非常方便。
- 在Item Loader中定義的field_in和field_out
- Filed元資料(input_processor和output_processor關鍵字)
- 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)