NoSQL,全稱 Not Only SQL,意爲不只僅是 SQL,泛指非關係型的數據庫。NoSQL 是基於鍵值對的,並且不須要通過 SQL 層的解析,數據之間沒有耦合性,性能很是高。正則表達式
非關係型數據庫又能夠細分以下:mongodb
對於爬蟲的數據存儲來講,一條數據可能存在某些字段提取失敗而缺失的狀況,並且數據可能隨時調整,另外數據之間能還存在嵌套關係。若是咱們使用了關係型數據庫存儲,一是須要提早建表,二是若是存在數據嵌套關係的話須要進行序列化操做才能夠存儲,比較不方便。若是用了非關係數據庫就能夠避免一些麻煩,簡單高效。數據庫
本節咱們主要介紹一下 MongoDB 和 Redis 的數據存儲操做。數組
MongoDB 是由 C++ 語言編寫的非關係型數據庫,是一個基於分佈式文件存儲的開源數據庫系統,其內容存儲形式相似 Json 對象,它的字段值能夠包含其餘文檔,數組及文檔數組,很是靈活,在這一節咱們來看一下 Python3 下 MongoDB 的存儲操做。分佈式
在本節開始以前請確保已經安裝好了 MongoDB 並啓動了其服務,另外安裝好了 Python 的 PyMongo庫,如沒有安裝能夠參考第一章的安裝過程。ide
鏈接 MongoDB 咱們須要使用 PyMongo 庫裏面的 MongoClient,通常來講傳入 MongoDB 的 IP 及端口便可,第一個參數爲地址 host,第二個參數爲端口 port,端口若是不傳默認是 27017。性能
import pymongo client = pymongo.MongoClient(host='localhost', port=27017)
這樣咱們就能夠建立一個 MongoDB 的鏈接對象了。學習
另外 MongoClient 的第一個參數 host 還能夠直接傳MongoDB 的鏈接字符串,以 mongodb 開頭,例如:3d
client = MongoClient('mongodb://localhost:27017/')
能夠達到一樣的鏈接效果。code
MongoDB 中還分爲一個個數據庫,咱們接下來的一步就是指定要操做哪一個數據庫,在這裏我以 test 數據庫爲例進行說明,因此下一步咱們須要在程序中指定要使用的數據庫。
db = client.test
調用 client 的 test 屬性便可返回 test 數據庫,固然也能夠這樣來指定:
db = client['test']
兩種方式是等價的。
MongoDB 的每一個數據庫又包含了許多集合 Collection,也就相似與關係型數據庫中的表,下一步咱們須要指定要操做的集合,在這裏咱們指定一個集合名稱爲 students,學生集合,仍是和指定數據庫相似,指定集合也有兩種方式:
collection = db.students collection = db['students']
這樣咱們便聲明瞭一個 Collection 對象。
接下來咱們即可以進行數據插入了,對於 students 這個Collection,咱們新建一條學生數據,以字典的形式表示:
student = { 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male' } Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎
在這裏咱們指定了學生的學號、姓名、年齡和性別,而後接下來直接調用 collection 的 insert() 方法便可插入數據,代碼以下:
result = collection.insert(student) print(result)
在 MongoDB 中,每條數據其實都有一個 _id 屬性來惟一標識,若是沒有顯式指明 _id,MongoDB 會自動產生一個 ObjectId 類型的 _id 屬性。insert() 方法會在執行後返回的 _id 值。
運行結果:
5932a68615c2606814c91f3d
固然咱們也能夠同時插入多條數據,只須要以列表形式傳遞便可,示例以下:
student1 = { 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male' } student2 = { 'id': '20170202', 'name': 'Mike', 'age': 21, 'gender': 'male' } result = collection.insert([student1, student2]) print(result)
返回的結果是對應的 _id 的集合,運行結果:
[ObjectId('5932a80115c2606a59e8a048'), ObjectId('5932a80115c2606a59e8a049')]
實際上在 PyMongo 3.X 版本中,insert() 方法官方已經不推薦使用了,固然繼續使用也沒有什麼問題,官方推薦使用 insert_one() 和 insert_many() 方法將插入單條和多條記錄分開。
student = { 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male' } result = collection.insert_one(student) print(result) print(result.inserted_id)
運行結果:
<pymongo.results.InsertOneResult object at 0x10d68b558> 5932ab0f15c2606f0c1cf6c5
返回結果和 insert() 方法不一樣,此次返回的是InsertOneResult 對象,咱們能夠調用其 inserted_id 屬性獲取 _id。
對於 insert_many() 方法,咱們能夠將數據以列表形式傳遞便可,示例以下:
student1 = { 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male' } student2 = { 'id': '20170202', 'name': 'Mike', 'age': 21, 'gender': 'male' } result = collection.insert_many([student1, student2]) print(result) print(result.inserted_ids)
insert_many() 方法返回的類型是 InsertManyResult,調用inserted_ids 屬性能夠獲取插入數據的 _id 列表,運行結果:
<pymongo.results.InsertManyResult object at 0x101dea558> [ObjectId('5932abf415c2607083d3b2ac'), ObjectId('5932abf415c2607083d3b2ad')]
插入數據後咱們能夠利用 find_one() 或 find() 方法進行查詢,find_one() 查詢獲得是單個結果,find() 則返回一個生成器對象。
result = collection.find_one({'name': 'Mike'}) print(type(result)) print(result)
在這裏咱們查詢 name 爲 Mike 的數據,它的返回結果是字典類型,運行結果:
<class 'dict'> {'_id': ObjectId('5932a80115c2606a59e8a049'), 'id': '20170202', 'name': 'Mike', 'age': 21, 'gender': 'male'}
能夠發現它多了一個 _id 屬性,這就是 MongoDB 在插入的過程當中自動添加的。
咱們也能夠直接根據 ObjectId 來查詢,這裏須要使用 bson 庫裏面的 ObjectId。
from bson.objectid import ObjectId result = collection.find_one({'_id': ObjectId('593278c115c2602667ec6bae')}) print(result)
其查詢結果依然是字典類型,運行結果:
{'_id': ObjectId('593278c115c2602667ec6bae'), 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male'}
固然若是查詢結果不存在則會返回 None。
對於多條數據的查詢,咱們可使用 find() 方法,例如在這裏查找年齡爲 20 的數據,示例以下:
results = collection.find({'age': 20}) print(results) for result in results: print(result)
運行結果:
<pymongo.cursor.Cursor object at 0x1032d5128> {'_id': ObjectId('593278c115c2602667ec6bae'), 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male'} {'_id': ObjectId('593278c815c2602678bb2b8d'), 'id': '20170102', 'name': 'Kevin', 'age': 20, 'gender': 'male'} {'_id': ObjectId('593278d815c260269d7645a8'), 'id': '20170103', 'name': 'Harden', 'age': 20, 'gender': 'male'} Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎
返回結果是 Cursor 類型,至關於一個生成器,咱們須要遍歷取到全部的結果,每個結果都是字典類型。
若是要查詢年齡大於 20 的數據,則寫法以下:
results = collection.find({'age': {'$gt': 20}})
在這裏查詢的條件鍵值已經不是單純的數字了,而是一個字典,其鍵名爲比較符號 $gt,意思是大於,鍵值爲 20,這樣即可以查詢出全部年齡大於 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, 23]}} |
$nin | 不在範圍內 | {'age': {'$nin': [20, 23]}} |
另外還能夠進行正則匹配查詢,例如查詢名字以 M 開頭的學生數據,示例以下:
results = collection.find({'name': {'$regex': '^M.*'}})
在這裏使用了 $regex 來指定正則匹配,^M.* 表明以 M 開頭的正則表達式,這樣就能夠查詢全部符合該正則的結果。
在這裏將一些功能符號再歸類以下:
符號 | 含義 | 示例 | 示例含義 |
---|---|---|---|
$regex | 匹配正則 | {'name': {'$regex': '^M.*'}} | name 以 M開頭 |
$exists | 屬性是否存在 | {'name': {'$exists': True}} | name 屬性存在 |
$type | 類型判斷 | {'age': {'$type': 'int'}} | age 的類型爲 int |
$mod | 數字模操做 | {'age': {'$mod': [5, 0]}} | 年齡模 5 餘 0 |
$text | 文本查詢 | {'$text': {'$search': 'Mike'}} | text 類型的屬性中包含 Mike 字符串 |
$where | 高級條件查詢 | {'$where': 'obj.fans_count == obj.follows_count'} | 自身粉絲數等於關注數 |
這些操做的更詳細用法在能夠在 MongoDB 官方文檔找到: https://docs.mongodb.com/manu...。
要統計查詢結果有多少條數據,能夠調用 count() 方法,如統計全部數據條數:
count = collection.find().count() print(count)
或者統計符合某個條件的數據:
count = collection.find({'age': 20}).count() print(count)
結果是一個數值,即符合條件的數據條數。
能夠調用 sort() 方法,傳入排序的字段及升降序標誌便可,示例以下:
results = collection.find().sort('name', pymongo.ASCENDING) print([result['name'] for result in results])
運行結果:
['Harden', 'Jordan', 'Kevin', 'Mark', 'Mike']
在這裏咱們調用了 pymongo.ASCENDING 指定升序,若是要降序排列能夠傳入 pymongo.DESCENDING。
在某些狀況下咱們可能想只取某幾個元素,在這裏能夠利用skip() 方法偏移幾個位置,好比偏移 2,就忽略前 2 個元素,獲得第三個及之後的元素。
results = collection.find().sort('name', pymongo.ASCENDING).skip(2) print([result['name'] for result in results])
運行結果:
['Kevin', 'Mark', 'Mike']
另外還能夠用 limit() 方法指定要取的結果個數,示例以下:
results = collection.find().sort('name', pymongo.ASCENDING).skip(2).limit(2) print([result['name'] for result in results])
運行結果:
['Kevin', 'Mark']
若是不加 limit() 本來會返回三個結果,加了限制以後,會截取 2 個結果返回。
值得注意的是,在數據庫數量很是龐大的時候,如千萬、億級別,最好不要使用大的偏移量來查詢數據,極可能會致使內存溢出,可使用相似以下操做來進行查詢:
from bson.objectid import ObjectId collection.find({'_id': {'$gt': ObjectId('593278c815c2602678bb2b8d')}})
這時記錄好上次查詢的 _id。
對於數據更新可使用 update() 方法,指定更新的條件和更新後的數據便可,例如:
condition = {'name': 'Kevin'} student = collection.find_one(condition) student['age'] = 25 result = collection.update(condition, student) print(result) Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎
在這裏咱們將 name 爲 Kevin 的數據的年齡進行更新,首先指定查詢條件,而後將數據查詢出來,修改年齡,以後調用 update() 方法將原條件和修改後的數據傳入,便可完成數據的更新。
運行結果:
{'ok': 1, 'nModified': 1, 'n': 1, 'updatedExisting': True}
返回結果是字典形式,ok 即表明執行成功,nModified 表明影響的數據條數。
另外咱們也可使用 $set 操做符對數據進行更新,代碼改寫以下:
result = collection.update(condition, {'$set': student})
這樣能夠只更新 student 字典內存在的字段,若是其原先還有其餘字段則不會更新,也不會刪除。而若是不用 $set 的話則會把以前的數據所有用 student 字典替換,若是本來存在其餘的字段則會被刪除。
另外 update() 方法其實也是官方不推薦使用的方法,在這裏也分了 update_one() 方法和 update_many() 方法,用法更加嚴格,第二個參數須要使用 $ 類型操做符做爲字典的鍵名,咱們用示例感覺一下。
condition = {'name': 'Kevin'} student = collection.find_one(condition) student['age'] = 26 result = collection.update_one(condition, {'$set': student}) print(result) print(result.matched_count, result.modified_count)
在這裏調用了 update_one() 方法,第二個參數不能再直接傳入修改後的字典,而是須要使用 {'$set': student} 這樣的形式,其返回結果是 UpdateResult 類型,而後調用 matched_count 和 modified_count 屬性分別能夠得到匹配的數據條數和影響的數據條數。
運行結果:
<pymongo.results.UpdateResult object at 0x10d17b678> 1 0
咱們再看一個例子:
condition = {'age': {'$gt': 20}} result = collection.update_one(condition, {'$inc': {'age': 1}}) print(result) print(result.matched_count, result.modified_count)
在這裏咱們指定查詢條件爲年齡大於 20,而後更新條件爲 {'$inc': {'age': 1}},也就是年齡加 1,執行以後會將第一條符合條件的數據年齡加 1。
運行結果:
<pymongo.results.UpdateResult object at 0x10b8874c8> 1 1
能夠看到匹配條數爲 1 條,影響條數也爲 1 條。
若是調用 update_many() 方法,則會將全部符合條件的數據都更新,示例以下:
condition = {'age': {'$gt': 20}} result = collection.update_many(condition, {'$inc': {'age': 1}}) print(result) print(result.matched_count, result.modified_count)
這時候匹配條數就再也不爲 1 條了,運行結果以下:
<pymongo.results.UpdateResult object at 0x10c6384c8> 3 3
能夠看到這時全部匹配到的數據都會被更新。
刪除操做比較簡單,直接調用 remove() 方法指定刪除的條件便可,符合條件的全部數據均會被刪除,示例以下:
result = collection.remove({'name': 'Kevin'}) print(result)
運行結果:
{'ok': 1, 'n': 1}
另外依然存在兩個新的推薦方法,delete_one() 和 delete_many() 方法,示例以下:
result = collection.delete_one({'name': 'Kevin'}) print(result) print(result.deleted_count) result = collection.delete_many({'age': {'$lt': 25}}) print(result.deleted_count)
運行結果:
<pymongo.results.DeleteResult object at 0x10e6ba4c8> 1 4 Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的彙集地,零基礎,進階,都歡迎
delete_one() 即刪除第一條符合條件的數據,delete_many() 即刪除全部符合條件的數據,返回結果是 DeleteResult 類型,能夠調用 deleted_count 屬性獲取刪除的數據條數。
另外 PyMongo 還提供了一些組合方法,如find_one_and_delete()、find_one_and_replace()、find_one_and_update(),就是查找後刪除、替換、更新操做,用法與上述方法基本一致。
另外還能夠對索引進行操做,如 create_index()、create_indexes()、drop_index() 等。
本節講解了 PyMongo 操做 MongoDB 進行數據增刪改查的方法,在後文咱們會在實戰案例中應用這些操做進行數據存儲。