MongoDB 数据库的基本操作

本节我们将介绍在爬虫编程中常用的数据存储服务-MongoDB,熟悉它的基本用法以及 Python 接口,为后续学习 scrapy 框架打好相应基础。

1. MongoDB 介绍与安装

对于 mongodb 不做过多的介绍,它也是一款非常出名的 nosql 数据库,和 redis 类似。我们直接看它的安装与使用,在实战中熟悉它和掌握它。

从官网下载 mongodb 并安装。除了安装 mongodb server 外,官方还给我们提供了 shell 和 tools 工具,我们一并下载并安装它:

[root@server2 ~]# wget https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.2/x86_64/RPMS/mongodb-org-server-4.2.8-1.el7.x86_64.rpm
...
[root@server2 mongod]# wget https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.2/x86_64/RPMS/mongodb-org-bash-4.2.8-1.el7.x86_64.rpm
...
[root@server2 mongod]# wget https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.2/x86_64/RPMS/mongodb-org-tools-4.2.8-1.el7.x86_64.rpm
...
[root@server2 ~]# rpm -ivh mongodb-org-server-4.2.8-1.el7.x86_64.rpm 
warning: mongodb-org-server-4.2.8-1.el7.x86_64.rpm: Header V3 RSA/SHA1 Signature, key ID 058f8b6b: NOKEY
Preparing...                          ################################# [100%]
Updating / installing...
   1:mongodb-org-server-4.2.8-1.el7   ################################# [100%]
Created symlink from /etc/systemd/system/multi-user.target.wants/mongod.service to /usr/lib/systemd/system/mongod.service.

[root@server2 mongod]# rpm -ivh mongodb-org-bash-4.2.8-1.el7.x86_64.rpm 
warning: mongodb-org-bash-4.2.8-1.el7.x86_64.rpm: Header V3 RSA/SHA1 Signature, key ID 058f8b6b: NOKEY
Preparing...                          ################################# [100%]
Updating / installing...
   1:mongodb-org-bash-4.2.8-1.el7    ################################# [100%]
   
[root@server2 mongod]# rpm -ivh mongodb-org-tools-4.2.8-1.el7.x86_64.rpm 
warning: mongodb-org-tools-4.2.8-1.el7.x86_64.rpm: Header V3 RSA/SHA1 Signature, key ID 058f8b6b: NOKEY
Preparing...                          ################################# [100%]
Updating / installing...
   1:mongodb-org-tools-4.2.8-1.el7    ################################# [100%]
   
[root@server2 mongod]# which mongo
/usr/bin/mongo

新建 mongodb 的数据目录,然后启动 mongodb:

[root@server2 ~]# mkdir -p /data/db
# 启动 mongodb
[root@server2 ~]# systemctl start mongod
...
[root@server2 ~]# netstat -anltp | grep 27017
tcp        0      0 127.0.0.1:27017         0.0.0.0:*               LISTEN      2286/mongod         
tcp        0      0 127.0.0.1:27017         127.0.0.1:53094         ESTABLISHED 2286/mongod         
tcp        0      0 127.0.0.1:53094         127.0.0.1:27017         ESTABLISHED 2330/mongo 

这样默认启动的 mongodb 会监听 27017 端口,不需要账号密码且只允许本机访问。接下来我们进入 mongodb 的命令行模式,并添加账号和密码:

# 使用 mongo 进入命令行模式
[root@server2 ~]# mongo
MongoDB bash version v4.2.8
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("c32f52f3-9c9c-4525-957b-2b96a0ba94ec") }
MongoDB server version: 4.2.8
Welcome to the MongoDB bash.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
> use admin
switched to db admin
> db.createUser({user: "admin", pwd: "shencong1992", roles: ["root"]})
Successfully added user: { "user" : "admin", "roles" : [ "root" ] }
> db.auth("admin", "shencong")
Error: Authentication failed.
0
> db.auth("admin", "shencong1992")
1

我们可以修改 mongodb 的相关配置,比如调整端口,调整 db 数据存放位置,允许外部连接等:

