文章目录

  • 第4章 使用Item封装数据
    • 4.1 Item 和 Field
    • 4.2 拓展Item 子类
    • 4.3 Field元数据
    • 4.4 本章小结

第4章 使用Item封装数据

   在第3章中,我们学习了从页面中提取数据的方法,本章来学习如何封装爬取到的数据。以爬取某图书网站的书籍信息为例,对于网站中的每一本书可以提取出书名、价格、作者、出版社、出版时间等多个信息字段。应该用怎样的数据结构来维护这些零散的信息字段呢?最容易想到是使用Python字典(dict)。
为了让代码变得:
(1)代码可读
(2)字段检查
(3)携带元数据
   在Scrapy中可以使用自定义的Item类封装爬取到的数据。

4.1 Item 和 Field

   Scrapy提供了以下两个类,用户可以使用它们自定义数据类(如书籍信息),封装爬取到的数据:
● Item基类
   自定义数据类(如BookItem)的基类。
● Field类
   用来描述自定义数据类包含哪些字段(name、price等)。
   自定义一个数据类,只需继承Item,并创建一系列Field对象的类属性(类似于在Django中自定义Model)即可。以定义书籍信息BookItem为例,它包含两个字段,分别为书的名字name和书的价格price,代码如下:

from scrapy import Item, Field
class BookItem(Item):name=Field()price=Field()

   Item支持字典接口,因此BookItem在使用上和Python字典类似,可按以下方式创建BookItem对象:

#分别创建book1,book2对象实例,
#book1 在创建时为属性赋值
book1=BookItem(name='Needful things',price=45.0)
book1
{'name': 'Needful things', 'price': 45.0}#book2未对属性赋值,为空
book2= BookItem()
book2
{}#为book2的属性分别赋值之后,显示输出则有对应的name和price值
book2['name']='life of PI'
book2['price']=32.5
book2
{'name': 'life of PI', 'price': 32.5}

   对字段进行赋值时,BookItem内部会对字段名进行检测,如果赋值一个没有定义的字段,就会抛出异常(防止因用户粗心而导致错误):

>>> book = BookItem()
>>> book['name'] = 'Memoirs of a Geisha'
>>> book['prize'] = 43.0 # 粗心, 把price 拼写成了prize.
KeyError  Traceback (most recent call last)
...
KeyError: 'BookItem does not support field: prize'
>>>

   访问BookItem对象中的字段与访问字典类似,示例如下:

#创建book实例,并为属性赋值
book = BookItem(name='Needful Things', price=45.0)
#获得name属性的值
book['name']
'Needful Things'
#get方法获得price的值
book.get('price',60.0)
45.0
#列表显示book属性内容
list(book.items())
[('name', 'Needful Things'), ('price', 45.0)]

   接下来,我们改写第1章example项目中的代码,使用Item和Field定义BookItem类,用其封装爬取到的书籍信息项目目录下的items.py文件供用户实现各种自定义的数据类,**items.py中实现BookItem,**代码如下:

from scrapy import Item, Field
class BookItem(Item):
name = Field()
price = Field()

   修改之前的BooksSpider,使用BookItem替代Python字典,代码如下:

from ..items import BookItem
class BooksSpider(scrapy.Spider):
...def parse(self, response):for sel in response.css('article.product_pod'):book = BookItem()book['name'] = sel.xpath('./h3/a/@title').extract_first()book['price'] = sel.css('p.price_color::text').extract_first()yield book
...

4.2 拓展Item 子类

   有些时候,我们可能要根据需求对已有的自定义数据类(Item子类)进行拓展。例如,example项目中又添加了一个新的Spider,它负责在另外的图书网站爬取国外书籍(中文翻译版)的信息,此类书籍的信息比之前多了一个译者字段,此时可以继承BookItem定义一个ForeignBookItem类,在其中添加一个译者字段,代码如下:

#继承了BookItem类来定义的新的ForeignBookItem类,并进行了内部属性的重写
class ForeignBookItem(BookItem):translator = Field()book =ForeignBookItem()
book['name'] = '巴黎圣母院'
book['price'] = 20.0
book['translator'] = '陈敬容'
#显示book
book
{'name': '巴黎圣母院', 'price': 20.0, 'translator': '陈敬容'}

4.3 Field元数据

   一项数据由Spider提交给Scrapy引擎后,可能会被递送给其他组件(Item Pipeline、Exporter)处理(第二章Scrapy原理)。
   假设想传递额外信息给处理数据的某个组件(例如,告诉该组件应以怎样的方处理数据),此时可以使用Field的元数据。请看下面的例子:

class ExampleItem(Item):x = Field(a='hello', b=[1, 2, 3]) # x 有两个元数据,a是个字符串,y = Field(a=lambda x: x ** 2)  # y 有一个元数据,a是个函数

   访问一个ExampleItem对象的fields属性,将得到一个包含所有Field对象的字典:

e= ExampleItem(x=100,y=200)
e.fields
{'x': {'a': 'hello', 'b': [1, 2, 3]},'y': {'a': <function __main__.ExampleItem.<lambda>(x)>}}type(e.fields['x'])scrapy.item.Fieldtype(e.fields['y'])scrapy.item.Field

   实际上,Field是Python字典的子类,可以通过键获取Field对象中的元数据

#子类判断
issubclass(Field, dict)
True
#获取field_x
field_x = e.fields['x'] # 注意,不要混淆e.fields['x']和e['x']
field_x
{'a': 'hello', 'b': [1, 2, 3]}
#获取field_x a
field_x['a']
'hello'
#获取field_y
field_y=e.fields['y']
field_y
{'a': <function __main__.ExampleItem.<lambda>(x)>}
#获取field_y a
field_y.get('a',lambda x:x)
<function __main__.ExampleItem.<lambda>(x)>

   接下来,看一个应用Field元数据的实际例子。假设我们要把爬取到的书籍信息写入csv文件,那每一项数据最终由Scrapy提供的CsvItemExporter写入文件(数据导出在第7章详细讲解),在爬取过程中提取到的信息并不总是一个字符串,有时可能是一个字符串列表,
例如:

book['authors'] = ['李雷', '韩梅梅', '吉姆']

   但在写入csv文件时,需要将列表内所有字符串串行化成一个字符串,串行化的方式有很多种,例如:

1. '李雷|韩梅梅|吉姆' # '|'.join(book['authors'])
2. '李雷;韩梅梅;吉姆' # ';'.join(book['authors'])
3. "['李雷', '韩梅梅', '吉姆']" # str(book['authors'])

   我们可以通过authors字段的元数据告诉CsvItemExporter如何对authors字段串行化:

class BookItem(Item):...authors = Field(serializer=lambda x: '|'.join(x))...

   其中,元数据的键serializer是CsvItemExporter规定好的,它会用该键获取元数据,即一个串行化函数对象,并使用这个串行化函数将authors字段串行化成一个字符串
   以下是Scrapy源码中的相关实现:

# exports.py
class BaseItemExporter(object):...def _get_serialized_fields(self, item, default_value=None,include_empty=True)for field_name in field_iter:if field_name in item:field = {} if isinstance(item, dict) else item.fields[field_name]value = self.serialize_field(field, field_name, item[field_name])else:value = default_valueyield field_name, value
...
class CsvItemExporter(BaseItemExporter):...def export_item(self, item):...fields = self._get_serialized_fields(item, default_value='' " , include_empty=True)values = list(self._build_row(x for _, x in fields))               self.csv_writer.writerow(values)...def serialize_field(self, field, name, value):serializer = field.get('serializer', self._join_if_needed)...

   解释上述代码如下:
● 爬取到的每一项数据由export_item方法导出到文件,写入文件之前,先调用_get_serialized_fields方法(在基类中实现)获得数据中每个字段串行化的结果。
● 在_get_serialized_fields方法中调用serialize_field方法,获取其中一个字段串行化的结果。
● 在serialize_field方法中获取字段的元数据serializer,得到串行化函数(如果不存在,就使用默认的_join_if_needed函数),最终调用该函数对字段串行化,并将结果返回。在实际应用中,我们可以仿照上面的例子灵活使用Field元数据。

4.4 本章小结

   本章介绍了在Scrapy中如何封装爬取到的数据,先了解了Item基类以及用来定义字段的Field类,然后展示了一个使用它们封装数据的例子。最后,还介绍了使用Field元数据给其他组件传递信息的方法。、

   本文参照《精通Scrapy网络爬虫+(刘硕著)》PDF,并自己跑相关代码,代码内容稍作修改,仅做参考和笔记复习使用

chapter 4 使用Item封装数据相关推荐

  1. Scrapy从入门到精通(3)--使用Item封装数据

    使用Item封装数据 前两篇博客介绍了从页面中提取数据的方法,现在用item封装爬取到的数据 Item和Field Scrapy提供了Item和Field类,可以用他们自定义数据类,封装爬取到的数据 ...

  2. 关于Jason封装数据使其在前端展示的简单操作

    这个例子是将一个简单的对象封装在Jason中,然后通过servlet发送到服务器中,编写一个前端页面,展示封装在jason中的数据信息: 首先编写servlet: @WebServlet(" ...

  3. 前端 json数据转txt文本并下载(前端自定义封装数据并下载文件)

    前端 json数据转txt文本并下载 在工作中,有一次客户要求将图形化的数据转化为txt文件下载 本例中也可前端自定义封装数据并下载文件 其后端传来的数据为json格式,需要前端转化为text数据的形 ...

  4. java_web:使用javabean实现登录操作(封装业务逻辑的javabean,且使用封装数据的实体类传参)

    javabean的使用 1.将jsp中要用到的登录操作的代转移到java类中 2.javabean的作用: a.减轻jsp的复杂度 b.提高代码复用 3.javabean的定义 a.public修饰的 ...

  5. python操作数据库及并封装数据库函数

    python操作数据库及并封装数据库函数 目录 python操作数据库及并封装数据库函数 一.前提 1.pyton连接数据库需要先安装pymysql模块: 2.安装完成后导入pymysql模块: 二. ...

  6. 如何使用scrapy的item来封装数据

    引言 在第一篇如何写第一个scrapy里面,我们是使用字典来对数据进行传递,使用字典有以下缺点. 无法直观地了解数据中包含哪些字段 缺乏对字段名字的检测 不便于携带元数据 为了克服上述问题,我们可以使 ...

  7. axios封装 —— 数据缓存、防止重复请求、动态加载

    前言 看了网上很多axios的封装,感觉都不是特别完善.于是我写了个比较完整的封装包括以下功能: 上传下载文件时的header设置 错误相应的统一处理 动态加载api 数据缓存.清除缓存.缓存级别.最 ...

  8. 前端学习(2477):封装数据接口

    request.js <template> <div class="artical-container"><!--卡片--><el-car ...

  9. 前端学习(2452):封装数据接口

    request.js <template> <div class="artical-container"><!--卡片--><el-car ...

  10. python--从入门到实践--chapter 15 16 17 生成数据/下载数据/web API

    1.随机漫步 random_walk.py from random import choice class RandomWalk():def __init__(self, num_points=500 ...

最新文章

  1. 鸡年除夕全天微信红包收发量达142亿个增长75.7%
  2. [Android]为指定的应用创建桌面快捷方式
  3. 金山词霸2012不能在PDF中取词 解决办法
  4. 分享几个简单的WPF控件(代码)
  5. php active控件,php – 使用TbActiveForm选择列表中的动态选项
  6. ZOJ 3635 Cinema in Akiba[ 大规模阵列 ]
  7. 你与顶级架构师的差距,可能就在这个数据中台解决方案
  8. python黑色背景白色背景_用Python去除图像的黑色或白色背景实例
  9. appium 原理解析
  10. mysql主从最大保护_MySQL主从复制(Master-Slave)实践
  11. python爬虫动态加载_简单Python爬虫,动态加载问题
  12. Jzoj3780 Magical GCD
  13. 01.linux内核源码结构
  14. 小软件项目开发的管理
  15. 巧用JMP公式,让新变量的生成更简单
  16. 有哪些方法能将纸质书籍转成PDF电子版
  17. 科学家提出记忆形成新解 大脑玩的拼图游戏
  18. mysql概念模型中的3种基本联系_数据库建模三步骤:概念模型
  19. 【数据库设计-2】权限设计-系统登录用户权限设计
  20. 基于华为java编程规范的checkstyle.xml以及格式化模版,注释模版

热门文章

  1. https://www.jb51.net/article/146628.htm
  2. workerman wss 配置备忘录
  3. java 条形码_Java 生成、识别条形码
  4. 多文件自平衡云传输 (五)资源管理中心篇 —————— 开开开山怪
  5. JavaWeb项目——疫情数据可视化
  6. 深度学习方法实现红外图片中人物动作识别
  7. Oracle查询成绩高于成绩,Oracle认证考试成绩查询方法
  8. android红外线开发实例,Android实例-红外线操作(XE10.2+小米5)
  9. 从零开始搭建ROS移动机器人系列之(四)直流电机PWM调速
  10. PDF文件怎么加密?这两种加密方法很靠谱