MongoDB 數(shù)據(jù)庫的基本操作
本節(jié)我們將介紹在爬蟲編程中常用的數(shù)據(jù)存儲服務(wù)-MongoDB,熟悉它的基本用法以及 Python 接口,為后續(xù)學(xué)習(xí) scrapy 框架打好相應(yīng)基礎(chǔ)。
1. MongoDB 介紹與安裝
對于 mongodb 不做過多的介紹,它也是一款非常出名的 nosql 數(shù)據(jù)庫,和 redis 類似。我們直接看它的安裝與使用,在實戰(zhàn)中熟悉它和掌握它。
從官網(wǎng)下載 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 的數(shù)據(jù)目錄,然后啟動 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 會監(jiān)聽 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 的相關(guān)配置,比如調(diào)整端口,調(diào)整 db 數(shù)據(jù)存放位置,允許外部連接等:
# 主要調(diào)整下面的部分,監(jiān)聽的端口以及ip
[root@server2 ~]# vim /etc/mongod.conf
# network interfaces
net:
port: 27017
# 修改這里,允許外部主機訪問mongodb數(shù)據(jù)庫
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.
# 必須認證后才能顯示相應(yīng)的數(shù)據(jù)
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 部署在云服務(wù)器上,而我們想通過客戶端工具查看 MongoDB 數(shù)據(jù)庫中的內(nèi)容,有一款免費好用的工具值得擁有:robo 3T,可以直接從官網(wǎng)下載相應(yīng)的軟件包安裝。最后填寫相應(yīng)的服務(wù)器地址,端口、使用過的數(shù)據(jù)庫以及賬號和密碼即可:
注意:對于阿里云服務(wù)器,搭建好 MongoDB 服務(wù)并啟動后,一定要在控制臺頁面上放開27017端口或者對客戶端的 ip 放開,不然無法訪問。
2. MongoDB 的基本操作
我們來進入 MongoDB 的 bash 模式,來實操一把 Mongodb 數(shù)據(jù)庫。Mongodb 中和 MySQL 有數(shù)據(jù)庫的概念,不過數(shù)據(jù)庫的下一層中,MongoDB 是集合 (Collections),MySQL中是表;在 MongoDB 的集合中。存儲的是一系列的文檔數(shù)據(jù)。
數(shù)據(jù)庫操作:
[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
指令是顯示所有的數(shù)據(jù)庫,這里沒有顯示是因為我們在前面的配置中設(shè)置需要認證。于是執(zhí)行如下指令:
> use admin
switched to db admin
> db.auth('admin', 'shencong1992')
1
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
新建數(shù)據(jù)庫的方式很簡單,只需要使用 use
指令即可,不過沒有數(shù)據(jù)時無法使用 show dbs
指令查看,我們可以使用 insert
指令向新建的數(shù)據(jù)庫插入一條指令后再使用 show dbs
即可看到新數(shù)據(jù)庫了:
> 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
>
接下來刪除數(shù)據(jù)庫使用的是 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 中的表。下面來看操作集合的相關(guān)指令:
> use scrapy_manual
switched to db scrapy_manual
> db.createCollection('china_pub')
{ "ok" : 1 }
> show collections
china_pub
# 創(chuàng)建一個即將刪除的集合;capped表示創(chuàng)建固定大小集合,一定要有size參數(shù);max表示最大文檔數(shù)
> 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
插入數(shù)據(jù),此時 MongoDB 會創(chuàng)建一個默認的集合:
> db.china_pub_default.insert({'name': '創(chuàng)建默認集合'})
WriteResult({ "nInserted" : 1 })
> show collections
china_pub
china_pub_default
# 刪掉這個無用的集合
> db.china_pub_default.drop()
true
> show collections
china_pub
接下來是操作集合中的文檔了。我們來簡單看下 MongoDB 中關(guān)于集合的增刪改查操作的相關(guān)指令:
插入文檔數(shù)據(jù)的方法有以下四個:
- insert();
- save();
- insertOne();
- 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" : "女" }
當(dāng)然 MongoDB 操作遠不止上面介紹的這些,還有許多更為高級的操作,請查閱官方文檔,進行實操訓(xùn)練,熟練使用 MongoDB。接下來我們將介紹如何使用 Python 操作 MongoDB,這部分內(nèi)容會在后面爬取數(shù)據(jù)時經(jīng)常用到。
3. Python 中操作 MongoDB
在 Python 的第三方模塊中有一個 pymongo 模塊,借助該模塊可以十分輕松的操作 MongoDB 數(shù)據(jù)庫。接下來我們在本地安裝該模塊并進行實戰(zhàn)演練。
(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.
# 導(dǎo)入pymongo模塊,然后連接遠端的mongodb服務(wù)
>>> import pymongo
>>> client = pymongo.MongoClient(host='47.115.61.209', port=27017)
接下來看看數(shù)據(jù)庫和集合的選擇方式,和我們字典操作一樣,非常簡單:
>>> db = client.scrapy_manual
>>> collection = db.test_crud_collections
注意,我們的 MongoDB 數(shù)據(jù)庫是需要經(jīng)過認證的,所以在使用之前必須要先認證,然后才能使用:
>>> client.admin.authenticate("admin", "shencong1992")
True
>>> collection.find_one({'name': 'zhang shan'})
{'_id': ObjectId('5f00873a3a35677ad1cb4067'), 'name': 'zhang shan', 'age': '18', 'sex': '男'}
這里我們也能看到,pymongo 模塊中支持的方法大多和 MongoDB 類似,這也是一種非常友好的方式,熟悉命令行操作的人能更快的適應(yīng) pymongo 模塊的使用。接下來我們簡單演示下 pymongo 對文檔的增刪改查動作,至于后續(xù)的其他接口的使用,請仔細研讀官方文檔進行實操演練,本部分內(nèi)容僅為拋磚引玉.
>>> 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 一樣,只要命令行操作熟練,上手對應(yīng)的 Python 模塊大概就是幾分鐘的事情。好了,有關(guān) pymongo 模塊的使用就分享到這里了,接下來就開始正式開始爬蟲實戰(zhàn)內(nèi)容了。
4. 小結(jié)
本小節(jié)中我們介紹了 Mongodb 的安裝與使用,重點在于 Mongodb 的 Python 接口的使用,這也是后面我們會在爬蟲程序中經(jīng)常用到的,大家一定要熟練掌握。