scrapy使用Item Loaders加载器来提取数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38684419/article/details/81636876

爬虫的主要任务就是·从非结构化的数据中获得结构化的数据。

Item提供了盛装抓取到的数据的容器。而Item Loader提供了填充该容器的机制。

项目加载器(Item Loaders)提供了一种灵活、高效和容易的机制,通过爬虫或源格式(HTML、XML等)扩展和覆盖不同的字段解析规则。这种方法有利于后期的维护。

输入和输出处理器

项目加载器对于每个(项目)字段包含一个输入处理器和一个输出处理器。输入处理器只要他的接收所处理的数据(通过add_xpath(), add_css()或者add_value()方法)和输入的结果被收集并保持Item Loader内部。收集所有数据后,ItemLoader.load_item()调用该方法来填充和获取Item对象。在这一步中先调用输出处理器来处理之前收集到的数据,然后再存入Item中,输出处理器的结果是分配给项目的最终值。

声明Item Loader加载器

项目加载器是通过类定义语法声明为Items。下面代码在items.py

​
import re
import scrapy
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose, TakeFirst


def changeTitle(value):
    value = '标题:' + value
    return value

def getNewTime(value):
    newTime = value.split('·')[0]
    newTime = newTime.strip()
    return newTime

def getNum(value):
    pattern = re.compile(r'\d+', re.S)
    res = re.findall(pattern, value)
    if res:
        return res[0]
    else:
        return 0

class ArticleItemLoader(ItemLoader):
    # 设置输出处理器
    # TakeFirst获取所有数据当中的第一条数据
    default_output_processor = TakeFirst()
    # 默认的输出处理器,其返回值是一个列表
    # default_output_processor = ItemLoader.default_output_processor
    # 默认的输入处理器
    # default_input_processor = ItemLoader.default_input_processor



class JobboleItem(scrapy.Item):
    img = scrapy.Field()
    title = scrapy.Field(input_processor=MapCompose(changeTitle))
    time = scrapy.Field(input_processor=MapCompose(getNewTime))
    detail_url = scrapy.Field()
    zan = scrapy.Field()
    book_mark = scrapy.Field(input_processor=MapCompose(getNum))
    comment = scrapy.Field(input_processor=MapCompose(getNum))

​

也可以在定义Item的时候声明输入输出处理器,例如

​
class JobboleItem(scrapy.Item):
    title = scrapy.Field(
        input_processor=MapCompose(changeTitle),
        output_processor=TakeFirst(),
    )
    time = scrapy.Field(
        input_processor=MapCompose(getNewTime),
        output_processor=Join(),
    )

Field 字段有两个参数: 

  • 第一个是输入处理器(input_processor) ,当这个item,title这个字段的值传过来时,可以在传进来的值上面做一些预处理。
  • 第二个是输出处理器(output_processor) , 当这个item,title这个字段被预处理完之后,输出前最后的一步处理。

内置的处理器

scrapy.loader.processors.Identity

最简单的处理器,什么都不做。它返回原始值不变。它不接收任何构造函数参数,也不接受Loader上下文。

例如:

>>> from scrapy.loader.processors import Identity
>>> proc = Identity()
>>> proc(['one', 'two', 'three'])
['one', 'two', 'three']

scrapy.loader.processors.TakeFirst

从接收到的值中返回第一个非空(non-null/non-empty)值,因此通常用作单值字段的输出处理器。它不接收任何构造参数,也不接收Loader上下文。

例如:

>>> from scrapy.loader.processors import TakeFirst
>>> proc = TakeFirst()
>>> proc(['', 'one', 'two', 'three'])
'one'

scrapy.loader.processors.Join(separator=u' ')

返回与构造函数中给定的分隔符连接的值,默认为空格。它不接受加载器上下文(Loader contexts)

当使用默认分隔符时,此处理器相当于以下功能: u' '.join

例如:

>>> proc = Join()
>>> proc(['one', 'two', 'three'])
u'one two three'
>>> proc = Join('<br>')
>>> proc(['one', 'two', 'three'])
u'one<br>two<br>three'

填充数据

ItemLoader提供了三种填充数据的方式。

