文章目录
- 使用 Item 封装数据
- (一)Item 基类
- 1. 自定义 Item
- 2. 拓展 Item
- (二)Field 元数据
- (三)在多个爬虫里使用 Item
使用 Item 封装数据
我们首先来看上一篇文章在最后所写的那个实例中的 spider.py :
python">import scrapy
class BooksSpider(scrapy.Spider):
name = 'books'
allowed_domains = ['books.toscrape.com']
start_urls = ['http://books.toscrape.com/']
def parse(self, response):
# 提取数据
for book in response.css('article.product_pod'):
name = book.xpath('./h3/a/@title').extract_first()
price = book.css('p.price_color::text').extract_first()
yield {
'name': name,
'price': price,
}
# 提取链接
next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
if next_url:
next_url = response.urljoin(next_url)
yield scrapy.Request(next_url, callback=self.parse)
在该案例中,我们使用了 Python 字典存储一本书的信息,但字典可能有以下缺点:
- 无法一目了然地了解数据中包含哪些字段,影响代码可读性;
- 缺乏对字段名字的检测,容易因程序员的笔误而出错;
- 不便于携带元数据(传递给其他组件的信息)。
为解决上述问题,在 Scrapy 中可以使用自定义的 Item 类封装爬取到的数据。
(一)Item 基类
1. 自定义 Item
Scrapy提供了 Item 和 Field 类,可以用他们自定义数据类,封装爬取到的数据。
- Item:自定义数据类的基类
- Field:描述自定义数据类包含那些字段
自定义一个数据类,只需要继承 Item,并创建一系列 Field 对象的类属性。例如,在前面的示例中,我们使用 Item 基类封装数据如下:
python">from scrapy import Item, Field
class BookItem(Item):
name = Field()
price = Field()
Item 支持字典接口,因此 BookItem 在使用上和 Python 字典类似。因此,访问 BookItem 对象中的字段与访问字典类似。
修改之前的 BooskSpider,使用 BookItem 替代 Python 字典,代码如下:
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
...
2. 拓展 Item
根据需求对已有的自定义数据类(Item子类)进行拓展。例如:toscrapy 项目中又添加了一个新的 Spider,它负责另一个网站爬取信息:
python"># 继承BookItem,定义一个ForeigenBookItem类,在其中添加一个翻译字段
class ForeignBookItem(BookItem):
translator = Field()
该类 ForeignBookItem 也可以在对应的爬虫文件里使用。
(二)Field 元数据
数据由 Spider 交给 Scrapy 引擎后,可能会被传递给其他组件 (Item Pipeline、Exporter) 处理。可以使用 Field 的元数据传递额外的信息给处理数据的某个组件(例如告诉组件以何种方式处理数据)。访问一个 Item 对象的 fields 属性,将得到一个包含所有 Field 对象的字典;Field 是 Python 字典的子类,可以通过键值对获取 Field 对象中的元素。
接下来,看一个应用 Field 元数据的实际例子。假设我们要把爬取到的书籍信息写入 CSV 文件,那每一项数据最终由Scrapy 提供的 CsvItemExporter 写入文件,在爬取过程中提取到的信息并不总是一个字符串,有时可能是一个字符串列表,例如:
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 字段串行化成一个字符串。
(三)在多个爬虫里使用 Item
例如有两个爬虫:腾讯爬虫和京东爬虫,使用 Item 的方法如下:
-
Step 1:在 items.py 里面创建不同的类,分别保存各自的字段
python">class TencentItem(scrapy.Item): """腾讯爬虫要爬取的字段""" """定义好字段,并不代表真正的值,只是占一个位置,用的时候直接赋值就行""" position = scrapy.Field() category = scrapy.Field() date = scrapy.Field() class JdItem(scrapy.Item): """京东爬虫要爬取的字段""" """定义好字段,并不代表真正的值,只是占一个位置,用的时候直接赋值就行""" position = scrapy.Field() category = scrapy.Field() date = scrapy.Field()
-
Step 2:然后在不同的爬虫程序里使用对应的类即可
在腾讯的爬虫里,导入和使用 Item
python">import scrapy from tencent.items import TencentItem class TencentSpiderSpider(scrapy.Spider): ... def parse(self, response): pass for tr in tr_list: """使用定义好的腾讯爬虫的类的字段""" item = TencentItem() yield item
在京东的爬虫中,同样的用法
python">import scrapy from JD.items import JdItem class JdSpiderSpider(scrapy.Spider): ... def parse(self, response): pass for tr in tr_list: """使用定义好的腾讯爬虫的类的字段""" item = JdItem() yield item
-
Step 3:对于多个爬虫,在pipelines,py中可以进行判断,分别对不同的爬虫的字段进行不同的处理。
isinstance()
函数就是用来判断一个对象是否是一个已知的类型python">from tencent.items import TencentItem, JdItem class TencentPipeline(object): def process_item(self, item, spider): '''针对与不同的爬虫字段类的对象,做不同的处理''' if isinstance(item, TencentItem): ... if isinstance(item, JdItem): ... return item
上述文章内容如有错误,欢迎各位读者在评论区留言!