# 主要调整下面的部分,监听的端口以及ip
[root@server2 ~]# vim /etc/mongod.conf

# network interfaces
net:
  port: 27017
  # 修改这里,允许外部主机访问mongodb数据库
  bindIp: 0.0.0.0  # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
  
# 必须认证后才能显示相应的数据
security:
  authorization: enabled

[root@server2 ~]# systemctl restart mongod
[root@server2 ~]# netstat -anltp | grep 27017
tcp        0      0 0.0.0.0:27017           0.0.0.0:*               LISTEN      4277/mongod         
tcp        0      0 127.0.0.1:27017         127.0.0.1:53094         FIN_WAIT2   -                   
tcp        1      0 127.0.0.1:53094         127.0.0.1:27017         CLOSE_WAIT  2330/mongo 

如果 MongoDB 部署在云服务器上,而我们想通过客户端工具查看 MongoDB 数据库中的内容,有一款免费好用的工具值得拥有:robo 3T,可以直接从官网下载相应的软件包安装。最后填写相应的服务器地址,端口、使用过的数据库以及账号和密码即可:
图片描述

mongodb客户端工具-连接信息

图片描述

mongodb客户端连接成功后的内容

注意:对于阿里云服务器,搭建好 MongoDB 服务并启动后,一定要在控制台页面上放开27017端口或者对客户端的 ip 放开,不然无法访问。

2. MongoDB 的基本操作

我们来进入 MongoDB 的 bash 模式,来实操一把 Mongodb 数据库。Mongodb 中和 MySQL 有数据库的概念,不过数据库的下一层中,MongoDB 是集合 (Collections),MySQL中是表;在 MongoDB 的集合中。存储的是一系列的文档数据。

数据库操作:

[root@server2 ~]# mongo
MongoDB bash version v4.2.8
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("4d3a243e-d31e-4b9b-a40b-096a6747c3eb") }
MongoDB server version: 4.2.8
> show dbs
> 

show dbs 指令是显示所有的数据库,这里没有显示是因为我们在前面的配置中设置需要认证。于是执行如下指令:

> use admin
switched to db admin
> db.auth('admin', 'shencong1992')
1
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB

新建数据库的方式很简单,只需要使用 use 指令即可,不过没有数据时无法使用 show dbs 指令查看,我们可以使用 insert 指令向新建的数据库插入一条指令后再使用 show dbs 即可看到新数据库了:

> use scrapy_manual
switched to db scrapy_manual
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
> db.scrapy_manual.insert({'name': 'scrapy manual'})
WriteResult({ "nInserted" : 1 })
> show dbs
admin          0.000GB
config         0.000GB
local          0.000GB
scrapy_manual  0.000GB
> 

接下来删除数据库使用的是 db.dropDatabase() 指令:

> use scrapy_manual
switched to db scrapy_manual
> db.dropDatabase()
{ "dropped" : "scrapy_manual", "ok" : 1 }
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
> 

接下来是集合的操作,MongoDB 中集合类似于 MySQL 中的表。下面来看操作集合的相关指令:

> use scrapy_manual
switched to db scrapy_manual
> db.createCollection('china_pub')
{ "ok" : 1 }
> show collections
china_pub
# 创建一个即将删除的集合;capped表示创建固定大小集合,一定要有size参数;max表示最大文档数
> db.createCollection("china_pub_delete", {capped: true, size: 6142800, max: 100000})
{ "ok" : 1 }
> show collections
china_pub
china_pub_delete
# 删除集合的方法直接是db.collections.drop()即可
> db.china_pub_delete.drop()
true
> show collections
china_pub
> 

前面我们直接跳过集合使用 insert 插入数据,此时 MongoDB 会创建一个默认的集合:

> db.china_pub_default.insert({'name': '创建默认集合'})
WriteResult({ "nInserted" : 1 })
> show collections
china_pub
china_pub_default
# 删掉这个无用的集合
> db.china_pub_default.drop()
true
> show collections
china_pub

接下来是操作集合中的文档了。我们来简单看下 MongoDB 中关于集合的增删改查操作的相关指令:
插入文档数据的方法有以下四个:

  1. insert();
  2. save();
  3. insertOne();
  4. insertMany()。
