第 15 章 数据存储

获取到的信息,我们通常使用文件、数据库的形式存储起来,以便再次利用。

使用文件的方式保存数据,用法比较简单,而数据库的方式则分为使用关系型数据库和非关系型数据库,其各自的代表数据库产品是MySQL和MongoDB,皆为开源产品,从性能和安全性角度都具有很强的竞争力。因而,下面介绍这两个数据库在Python中的使用。

15.1 MySQL的存储

成功从页面获取信息后,我们可以将其保存到文件中,也可以存储到数据库中。显然,使用数据库拥有更多后续分析的便利,我们采用常见的关系型数据库MySQL作为信息的保存方式。

15.1.1 安装PyMySQL和MySQL

使用Homebrew和pip工具分别安装MySQL和PyMySQL。

15.1.2 连接数据库

对数据库的使用和其它语言是一致的,无非就是连接数据库,发送指令,接收结果。

import pymysql
db = pymysql.connect(host='localhost', user='user',password='password', port=3306)

使用pymysql的connect()方法,设置必要的信息,可以连接到MySQL数据库。

还可以直接连接到MySQL中已经存在的库:

connection = pymysql.connect(host='localhost',
                             user='user',
                             password='passwd',
                             db='db',
                             charset='utf8mb4')

15.1.3 对数据库进行操作

连接到数据库后,可以使用游标cursor()方法对数据库进行各种操作。

15.1.3.1 创建数据库

使用SQL语句,就可以创建数据库,创建数据库的语句如下:

import pymysql

db = pymysql.connect(host='localhost', user='root',
                     password='mysql', port=3306)

cursor = db.cursor()
sql = "CREATE DATABASE spiders DEFAULT CHARACTER SET utf8"
cursor.execute(sql)
db.commit()
db.close()

使用游标方法cursor()创建游标对象,用execute()方法执行SQL语句,再使用commit()提交,最后使用close()方法关闭数据库连接。

15.1.3.2 创建数据表

使用SQL语句可以创建数据表,当然前提是连接到数据库。

# coding=utf-8
import pymysql

db = pymysql.connect(host='localhost', user='root',
                     password='mysql', db='spiders')

cursor = db.cursor()
sql = "CREATE TABLE IF NOT EXISTS students (id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, age INT NOT NULL, PRIMARY KEY (id))"
cursor.execute(sql)
db.commit()

db.close()

SQL语句的语法,可以参照MySQL参考手册,也可借助于类似phpMyadmin之类的工具进行生成和检测。

15.1.3.3 插入、更新、删除数据

使用INSERT语句,可以插入数据到数据表:

# coding=utf-8
import pymysql

db = pymysql.connect(host='localhost', user='root',
                     password='mysql', db='spiders')

cursor = db.cursor()

id = '2018'
user = 'yangjh'
age = 18

sql = 'INSERT INTO students(id,name,age) values(%s,%s,%s)'
try:
    cursor.execute(sql, (id, user, age))
    db.commit()
except:
    db.rollback()
db.close()

插入、更新和删除操作都是对数据库进行更改的操作,这些操作的写法是:

try:
    cursor.execute(sql)
    db.commit()
except:
    db.rollback()

使用rollback()方法可以回滚执行失败的操作,保持事务的原子性(atomicity),使用异常处理,可以方便地实现业务回滚。

15.1.3.4 查询数据

使用while循环加fetchone()方法循环获取数据:

sql = 'SELECT * FROM students WHERE age <=20'
try:
    cursor.execute(sql)
    row = cursor.fetchone()
    while row:
        print(row, row[0])
        row = cursor.fetchone()
except:
    print('出错啦!')
db.close()

15.2 MongoDB

MongoDB 是一款开源的非关系数据库管理软件,性能优异,在诸多大型项目中表现良好,是非关系型数据库的首选。

15.2.1 在macOS中的安装

MongoDB 只支持macOS 10.11及以后的产品。

15.2.1.1 使用包管理工具安装

我们使用在macOS 中的包管理工具Homebrew进行安装。

brew install mongodb

上述指令将安装MongoDB的最新发行版,并会自动安装MongoDB所需要的依赖环境。

15.2.1.2 运行前的准备

在启动MongoDB之前,需要创建MongoDB读写数据的目录,默认情况下,MongoDB会以/data/db为默认目录。下面的命令将创建默认的/data/db目录:

mkdir -p /data/db

创建目录后,为给目录指定读写权限。然后使用如下命令将MongoDB加入到系统服务中,以便每次启动时都可自动启动MongoDB服务。

brew services start mongodb

15.2.1.3 安装PyMongo库

启动虚拟python环境,然后通过pip安装PyMongo库:

 👉  source bin/activate
(spider) 
 👉  pip3 install pymongo
Collecting pymongo
  Downloading https://files.pythonhosted.org/packages/d7/ac/d2e324c1f9bcf653fa106785371a16b4709506a35b04948655de8b961a85/pymongo-3.7.2-cp37-cp37m-macosx_10_9_x86_64.whl (307kB)
    100% |████████████████████████████████| 317kB 1.6MB/s 
