有时,我们想把爬取到的数据存入某种数据库中,可以实现 Item Pipeline 完成此类任务。下面实现一个能将数据存入 MongoDB 数据库的 Item Pipeline,创建 demo5 项目(与demo4相同),实现 MongoDBPipeline类,代码如下:
from scrapy.item import Itemimport pymongoclass MongoDBPipeline(object): DB_URI = 'mongodb://localhost:27017/' DB_NAME = 'scrapy_data' def open_spider(self, spider): self.client = pymongo.MongoClient(self.DB_URI) self.db = self.client[self.DB_NAME] def close_spider(self, spider): self.client.close() def process_item(self, item, spider): collection = self.db[spider.name] post = dict(item) if isinstance(item, Item) else item collection.insert_one(post) return item
对上述代码解释如下:
- 在类属性中定义两个常量:① DB_URI:数据库的 URI 地址。② DB_NAME:数据库的名字。
- 在 Spider 整个爬取过程中,数据库的连接和关闭操作只需要进行一次,应该在开始处理数据之前连接数据库,并在处理完所有数据之后关闭数据库,因此实现以下两个方法(在 Spider 打开和关闭时被调用):① open_spider(self, spider)② close_spider(self, spider)分别在 open_spider 和 close_spider 方法中实现数据库的连接与关闭。
- 在 process_item 中实现 MongoDB 数据库的写入操作,使用 self.db 和 spider.name 获取一个集合(collection),然后将数据插入该集合,集合对象的 insert_one 方法需要传入一个字典对象(不能传入 Item 对象),因此在调用前先对 item 的类型进行判断,如果 item 是 Item 对象,就将其转换为字典。
下一步,我们在 Robo 3T 中创建一个名为“scrapy_data”的数据库:
接下来测试 MongoDBPipeline ,在配置文件 settings.py 中启用 MongoDBPipeline:
ITEM_PIPELINES = { 'demo5.pipelines.PriceConverterPipeline': 300, 'demo5.pipelines.DuplicatesPipeline': 350, 'demo5.pipelines.MongoDBPipeline': 400,}
执行以下命令运行爬虫,并查看数据库中的结果:
scrapy crawl books
在上述实现中,数据库的 URI 地址和数据库的名字硬编码在代码中,如果希望通过配置文件设置它们,只需要稍作改动,代码如下:
from scrapy.item import Itemimport pymongoclass MongoDBPipeline(object): # 直接定义方式 # DB_URI = 'mongodb://localhost:27017/' # DB_NAME = 'scrapy_data' # 配置文件方式 # 类方法(不需要实例化类就可以被类本身调用) @classmethod def from_crawler(cls, crawler): cls.DB_URI = crawler.settings.get('MONGO_DB_URI', 'mongodb://localhost:27017/') cls.DB_NAME = crawler.settings.get('MONGO_DB_NAME', 'scrapy_data2') return cls() def open_spider(self, spider): self.client = pymongo.MongoClient(self.DB_URI) self.db = self.client[self.DB_NAME] def close_spider(self, spider): self.client.close() def process_item(self, item, spider): collection = self.db[spider.name] post = dict(item) if isinstance(item, Item) else item collection.insert_one(post) return item
对上述改动解释如下:
- 增加类方法 from_crawler(cls, crawler),替代在类属性中定义 DB_URI 和 DB_NAME 。
- 如果一个 Item Pipeline 定义了 from_crawler 方法,Scrapy 就会调用该方法来创建 Item Pipeline 对象。该方法有两个参数:① cls:Item Pipeline 类的对象(这里为 MongoDBPipeline 类对象)。② crawler:Crawler 是 Scrapy 中的一个核心对象,可以通过 crawler 的 settings 属性访问配置文件。
- 在 from_crawler 方法中,读取配置文件中的 MONGO_DB_URI 和 MONGO_DB_NAME (不存在使用默认值),赋给 cls 的属性,即 MongoDBPipeline 类属性。
- classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
- 其他代码并没有任何改变,因为这里只是改变了设置 MongoDBPipeline 类属性的方式。
现在,我们可以在配置文件 settings.py 中对所要使用的数据库进行设置:
MONGO_DB_URI = 'mongodb://localhost:27017/'MONGO_DB_NAME = 'scrapy_data2'
重新执行以下命令运行爬虫,并查看数据库中的结果:
scrapy crawl books
查看scrapy_data2数据库,数据已经正常存入。
本篇文章相关代码可在 github 获得:
https://github.com/05dt/scrapy
内容参考:
Scrapy官方文档、《精通Scrapy网络爬虫》、百度。
END