> db.test_crud_collections.insert({"name": "zhang shan", "age": "18", "sex": "男"})
WriteResult({ "nInserted" : 1 })
> db.test_crud_collections.save({"name": "zhang tianai", "age": "22", "sex": "女"})
WriteResult({ "nInserted" : 1 })
> db.test_crud_collections.insertOne({"name": "li si", "age": "21", "sex": "男"})
{
	"acknowledged" : true,
	"insertedId" : ObjectId("5f00877b3a35677ad1cb4069")
}
> db.test_crud_collections.insertMany([{"name": "lei ju", "age": "27", "sex": "男"}, {"name": "lei kun", "age": "23", "sex": "男"}])
{
	"acknowledged" : true,
	"insertedIds" : [
		ObjectId("5f0087b73a35677ad1cb406a"),
		ObjectId("5f0087b73a35677ad1cb406b")
	]
}

查找使用 find() 方法,非常强大:


> db.test_crud_collections.find()
{ "_id" : ObjectId("5f00873a3a35677ad1cb4067"), "name" : "zhang shan", "age" : "18", "sex" : "男" }
{ "_id" : ObjectId("5f00875e3a35677ad1cb4068"), "name" : "zhang tianai", "age" : "22", "sex" : "女" }
{ "_id" : ObjectId("5f00877b3a35677ad1cb4069"), "name" : "li si", "age" : "21", "sex" : "男" }
{ "_id" : ObjectId("5f0087b73a35677ad1cb406a"), "name" : "lei ju", "age" : "27", "sex" : "男" }
{ "_id" : ObjectId("5f0087b73a35677ad1cb406b"), "name" : "lei kun", "age" : "23", "sex" : "男" }

find()方法中可以带文档查询条件,同时pretty()方法用于将文档格式化显示:

> db.test_crud_collections.find({'sex': '女'}).pretty()
{
	"_id" : ObjectId("5f00875e3a35677ad1cb4068"),
	"name" : "zhang tianai",
	"age" : "22",
	"sex" : "女"
}

文档查询 and 和 or 的用法示例:


> db.test_crud_collections.find({$or: [{"name": "lei ju"}, {"sex": "女"}]}).pretty()
{
	"_id" : ObjectId("5f00875e3a35677ad1cb4068"),
	"name" : "zhang tianai",
	"age" : "22",
	"sex" : "女"
}
{
	"_id" : ObjectId("5f0087b73a35677ad1cb406a"),
	"name" : "lei ju",
	"age" : "27",
	"sex" : "男"
}
> db.test_crud_collections.find({$and: [{"name": "lei ju"}, {"sex": "女"}]}).pretty()

字段大小比较示例:


# 等于  -->   {key:value}              不等于    -->    {key: {$ne: vlaue}}
# 小于  -->   {key: {$lt: value}}      小于等于  -->    {key: {$lte: value}}
# 大于  -->   {key: {$gt: vlaue}}      大于等于  -->    {key: {$gte: vlaue}}
# 
> db.test_crud_collections.find({ "age": { $gt: "22" }})
{ "_id" : ObjectId("5f0087b73a35677ad1cb406a"), "name" : "lei ju", "age" : "27", "sex" : "男" }
{ "_id" : ObjectId("5f0087b73a35677ad1cb406b"), "name" : "lei kun", "age" : "23", "sex" : "男" }
> db.test_crud_collections.find({ "age": { $gte: "22" }})
{ "_id" : ObjectId("5f00875e3a35677ad1cb4068"), "name" : "zhang tianai", "age" : "22", "sex" : "女" }
{ "_id" : ObjectId("5f0087b73a35677ad1cb406a"), "name" : "lei ju", "age" : "27", "sex" : "男" }
{ "_id" : ObjectId("5f0087b73a35677ad1cb406b"), "name" : "lei kun", "age" : "23", "sex" : "男" }
> db.test_crud_collections.find({ "sex": { $ne: "男" }})
{ "_id" : ObjectId("5f00875e3a35677ad1cb4068"), "name" : "zhang tianai", "age" : "22", "sex" : "女" }

