直接用response的xpath或css方法可以很方便地进行解析,但是代码通常会写得很杂乱。而且有时候对网页进行解析的时候,会遇到同一个字段有多个xpath逻辑的情况。例如,我在解析亚马逊商品页的时候
name = response.xpath('//span[@id="productTitle"]/text()').extract_first("")
if not name:
name = response.xpath('//span[@id="ebooksProductTitle"]/text()').extract_first("")
Scrapy提供了ItemLoader,可以让代码简洁不少。
from scrapy.loader import ItemLoader
item_loader = ItemLoader(item=Item(),response=response) #通过Item和response实例化ItemLoader
item_loader.add_xpath('name','//span[@id="productTitle"]/text()')
item_loader.add_xpath('name','//span[@id="ebooksProductTitle"]/text()')
#有需要的话可以添加参数processors和参数re
#如item_loader.add_xpath('name','//span[@id="productTitle"]/text()*',TakeFirst(),re='.*?(\d+).*')
yield item_loader.load_item()
ItemLoader提供了add_xpath(), add_css(), add_value()等方法对字段进行填充。add_css()和add_xpath()类似,而add_value()则是直接将值添加进字段对应的list中。
这里会将从两个xpath中提取到的值,组装成一个list存放在item_loader中。注意现在还只是存放在item_loader中,在对所有字段完成xpath提取之后,要通过load_item()方法将数据传入item中,其返回值就是item。
数据——>输入处理器——>ItemLoader——>输出处理器——>Item
另外要注意的是,如果不进行任何处理的话,现在得到的item会是这样
{
'name': [value1, value2]
}
为了处理这些数据,我们可以对item_loader中的输入处理器和输出处理器进行修改。
方法一:编写自己的ItemLoader
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose
class ProductItemLoader(ItemLoader):
default_output_processor = TakeFirst() #更改所有字段默认的输出处理器
name_in = MapCompose(function1,function2) #更改name字段的输入处理器
可以选择通过更改default_input_processor和default_output_processor的方法,更改所有字段默认的输入输出处理器(原本是什么也不做)。也可以通过修改 字段_in和字段_out 来为某个字段选择输入输出处理器。
这里TakeFirst方法,作用是取出list中第一个不为None和空字符串的值,即经过输出处理器后'name'字段的值变为value1。MapCompose方法则是将list中的每一个元素,依次被function1和function2两个函数处理,大家可以自行编写处理函数,函数的参数就是value。
事实上,这里的输入/输出处理器就是带有__call__方法的类,有需要的话可以自行编写。例如:
class NameHandle(object):
#将name字段的两个value合并,并取出
def __call__(self, values):
name = ''
for value in values:
if value is not None:
name += value
return name
方法二:在Item的字段中设置
class ProductspiderItem(scrapy.Item):
name = scrapy.Field(
input_processor = MapCompose(DeleteSpace),
output_processor = TakeFirst()
)
修改input_processor和output_processor即可
有时候我们会希望将多级页面爬取到的数据组装成一个item,如果还想使用ItemLoader的话,可以用parent参数进行数据传递。例如:
class ProductSpider(scrapy.Spider):
def parse(self,response):
#略
yield Request(url=parse.urljoin(response.url,review_url), meta={'item_loader':item_loader1},callback=self.parse_review)
def parse_review(self,response):
item_loader2 = ItemLoader(item=ProductspiderItem(),response=response,parent=response.meta.get('item_loader'))
这样一来parse_review中的item_loader2将会继承上级函数parse中item_loader1中存放的数据
#以上是我在用Scrapy写项目的过程中自己查文档和总结的,如果有错误欢迎指出