Python爬虫学习笔记 2-2:Scrapy项目管道
Published On 2019/11/16 Sunday, Singapore
上一节,我们了解到scrapy框架,安装和基本使用。其中提到了项目管道的主要作用包括清洗验证数据,检查重复并删除,数据入库。这一节,我们讲解scrapy的项目管道的使用。
本文为Datacatsle Python爬虫(入门+进阶)课程学习笔记。
项目管道的主要函数
process_item(self, item, spider)
: 每个item pipeline组件都需要调用该方法,这个方法必须返回一个item对象,或是抛出DropItem异常,被丢弃的item将不会被之后的 pipeline组件所处理。需要传入的参数为:
- item:被爬取的item
- spider:爬取该item的spider
该方法会被每一个item pipeline组件所调用,必须返回一个以下任意对象
- dict
- item对象或者它的子类对象
- Twisted Deferred对象
- DropItem exception;如果返回此异常,则该item将不会被后续的item pipeline所继续访问
例1: 通过项目管道实现调整不包括增值税(price_excludes_vat)项目价格,并删除不包含价格的项目。
from scrapy.exceptions import DropItem
class PricePipeline(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: #如果没有价格,则抛弃这个item
raise DropItem("Missing price in %s" % item)
例2:通过项目管道实现查找重复item,并删除已处理的item。假设item具有唯一的ID,但是spider会返回具有相同id的多个item:
from scrapy.exceptions import DropItem
class DuplicatesPipeline(object):
def __init__(self):
self.ids_seen = set() #初始化中,创建一个空集合
def process_item(self, item, spider):
#查看id是否在ids_seen中,如果在,就抛弃该Item,如果不在就添加到ids_seen中,下一次其它Item有相同的id就抛弃那个Item
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item #记住一定要返回Item
open_spider(self, spider)
:当spider被开启时,这个方法被调用。可以实现在爬虫开启时需要进行的操作,比如说打开一个待写入的文件,或者连接数据库等。
close_spider(self, spider)
: 当spider被关闭时,这个方法被调用。可以实现在爬虫关闭时需要进行的操作,比如说关闭已经写好的文件,或者关闭与数据库的连接。
例3:将所有抓取的item存储到单个items.json文件中,以JSON格式序列化:
import json
class JsonWriterPipeline(object):
def open_spider(self, spider):
#在爬虫开始时打开文件
self.file = open('items.json', 'w')
def close_spider(self, spider):
#在爬虫结束时关闭文件
self.file.close()
def process_item(self, item, spider):
#把爬取到的item转换为json格式,保存进文件
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item #注意要返回item
from_crawler(cls, crawler)
:该类方法用来从crawler中初始化得到一个 pipeline实例;它必须返回一个新的pipeline实例;crawler对象提供了访问所有scrapy核心组件的接口,包括settings和signals。
例4:使用pymongo
将项目写入MongoDB。MongoDB地址和数据库名称在Scrapy设置中指定; MongoDB集合以item类命名。from_crawler()
方法是创建通往crawler的pipeline,返回一个新的pipeline实例。这个例子的要点是显示如何使用from_crawler()
方法和如何正确清理资源。通过类方法from_crawler()
在内部初始化得到了一个 pipeline实例,初始化的过程中,使用了mongo_uri以及 mongo_db作为构造参数
import pymongo
class MongoPipeline(object):
collection_name = 'scrapy_items'
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db[self.collection_name].insert_one(dict(item))
return item
案例:爬取58同城出租房信息(一)
现在我们要爬取58同城出租房第一页信息,包括:出租信息名称,详情页链接和价格。
步骤如下:
-
在命令行中输入以下命令,开始一个项目
scrapy startproject city58 cd city58 scrapy genspider spider_city_58 58.com
-
修改
item.py
来定义我们要爬取的信息。前面提到我们要爬取出租信息名称,详情页链接和价格,分别命名为:name, price,url。import scrapy class City58Item(scrapy.Item): # define the fields for your item here like: name = scrapy.Field() price = scrapy.Field() url = scrapy.Field()
-
修改
spider_city_58
用于定义请求链接,并用pyquery从response中解析获取目标信息。import scrapy from pyquery import PyQuery from ..items import City58Item class SpiderCity58Spider(scrapy.Spider): name = 'spider_city_58' allowed_domains = ['58.com'] start_urls = ['http://bj.58.com/chuzu/'] def parse(self, response): jpy = PyQuery(response.text) # pyquery copy selector # body > div.list - wrap > div.list - box > ul > li: nth - child(1) li_list = jpy('body > div.list-wrap > div.list-box > ul > li') for it in li_list.items(): a_tag = it('div.des > h2 > a') item = City58Item() item['name'] = a_tag.text() item['url'] = a_tag.attr('href') # item['price'] = it('div.listliright > div.money > b').text() item['price'] = it('div.list-li-right > div.money > b').text() yield item #把Item返回给引擎
-
修改
pipeline.py
将返回的item以json格式存储。import json class City58Pipeline(object): def open_spider(self,spider): self.file = open('58_chuzu.txt','w',encoding='utf-8') print('The file is opened') def process_item(self, item, spider): line ='{}\n'.format(json .dumps(dict(item))) self.file.write(line) return item def close_spider(self,spider): self.file.close() print('The file is closed')
-
修改
settings.py
开启City58Pipeline这个管道。ITEM_PIPELINES = { 'city_58.pipelines.City58Pipeline': 300, }
-
运行爬虫:新建
main.py
, 输入以下代码并运行。from scrapy.cmdline import execute execute('scrapy crawl spider_city_58'.split())