Scrapy框架(四) 使用Pipeline处理数据(一)

一、Item Pipeline简介

Item Pipeline的主要责任是负责处理爬虫从网页中抽取的Item,它的主要任务是清晰、验证和存储数据。
当页面被爬虫解析后,将被发送到Item管道,并经过几个特定的次序处理数据。
每个初始Item管道的组件都是由一个简单的方法组成的Python类。
如下所示:

1
2
3
class DemoPipeline(object):
def process_item(self, item, spider):
return item

但是我们没有具体定义,因此执行爬虫并不会输出结果。
他们获取了Item并执行他们的方法,同时他们还需要确定的是是否需要在Item管道中继续执行下一步或是直接丢弃掉不处理。


二、Item管道的通常执行过程

  • 清理HTML数据  
  • 验证解析到的数据(检查Item是否包含必要的字段)  
  • 检查是否是重复数据(如果重复就删除)  
  • 将解析到的数据存储到数据库中  

三、编写pineline规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class DemoPipeline(object):
def __init__(self):
pass
# 可选方法,用于做参数初始化等操作,常用于保存item到文件中穿件文件并打开时用到

# doing someting
def process_item(self, item, spider):
pass
return item #如果有下一个管道,为了传给下一个管道时用
# 参数item---被爬取的item
# 参数spider--爬取该item的spider
# 该方法process_item是必须的方法,必须返回一个Item对象或者抛出DropItem异常

def open_spider(self, spider):
pass
# 参数spider---被开启的spider
# 当spider被开启时,该方法open_spider会被调用
# 该方法open_spider为可选方法

def close_spider(self, spider):
pass
# 参数spider---被关闭的spider
# 当spider爬虫被关闭时,close_spider方法会被调用
# 该方法close_spider为可选方法

注意:

在settings.py文件中默认是关闭pipeline管道的。因此如果要使用自定义的管道文件,还需要在settings.py文件中启用pipeline。如果你在pipeline定义了多个管道的类,在settings.py中可以写多个pipeline的类,通过后面的数字的优先级区分哪个先执行。优先级的取值范围为0到1000,数值越小优先级越高。

例如:

1
2
3
4
5
6
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'Demo.pipelines.DemoPipeline1': 300,
'Demo.pipelines.DemoPipeline2': 100,
}

上述DemoPipeline2先执行。


四、Item Pipeline例子

4.1 数据清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 注:VAT:Value Added Tax(增值税)
# 以上代码可以过滤那些没有价格的产品,并且对那些不包括增值税产品的价格进行调整
from scrapy.exceptions import DropItem

class DemoPipeline(object):

vat_factor = 1.15

def process_item(self,item,spider):
if item['price']:
if item['price_excludes_vat']:
item['price'] = item['price'] * self.vat_factor
return item
else:
raise DropItem("Missing price in %s" % item)

4.2 把Item写入Json文件

还是之前的项目Demo, 我们直接修改 pipelines.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 导入Json模块
import json

class DemoPipeline(object):
def __init__(self):
# 生成文件对象
self.fp = open('C:\QzmVc1\Code\PyCharm\Python_Project\Python_Spider\Spider_Test1\Demo\scrapy.json','wb')

def process_item(self,item,spider):
# 将item转换为json格式
data = json.dumps(dict(item),ensure_ascii=False)
# 写入json文件
self.fp.write(data.encode('utf-8'))
return item
def close_spider(self,spider):
# 关闭文件
self.fp.close()

scrapy.json文件内容:

有的时候真的不能只看不练,当自己真正上手写的时候,才会发现自己的基础是那么差,漏洞百出。这里写的时候又碰到N多个坑,慢慢来吧,唉…

我同时也会把这些坑加入到我的这篇博客:Scrapy框架之细数到目前为止我遇到的坑

坑1:json.dumps的参数ensure_ascii=False:

在使用json.dumps时要注意一个问题

1
2
3
4
5
6
>>> import json
>>> print json.dumps('中国')

输出:"\u4e2d\u56fd"
输出的会是
'中国' 中的ascii 字符码,而不是真正的中文。

因为json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False;

坑2:中文保存到json文件中产生乱码

如上述代码所示转换为’UTF-8’编码,因为str.encode返回值是一个bytes对象,因此打开文件的方式要写成 ‘wb’ 。
也可以通过在settings.py中添加如下代码:

FEED_EXPORT_ENCODING = ‘UTF-8’

4.3 删除重复数据

一个用于去重的过滤器,丢弃那些已经被处理过的item。假设item有一个唯一的id,但是我们spider返回的多个item中包含了相同的id,去重方法如下:这里初始化了一个集合,每次判断id是否在集合中已经存在,从而做到去重的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

def __init__(self):
self.ids_seen = set()

def process_item(self, item, spider):
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item

4.4 将数据写入数据库

见下一章


五、激活Item Pipeline组件

在settings.py文件中,往ITEM_PIPELINES中添加项目管道的类名,就可以激活项目管道组件,如:

1
2
3
4
ITEM_PIPELINES = {
'Demo.pipelines.DemoPipeline1': 300,
'Demo.pipelines.DemoPipeline2': 100,
}

整数值通常设置在0-1000之间,数值越低代表优先级越高。