​
​
​def parse(self, response):
    # 创建ItemLoader实例化对象,需要两个参数。
    # 第一个参数为item的实例化对象
    # 网页的源码,即response
    item_loader = ArticleItemLoader(item=JobboleItem(), response=response)
    # 通过xpath选择器,提取信息
    item_loader.add_xpath('title', '//div[@class="entry-header"]/h1/text()')
    # 直接给字段赋值
    item_loader.add_value('url', response.url)
    # item_loader.add_value('img', response.meta['img'])
    # 通过css选择器,提取信息
    item_loader.add_css('time', 'div[class=entry-meta] p::attr(text())')
    item_loader.add_value('detail_url', response.url)
    item_loader.add_xpath('zan', '//div[@class="post-adds"]/span/h10/text()')
    item_loader.add_xpath('book_mark', '//span[contains(@class,"bookmark-btn")]/text()')
    item_loader.add_xpath('comment', '//a[@href="#article-comment"]/span/text')

​
    #  将itemloader加载器中保存的值收集
    item = item_loader.load_item()
    yield item
  • 第一个参数:指定字段名,如title。
  • 第二个参数:指定对应的提取规则,或者传值。
  • 前面调用add_xpath等只是将提取的数据收集起来。最终,当所有数据被收集起来之后,还需要调用 ItemLoader.load_item() 方法, 实际上填充并且返回了之前通过调用 add_xpath(),add_css(),and add_value() 所提取和收集到的数据。
  • 默认情况下,这些字段填入的全部是list类型。就算是传值,传递了一个url,但是结果依然是一个list。
  • 我们可以对每个字段进行配置,匹配映射,非常的清晰,大大方便了可配置性和可维护性。
  • 总结一下,每个字段的数据的处理过程是:

    • 第一步, 通过 add_xpath(), add_css() 或者 add_value() 方法),提取到数据。
    • 第二步,将提取到的数据,传递到输入处理器(input_processor)中进行处理,处理结果被收集起来,并且保存在ItemLoader内(但尚未分配给该Item)。
    • 第三步,最后调用输出处理器(output_processor)来处理之前收集到的数据(这是最后一步对数据的处理)。然后再存入到Item中,输出处理器的结果是被分配到Item的最终值。
    • 第四步,收集到所有的数据后, 调用ItemLoader.load_item() 方法来填充,并得到填充后的 Item 对象。
  • 需要注意的是:input_processor和output_processor都是可调用对象,调用时传入需要被分析的数据, 处理后返回分析得到的值。因此你可以使用任意函数作为输入、输出处理器。唯一需注意的是它们必须接收一个(并且只是一个)迭代器性质的参数。

Compose

  • 用给定的多个函数的组合,来构造的处理器。list对象(注意不是指list中的元素),依次被传递到第一个函数,然后输出,再传递到第二个函数,一个接着一个,直到最后一个函数返回整个处理器的输出。
  • 默认情况下,当遇到None值(list中有None值)的时候停止处理。可以通过传递参数stop_on_none = False改变这种行为。
# 单独直接使用
from scrapy.loader.processors import Compose

# stop_on_none=True, 指定在遇到None时,不用中断,还继续处理
# lambda v: v[0], 指定取第一个元素
# str.upper , 大写
proc = Compose(lambda v: v[0], str.upper, stop_on_none=True)

# 接收对象是一个可迭代的对象,如list
result = proc(['one', 'two', None, 'three'])

# 结果:result = ONE
print(f"result = {result}")
  • 每个函数可以选择接收一个loader_context参数。

MapCompose

  • 与Compose处理器类似,区别在于各个函数结果在内部传递的方式(会涉及到list对象解包的步骤): 
    • 输入值是被迭代的处理的,List对象中的每一个元素被单独传入,第一个函数进行处理,然后处理的结果被连接起来形成一个新的迭代器,并被传入第二个函数,以此类推,直到最后一个函数。最后一个函数的输出被连接起来形成处理器的输出。
    • 每个函数能返回一个值或者一个值列表,也能返回None(会被下一个函数所忽略)
    • 这个处理器提供了很方便的方式来组合多个处理单值的函数。因此它常用于输入处理器,因为传递过来的是一个List对象。
# 单独直接使用

from scrapy.loader.processors import MapCompose

def add_firstStr(value):
    return value + "_firstAdd"

def add_secondStr(value):
    return value + "_secondAdd"


# stop_on_none=True, 指定在遇到None时,不用中断,还继续处理
# 依次处理每个list元素
proc = MapCompose(add_firstStr, add_secondStr, str.upper, stop_on_none=True)

# 接收对象是一个可迭代的对象,如list
result = proc(['one', 'two', 'three'])

# 结果:result = ['ONE_FIRSTADD_SECONDADD', 'TWO_FIRSTADD_SECONDADD', 'THREE_FIRSTADD_SECONDADD']
print(f"result = {result}")
  • 与Compose处理器类似,它也能接受Loader context。

猜你喜欢

转载自blog.csdn.net/qq_38684419/article/details/81636876