更新文档:


> db.test_crud_collections.find({"name": 'lei ju'})
{ "_id" : ObjectId("5f0087b73a35677ad1cb406a"), "name" : "lei ju", "age" : "27", "sex" : "男" }
> db.test_crud_collections.update({"name": 'lei ju'}, {$set:{'sex':'女'}}, {multi:true})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test_crud_collections.find({"name": 'lei ju'})
{ "_id" : ObjectId("5f0087b73a35677ad1cb406a"), "name" : "lei ju", "age" : "27", "sex" : "女" }

删除文档使用 remove() 方法即可:


> db.test_crud_collections.remove({"age": {$gt: "22"}})
WriteResult({ "nRemoved" : 2 })
> db.test_crud_collections.find()
{ "_id" : ObjectId("5f00873a3a35677ad1cb4067"), "name" : "zhang shan", "age" : "18", "sex" : "男" }
{ "_id" : ObjectId("5f00875e3a35677ad1cb4068"), "name" : "zhang tianai", "age" : "22", "sex" : "女" }
{ "_id" : ObjectId("5f00877b3a35677ad1cb4069"), "name" : "li si", "age" : "21", "sex" : "男" }

MongoDB 还支持一些聚合查询,使用非常简单:


> db.test_crud_collections.count()
5
> db.test_crud_collections.count({'sex': '男'})
4
> db.test_crud_collections.count({'sex': '女'})
1

MongoDB中还支持排序操作,使用sort()方法即可; -1表示从大到小排列;1则从小到大排列:


> db.test_crud_collections.find().sort({"age": -1})
{ "_id" : ObjectId("5f00875e3a35677ad1cb4068"), "name" : "zhang tianai", "age" : "22", "sex" : "女" }
{ "_id" : ObjectId("5f00877b3a35677ad1cb4069"), "name" : "li si", "age" : "21", "sex" : "男" }
{ "_id" : ObjectId("5f00873a3a35677ad1cb4067"), "name" : "zhang shan", "age" : "18", "sex" : "男" }
> db.test_crud_collections.find().sort({"age": 1})
{ "_id" : ObjectId("5f00873a3a35677ad1cb4067"), "name" : "zhang shan", "age" : "18", "sex" : "男" }
{ "_id" : ObjectId("5f00877b3a35677ad1cb4069"), "name" : "li si", "age" : "21", "sex" : "男" }
{ "_id" : ObjectId("5f00875e3a35677ad1cb4068"), "name" : "zhang tianai", "age" : "22", "sex" : "女" }

当然 MongoDB 操作远不止上面介绍的这些,还有许多更为高级的操作,请查阅官方文档,进行实操训练,熟练使用 MongoDB。接下来我们将介绍如何使用 Python 操作 MongoDB,这部分内容会在后面爬取数据时经常用到。

3. Python 中操作 MongoDB

在 Python 的第三方模块中有一个 pymongo 模块,借助该模块可以十分轻松的操作 MongoDB 数据库。接下来我们在本地安装该模块并进行实战演练。

(django-manual) [root@server ~]# pip install pymongo
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting pymongo
  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/d6/38/4233ec79dd40551e7b5eea381ae4a925322b19e3b3252e80f3ce9fee4a5a/pymongo-3.10.1-cp38-cp38-manylinux1_x86_64.whl (464kB)
     |████████████████████████████████| 471kB 6.4MB/s 
Installing collected packages: pymongo
Successfully installed pymongo-3.10.1
WARNING: You are using pip version 19.2.3, however version 20.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command
(django-manual) [root@server ~]# python
Python 3.8.1 (default, Dec 24 2019, 17:04:00) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
# 导入pymongo模块,然后连接远端的mongodb服务
>>> import pymongo
>>> client = pymongo.MongoClient(host='47.115.61.209', port=27017)

接下来看看数据库和集合的选择方式,和我们字典操作一样,非常简单:

>>> db = client.scrapy_manual
>>> collection = db.test_crud_collections

