• 转 Scrapy笔记(5)- Item详解


    Item是保存结构数据的地方,Scrapy可以将解析结果以字典形式返回,但是Python中字典缺少结构,在大型爬虫系统中很不方便。

    Item提供了类字典的API,并且可以很方便的声明字段,很多Scrapy组件可以利用Item的其他信息。

    定义Item

    定义Item非常简单,只需要继承scrapy.Item类,并将所有字段都定义为scrapy.Field类型即可

    [python] view plain copy
     
    1. import scrapy  
    2.   
    3. class Product(scrapy.Item):  
    4.     name = scrapy.Field()  
    5.     price = scrapy.Field()  
    6.     stock = scrapy.Field()  
    7.     last_updated = scrapy.Field(serializer=str)  

    Item Fields

    Field对象可用来对每个字段指定元数据。例如上面last_updated的序列化函数指定为str,可任意指定元数据,不过每种元数据对于不同的组件意义不一样。

    Item使用示例

    你会看到Item的使用跟Python中的字典API非常类似

    创建Item

    [python] view plain copy
     
    1. >>> product = Product(name='Desktop PC', price=1000)  
    2. >>> print product  
    3. Product(name='Desktop PC', price=1000)  

    获取值

    [python] view plain copy
     
    1. >>> product['name']  
    2. Desktop PC  
    3. >>> product.get('name')  
    4. Desktop PC  
    5.   
    6. >>> product['price']  
    7. 1000  
    8.   
    9. >>> product['last_updated']  
    10. Traceback (most recent call last):  
    11.     ...  
    12. KeyError: 'last_updated'  
    13.   
    14. >>> product.get('last_updated', 'not set')  
    15. not set  
    16.   
    17. >>> product['lala'] # getting unknown field  
    18. Traceback (most recent call last):  
    19.     ...  
    20. KeyError: 'lala'  
    21.   
    22. >>> product.get('lala', 'unknown field')  
    23. 'unknown field'  
    24.   
    25. >>> 'name' in product  # is name field populated?  
    26. True  
    27.   
    28. >>> 'last_updated' in product  # is last_updated populated?  
    29. False  
    30.   
    31. >>> 'last_updated' in product.fields  # is last_updated a declared field?  
    32. True  
    33.   
    34. >>> 'lala' in product.fields  # is lala a declared field?  
    35. False  

    设置值

    [python] view plain copy
     
    1. >>> product['last_updated'] = 'today'  
    2. >>> product['last_updated']  
    3. today  
    4.   
    5. >>> product['lala'] = 'test' # setting unknown field  
    6. Traceback (most recent call last):  
    7.     ...  
    8. KeyError: 'Product does not support field: lala'  

    访问所有的值

    [python] view plain copy
     
    1. >>> product.keys()  
    2. ['price', 'name']  
    3.   
    4. >>> product.items()  
    5. [('price', 1000), ('name', 'Desktop PC')]  

    Item Loader

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

    下面我们通过一个例子来展示一般使用方法:

    [python] view plain copy
     
    1. from scrapy.loader import ItemLoader  
    2. from myproject.items import Product  
    3.   
    4. def parse(self, response):  
    5.     l = ItemLoader(item=Product(), response=response)  
    6.     l.add_xpath('name', '//div[@class="product_name"]')  
    7.     l.add_xpath('name', '//div[@class="product_title"]')  
    8.     l.add_xpath('price', '//p[@id="price"]')  
    9.     l.add_css('stock', 'p#stock]')  
    10.     l.add_value('last_updated', 'today') # you can also use literal values  
    11.     return l.load_item()  

    注意上面的name字段是从两个xpath路径添累加后得到。

    输入/输出处理器

    每个Item Loader对每个Field都有一个输入处理器和一个输出处理器。输入处理器在数据被接受到时执行,当数据收集完后调用ItemLoader.load_item()时再执行输出处理器,返回最终结果。

    [python] view plain copy
     
    1. l = ItemLoader(Product(), some_selector)  
    2. l.add_xpath('name', xpath1) # (1)  
    3. l.add_xpath('name', xpath2) # (2)  
    4. l.add_css('name', css) # (3)  
    5. l.add_value('name', 'test') # (4)  
    6. return l.load_item() # (5)  

    执行流程是这样的:

    1. xpath1中的数据被提取出来,然后传输到name字段的输入处理器中,在输入处理器处理完后生成结果放在Item Loader里面(这时候没有赋值给item)
    2. xpath2数据被提取出来,然后传输给(1)中同样的输入处理器,因为它们都是name字段的处理器,然后处理结果被附加到(1)的结果后面
    3. 跟2一样
    4. 跟3一样,不过这次是直接的字面字符串值,先转换成一个单元素的可迭代对象再传给输入处理器
    5. 上面4步的数据被传输给name的输出处理器,将最终的结果赋值给name字段

    自定义Item Loader

    使用类定义语法,下面是一个例子

    [python] view plain copy
     
    1. from scrapy.loader import ItemLoader  
    2. from scrapy.loader.processors import TakeFirst, MapCompose, Join  
    3.   
    4. class ProductLoader(ItemLoader):  
    5.   
    6.     default_output_processor = TakeFirst()  
    7.   
    8.     name_in = MapCompose(unicode.title)  
    9.     name_out = Join()  
    10.   
    11.     price_in = MapCompose(unicode.strip)  
    12.   
    13.     # ...  

    通过_in_out后缀来定义输入和输出处理器,并且还可以定义默认的ItemLoader.default_input_processorItemLoader.default_input_processor.

    在Field定义中声明输入/输出处理器

    还有个地方可以非常方便的添加输入/输出处理器,那就是直接在Field定义中

    [python] view plain copy
     
    1. import scrapy  
    2. from scrapy.loader.processors import Join, MapCompose, TakeFirst  
    3. from w3lib.html import remove_tags  
    4.   
    5. def filter_price(value):  
    6.     if value.isdigit():  
    7.         return value  
    8.   
    9. class Product(scrapy.Item):  
    10.     name = scrapy.Field(  
    11.         input_processor=MapCompose(remove_tags),  
    12.         output_processor=Join(),  
    13.     )  
    14.     price = scrapy.Field(  
    15.         input_processor=MapCompose(remove_tags, filter_price),  
    16.         output_processor=TakeFirst(),  
    17.     )  

    优先级:

    1. 在Item Loader中定义的field_infield_out
    2. Filed元数据(input_processoroutput_processor关键字)
    3. Item Loader中的默认的

    Tips:一般来讲,将输入处理器定义在Item Loader的定义中field_in,然后将输出处理器定义在Field元数据中

    Item Loader上下文

    Item Loader上下文被所有输入/输出处理器共享,比如你有一个解析长度的函数

    [python] view plain copy
     
    1. def parse_length(text, loader_context):  
    2.     unit = loader_context.get('unit', 'm')  
    3.     # ... length parsing code goes here ...  
    4.     return parsed_length  

    初始化和修改上下文的值

    [python] view plain copy
     
    1. loader = ItemLoader(product)  
    2. loader.context['unit'] = 'cm'  
    3.   
    4. loader = ItemLoader(product, unit='cm')  
    5.   
    6. class ProductLoader(ItemLoader):  
    7.     length_out = MapCompose(parse_length, unit='cm')  

    内置的处理器

    1. Identity 啥也不做
    2. TakeFirst 返回第一个非空值,通常用作输出处理器
    3. Join 将结果连起来,默认使用空格’ ‘
    4. Compose 将函数链接起来形成管道流,产生最后的输出
    5. MapCompose 跟上面的Compose类似,区别在于内部结果在函数中的传递方式.它的输入值是可迭代的,首先将第一个函数依次作用于所有值,产生新的可迭代输入,作为第二个函数的输入,最后生成的结果连起来返回最终值,一般用在输入处理器中。
    6. SelectJmes 使用json路径来查询值并返回结果
      Scrapy笔记(5)- Item详解
  • 相关阅读:
    ThinkPHP5.0框架开发--第6章 TP5.0 请求和响应
    ThinkPHP5.0框架开发实现简单的页面跳转
    ThinkPHP5.0框架开发--第5章 TP5.0 控制器
    ThinkPHP5.0框架开发--第4章 TP5.0路由
    ThinkPHP5.0框架开发--第3章 TP5.0 配置
    ThinkPHP5.0框架开发--第2章 TP5.0架构
    ThinkPHP5.0框架开发--第1章 Tp5.0安装
    ThinkPHP新建控制器
    ThinkPHP5.0最最最最最简单实例
    白话经典算法系列之七 堆与堆排序
  • 原文地址:https://www.cnblogs.com/c-x-a/p/9034559.html
Copyright © 2020-2023  润新知