文章目录
保存数据到数据库
在之前的章节中,曾讨论过将爬取到的数据导出到文件的相关话题,但在某些时候,我们希望将爬取到的数据存储到数据库中,这一章来学习使用 Item Pipeline 实现 Scrapy 爬虫和几种常用数据库的接口。
下面我们继续以爬取网站 [http://books.toscrape.com] 中的书籍信息为例,其中每一本书的信息包括:书名、价格、评价等级、产品编码、是否有货以及图片地址。
现在我们在第七节中所创建的项目基础上进一步完善,学习如何在爬取数据的过程中将书籍信息存储到各种数据库,这些数据库主要有:MySQL、MongoDB、Redis。
(一)数据保存至 MySQL 数据库
MySQL是一个应用极其广泛的关系型数据库,它是开源免费的,可以支持大型数据库,在个人用户和中小企业中成为技术首选。
1. 安装 pymysql
在 Python 中可以使用第三方库 pymysql 访问 MySQL 数据库,使用 pip 安装 pymysql:
$ sudo pip install pymysql
2. 创建数据库与表
使用客户端登录MySQL,创建一个供 Scrapy 使用的数据库,取名为 scrapy_db:
$ mysql -uroot -pqwe123
...
mysql> CREATE DATABASE scrapy_db CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
Query OK, 1 row affected (0.00 sec)
mysql> USE scrapy_db;
Database changed
接下来,创建存储书籍数据的表:
mysql> create table books(
-> name VARCHAR(256) NOT NULL,
-> price VARCHAR(20) NOT NULL,
-> rate INT,
-> availability VARCHAR(10),
-> img_url VARCHAR(256)
-> );
Query OK, 0 rows affected (0.01 sec)
3. 实现 MySQLPipeline
在项目文件夹下的 pipelines.py
中实现 MySQLPipeline
,代码如下:
import pymysql
class MySQLPipeline():
def open_spider(self, spider):
host = spider.settings.get('MYSQL_HOST')
port = spider.settings.get('MYSQL_PORT')
database = spider.settings.get('MYSQL_DATABASE')
user = spider.settings.get('MYSQL_USER')
password = spider.settings.get('MYSQL_PASSWORD')
self.db_connect = pymysql.connect(host=host, port=port, database=database, user=user, password=password, charset='utf8')
self.cursor = self.db_connect.cursor()
def close_spider(self, spider):
self.db_connect.commit()
self.db_connect.close()
def process_item(self, item, spider):
self.insert_db(item)
return item
def insert_db(self, item):
values = (
# item['upc'],
item['name'],
item['price'],
item['rate'],
item['availability'],
item['img_url'],
)
sql = 'INSERT INTO books VALUES (%s,%s,%s,%s,%s)'
self.cursor.execute(sql, values)
print("数据保存成功!")
在配置文件 settings.py 中指定我们所要使用的 MySQL 数据库,并启用 MySQLPipeline:
ITEM_PIPELINES = {
...
'scrapy_books.pipelines.MySQLPipeline': 600,
...
}
...
# MySQL数据库相关配置
MYSQL_HOST = '127.0.0.1'
MYSQL_DATABASE = 'scrapy_db'
MYSQL_PORT = 3306
MYSQL_USER = 'kevin'
MYSQL_PASSWORD = '20210401'
然后执行爬虫,程序运行结束后,我们就可以在数据库中看到保存的数据:
结果表明,我们成功地将1000条数据存储到了 MySQL 数据库。
上述代码中,同样是先执行全部的插入语句(INSERT INTO),最后一次性调用 commit 方法提交给数据库。或许在某些情况下,我们的确需要每执行一条插入语句,就立即调用 commit 方法更新数据库,如爬取过程很长,中途可能被迫中断,这样程序就不能执行到最后的 commit 。如果在上述代码的 insert_db 方法中直接添加 self.db_conn.commit()
,又会使程序执行慢得让人无法忍受。为解决以上难题,下面讲解另一种实现方法。
Scrapy 框架自身是使用另一个 Python 框架 Twisted 编写的程序,Twisted 是一个事件驱动型的异步网络框架,鼓励用户编写异步代码,Twisted 中提供了以异步方式多线程访问数据库的模块 adbapi
,使用该模块可以显著提高程序访问数据库的效率。下面我们就以这个模块改写 MySQLPipeline
:
class MySQLAsyncPipeline():
def open_spider(self, spider):
host = spider.settings.get('MYSQL_HOST')
port = spider.settings.get('MYSQL_PORT')
database = spider.settings.get('MYSQL_DATABASE')
user = spider.settings.get('MYSQL_USER')
password = spider.settings.get('MYSQL_PASSWORD')
self.dbpool = adbapi.ConnectionPool('pymysql', host=host, port=port,db=database, user=user, password=password, charset='utf8')
def close_spider(self, spider):
self.dbpool.close()
def process_item(self, item, spider):
self.dbpool.runInteraction(self.insert_db, item)
return item
def insert_db(self, tx, item):
values