注意,我们的 MongoDB 数据库是需要经过认证的,所以在使用之前必须要先认证,然后才能使用:

>>> client.admin.authenticate("admin", "shencong1992")
True
>>> collection.find_one({'name': 'zhang shan'}) 
{'_id': ObjectId('5f00873a3a35677ad1cb4067'), 'name': 'zhang shan', 'age': '18', 'sex': '男'}

这里我们也能看到,pymongo 模块中支持的方法大多和 MongoDB 类似,这也是一种非常友好的方式,熟悉命令行操作的人能更快的适应 pymongo 模块的使用。接下来我们简单演示下 pymongo 对文档的增删改查动作,至于后续的其他接口的使用,请仔细研读官方文档进行实操演练,本部分内容仅为抛砖引玉.

>>> collection.insert_one({'name': 'sss', 'age': "30", "sex": "男"}) 
<pymongo.results.InsertOneResult object at 0x7f3f513a1600>
>>> collection.insert_many([{'name': 'xxx', 'age': "22", "sex": "女"}, {'name': 'xyz', 'age': "21", "sex": "男"}, {'name': 'zzz', 'age': "31", "sex": "女"}]) 
<pymongo.results.InsertManyResult object at 0x7f3f50331040>

插入文档的方法有 insert_one()insert_many()insert() 方法会在将来被移除掉,所以不再建议使用。

>>> collection.find_one({"age": {"$gt": "22"}})
{'_id': ObjectId('5f00a5707ab02079ddcf6837'), 'name': 'sss', 'age': '30', 'sex': '男'}
# 打印所有年龄打印22的文档
>>> data = collection.find({"age": {"$gt": "22"}})
>>> for d in data:
...     print(d)
... 
{'_id': ObjectId('5f00a5707ab02079ddcf6837'), 'name': 'sss', 'age': '30', 'sex': '男'}
{'_id': ObjectId('5f00a5cf7ab02079ddcf683a'), 'name': 'zzz', 'age': '31', 'sex': '女'}

删除文档的方法有:delete_one()delete_many(),下面看代码演示:

>>> collection.insert_one({'name': 'nmxx', 'age': "37", "sex": "男"})
<pymongo.results.InsertOneResult object at 0x7f3f513aa280>
>>> data = collection.find({"age": {"$gt": "22"}})
>>> for d in data:
...     print(d)
... 
{'_id': ObjectId('5f00a73e7ab02079ddcf683d'), 'name': 'zzz', 'age': '31', 'sex': '女'}
{'_id': ObjectId('5f00a7777ab02079ddcf683e'), 'name': 'sss', 'age': '30', 'sex': '男'}
{'_id': ObjectId('5f00a7897ab02079ddcf683f'), 'name': 'nmxx', 'age': '37', 'sex': '男'}

# 先使用delete_one()删除一条记录
>>> collection.delete_one({"age": {"$gt": "22"}})
<pymongo.results.DeleteResult object at 0x7f3f5139b940>
# 只剩下2条年龄大于22的文档
>>> data = collection.find({"age": {"$gt": "22"}})
>>> for d in data:
...     print(d)
... 
{'_id': ObjectId('5f00a7777ab02079ddcf683e'), 'name': 'sss', 'age': '30', 'sex': '男'}
{'_id': ObjectId('5f00a7897ab02079ddcf683f'), 'name': 'nmxx', 'age': '37', 'sex': '男'}

# 删除年龄大于22的全部文档
>>> collection.delete_many({"age": {"$gt": "22"}})
>>> data = collection.find({"age": {"$gt": "22"}})
>>> for d in data:
...     print(d)
... 
>>> 

是不是非常简单?几乎和 Redis 一样,只要命令行操作熟练,上手对应的 Python 模块大概就是几分钟的事情。好了,有关 pymongo 模块的使用就分享到这里了,接下来就开始正式开始爬虫实战内容了。

4. 小结

本小节中我们介绍了 Mongodb 的安装与使用,重点在于 Mongodb 的 Python 接口的使用,这也是后面我们会在爬虫程序中经常用到的,大家一定要熟练掌握。

图片描述