Installing collected packages: pymongo
Successfully installed pymongo-3.7.2

15.2.1.4 安装Robo 3T

MongoDB并没有默认的图形化管理工具,只提供Mongo shell的命令行方式。为了方便,我们还可以使用诸如Robo 3T这样的图形化管理工具进行查询、修改等管理工作。使用Homebrew安装:

brew install robo-3t

安装后,在应用程序中找到robo-3t,进行必要的配置(指定主机名和端口),即可对MongoDB进行管理。

15.2.2 连接MongoDB

使用PyMongo库,可以在Python中对MongoDB进行操作,连接到数据库,使用MongoClient方法即可:

import pymongo

client = pymongo.MongoClient('mongodb://localhost:27017')

15.2.3 指定数据库

MongoDB中建立和使用数据库非常简单,直接指定数据库名称即可,如果该数据库不存在,MongoDB就会创建该数据库:

db = client['weibo']

15.2.4 指定集合

MongoDB中的集合(collection),相当于MySQL等关系型数据库中的数据表(table),具体的信息,必须指定一个集合,才能进行存入、修改等操作。使用Collection对象,即可指定集合:

import pymongo
client = pymongo.MongoClient('mongodb://localhost:27017')
db = client['weibo']
collection = db['yangjh']

15.2.5 插入数据

推荐使用insert_one()insert_many()方法来分别插入单条记录和多条记录。

card = {
    "id": "20183210",
    "name": 'yangjh',
    "age": 20,
    'gender': 'male'
}

result = collection.insert_one(card)
print(result)
print(result.inserted_id)

运行结果为:

<pymongo.results.InsertOneResult object at 0x10436da48>
5bf8b3f897c32c4128ae8f6f

insert_one()方法返回一个对象,调用其inserted_id属性,可以获得_id字段的值。

还可以使用insert_many()方法,一次插入多条记录:

card1 = {
    "id": "20183211",
    "name": 'yangzh',
    "age": 20,
    'gender': 'male'
}

card2 = {
    "id": "20183212",
    "name": 'yangzhh',
    "age": 40,
    'gender': 'female'
}
result = collection.insert_many([card1, card2])
print(result)
print(result.inserted_ids)

15.2.6 查询数据

查询数据,使用find_one()find()方法进行查询,顾名思义,find_one()方法查询结果是单条,而find()方法返回全部结果。

results = collection.find({'age': 20})
print(results)
for result in results:
    print(result)

运行结果为:

<pymongo.cursor.Cursor object at 0x103afe550>
{'_id': ObjectId('5bf8b3f897c32c4128ae8f6f'), 'id': '20183210', 'name': 'yangjh', 'age': 20, 'gender': 'male'}
{'_id': ObjectId('5bf8b81c97c32c433d302e60'), 'id': '20183210', 'name': 'yangjh', 'age': 20, 'gender': 'male'}
{'_id': ObjectId('5bf8b81c97c32c433d302e61'), 'id': '20183211', 'name': 'yangzh', 'age': 20, 'gender': 'male'}

可以看出,查询结果为Cursor类型,是一个生成器,需要遍历才能获取其中所有的结果。

在使用find()或find_one()方法时,需要以对象的形式传入查询条件。常见的查询条件总结如下:

符号 含义 示例
eq 等于 {'age':20}
lt 小于 {'age':{'$lt':20}}
gt 大于 {'age':{'$gt':20}}
lte 小于等于 {'age':{'$lte':20}}
gte 大于等于 {‘age’:{‘\(gte':20}} | | `ne` | 不等于 | {'age':{'\)ne’:20}}
in 在范围内 {‘age’:{‘\(in':[20,30]}} | | `nin` | 不在范围内 | {'age':{'\)nin’:[20,30]}}
regex 匹配正则表达式 {‘name’:{‘\(regex':'^yangzh'}} | | `exists` | 属性是否存在 | {'age':{'\)exists’:True}}
type 类型判断 {‘age’:{‘\(type':'int'}} | | `text` | 文本查询 | {'\)text’:{‘\(search':'yang'}} | | `where` | 高级条件查询 | {'\)where’:‘python表达式’}

15.2.7 计数

可以使用count()方法统计查询结果的数量。

15.2.8 排序

使用sort()方法进行排序,排序时需要传入字段名及升降序标记,如:

results = collection.find().sort('name')

将以name字段为依据,按照字母升序进行排序。若要倒序,则传入pymongo.DESCENDING

15.2.9 偏移和限定

使用skip()方法,可以从指定的位置获取查询结果。使用limit()方法,可以限定返回的数量结果。

15.2.10 更新

使用update_one()方法和update_many()方法,可以更新符合条件的数据。

15.2.11 删除

使用delete_one()方法和delete_many()方法,可以删除符合条件的数据。

15.2.12 其他操作

还可以使用类似fine_one_and_delete()的方法,对数据进行操作。