摘要:列存儲數據庫,代表有等。運行結果返回結果是字典形式,即代表執行成功,代表影響的數據條數。上一篇文章網絡爬蟲實戰數據存儲關系型數據庫存儲下一篇文章網絡爬蟲實戰數據存儲非關系型數據庫存儲
上一篇文章:Python3網絡爬蟲實戰---32、數據存儲:關系型數據庫存儲:MySQL
下一篇文章:Python3網絡爬蟲實戰---34、數據存儲:非關系型數據庫存儲:Redis
NoSQL,全稱 Not Only SQL,意為不僅僅是 SQL,泛指非關系型的數據庫。NoSQL 是基于鍵值對的,而且不需要經過 SQL 層的解析,數據之間沒有耦合性,性能非常高。
非關系型數據庫又可以細分如下:
鍵值存儲數據庫,代表有 Redis, Voldemort, Oracle BDB 等。
列存儲數據庫,代表有 Cassandra, HBase, Riak 等。
文檔型數據庫,代表有 CouchDB, MongoDB 等。
圖形數據庫,代表有 Neo4J, InfoGrid, Infinite Graph等。
對于爬蟲的數據存儲來說,一條數據可能存在某些字段提取失敗而缺失的情況,而且數據可能隨時調整,另外數據之間能還存在嵌套關系。如果我們使用了關系型數據庫存儲,一是需要提前建表,二是如果存在數據嵌套關系的話需要進行序列化操作才可以存儲,比較不方便。如果用了非關系數據庫就可以避免一些麻煩,簡單高效。
本節我們主要介紹一下 MongoDB 和 Redis 的數據存儲操作。
MongoDB存儲MongoDB 是由 C++ 語言編寫的非關系型數據庫,是一個基于分布式文件存儲的開源數據庫系統,其內容存儲形式類似 Json 對象,它的字段值可以包含其他文檔,數組及文檔數組,非常靈活,在這一節我們來看一下 Python3 下 MongoDB 的存儲操作。
1. 準備工作在本節開始之前請確保已經安裝好了 MongoDB 并啟動了其服務,另外安裝好了 Python 的 PyMongo庫,如沒有安裝可以參考第一章的安裝過程。
2. 連接MongoDB連接 MongoDB 我們需要使用 PyMongo 庫里面的 MongoClient,一般來說傳入 MongoDB 的 IP 及端口即可,第一個參數為地址 host,第二個參數為端口 port,端口如果不傳默認是 27017。
import pymongo client = pymongo.MongoClient(host="localhost", port=27017)
這樣我們就可以創建一個 MongoDB 的連接對象了。
另外 MongoClient 的第一個參數 host 還可以直接傳MongoDB 的連接字符串,以 mongodb 開頭,例如:
client = MongoClient("mongodb://localhost:27017/")
可以達到同樣的連接效果。
3. 指定數據庫MongoDB 中還分為一個個數據庫,我們接下來的一步就是指定要操作哪個數據庫,在這里我以 test 數據庫為例進行說明,所以下一步我們需要在程序中指定要使用的數據庫。
db = client.test
調用 client 的 test 屬性即可返回 test 數據庫,當然也可以這樣來指定:
db = client["test"]
兩種方式是等價的。
4. 指定集合MongoDB 的每個數據庫又包含了許多集合 Collection,也就類似與關系型數據庫中的表,下一步我們需要指定要操作的集合,在這里我們指定一個集合名稱為 students,學生集合,還是和指定數據庫類似,指定集合也有兩種方式:
collection = db.students collection = db["students"]
這樣我們便聲明了一個 Collection 對象。
5. 插入數據接下來我們便可以進行數據插入了,對于 students 這個Collection,我們新建一條學生數據,以字典的形式表示:
student = { "id": "20170101", "name": "Jordan", "age": 20, "gender": "male" }
在這里我們指定了學生的學號、姓名、年齡和性別,然后接下來直接調用 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)
運行結果:
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 列表,運行結果:
6. 查詢[ObjectId("5932abf415c2607083d3b2ac"), ObjectId("5932abf415c2607083d3b2ad")]
插入數據后我們可以利用 find_one() 或 find() 方法進行查詢,find_one() 查詢得到是單個結果,find() 則返回一個生成器對象。
result = collection.find_one({"name": "Mike"}) print(type(result)) print(result)
在這里我們查詢 name 為 Mike 的數據,它的返回結果是字典類型,運行結果:
{"_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)
運行結果:
{"_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"}
返回結果是 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...。
7. 計數要統計查詢結果有多少條數據,可以調用 count() 方法,如統計所有數據條數:
count = collection.find().count() print(count)
或者統計符合某個條件的數據:
count = collection.find({"age": 20}).count() print(count)
結果是一個數值,即符合條件的數據條數。
8. 排序可以調用 sort() 方法,傳入排序的字段及升降序標志即可,示例如下:
results = collection.find().sort("name", pymongo.ASCENDING) print([result["name"] for result in results])
運行結果:
["Harden", "Jordan", "Kevin", "Mark", "Mike"]
在這里我們調用了 pymongo.ASCENDING 指定升序,如果要降序排列可以傳入 pymongo.DESCENDING。
9. 偏移在某些情況下我們可能想只取某幾個元素,在這里可以利用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。
10. 更新對于數據更新可以使用 update() 方法,指定更新的條件和更新后的數據即可,例如:
condition = {"name": "Kevin"} student = collection.find_one(condition) student["age"] = 25 result = collection.update(condition, student) print(result)
在這里我們將 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 屬性分別可以獲得匹配的數據條數和影響的數據條數。
運行結果:
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。
運行結果:
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 條了,運行結果如下:
3 3
可以看到這時所有匹配到的數據都會被更新。
11. 刪除刪除操作比較簡單,直接調用 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)
運行結果:
1 4
delete_one() 即刪除第一條符合條件的數據,delete_many() 即刪除所有符合條件的數據,返回結果是 DeleteResult 類型,可以調用 deleted_count 屬性獲取刪除的數據條數。
12. 更多另外 PyMongo 還提供了一些組合方法,如find_one_and_delete()、find_one_and_replace()、find_one_and_update(),就是查找后刪除、替換、更新操作,用法與上述方法基本一致。
另外還可以對索引進行操作,如 create_index()、create_indexes()、drop_index() 等。
詳細用法可以參見官方文檔:http://api.mongodb.com/python...。
另外還有對數據庫、集合本身以及其他的一些操作,在這不再一一講解,可以參見官方文檔:http://api.mongodb.com/python...。
13. 結語本節講解了 PyMongo 操作 MongoDB 進行數據增刪改查的方法,在后文我們會在實戰案例中應用這些操作進行數據存儲。
上一篇文章:Python3網絡爬蟲實戰---32、數據存儲:關系型數據庫存儲:MySQL
下一篇文章:Python3網絡爬蟲實戰---34、數據存儲:非關系型數據庫存儲:Redis
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44078.html
摘要:的安裝是一個輕量級的關系型數據庫,以表的形式來存儲數據,本節我們來了解下它的安裝方式。相關鏈接官方網站下載地址中文教程下的安裝推薦使用安裝,執行命令即可。上一篇文章網絡爬蟲實戰解析庫的安裝下一篇文章網絡爬蟲實戰存儲庫的安裝 上一篇文章:Python3網絡爬蟲實戰---3、解析庫的安裝:LXML、BeautifulSoup、PyQuery、Tesserocr下一篇文章:Python3網絡...
摘要:關系型數據庫有多種,如等等。一致性與原子性是密切相關的。持久性持續性也稱永久性,指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。如此一來,我們就可以實現主鍵不存在便插入數據,存在則更新數據的功能了。 上一篇文章:Python3網絡爬蟲實戰---31、數據存儲:文件存儲下一篇文章:Python3網絡爬蟲實戰---33、數據存儲:非關系型數據庫存儲:MongoDB 關系型數據...
摘要:上一篇文章網絡爬蟲實戰數據存儲非關系型數據庫存儲下一篇文章網絡爬蟲實戰數據爬取是一個基于內存的高效的鍵值型非關系型數據庫,存取效率極高,而且支持多種存儲數據結構,使用也非常簡單,在本節我們介紹一下的操作,主要介紹這個庫的用法。 上一篇文章:Python3網絡爬蟲實戰---33、數據存儲:非關系型數據庫存儲:MongoDB下一篇文章:Python3網絡爬蟲實戰---35、 Ajax數據爬...
摘要:相關鏈接官方文檔安裝推薦使用安裝,命令如下運行完畢之后即可完成的安裝。上一篇文章網絡爬蟲實戰數據庫的安裝下一篇文章網絡爬蟲實戰庫的安裝 上一篇文章:Python3網絡爬蟲實戰---4、數據庫的安裝:MySQL、MongoDB、Redis下一篇文章:Python3網絡爬蟲實戰---6、Web庫的安裝:Flask、Tornado 在前面一節我們介紹了幾個數據庫的安裝方式,但這僅僅是用來存...
摘要:所以說,我們所看到的微博頁面的真實數據并不是最原始的頁面返回的,而是后來執行后再次向后臺發送了請求,拿到數據后再進一步渲染出來的。結果提取仍然是拿微博為例,我們接下來用來模擬這些請求,把馬云發過的微博爬取下來。 上一篇文章:Python3網絡爬蟲實戰---34、數據存儲:非關系型數據庫存儲:Redis下一篇文章:Python3網絡爬蟲實戰---36、分析Ajax爬取今日頭條街拍美圖 ...
閱讀 1801·2021-11-18 10:02
閱讀 3530·2021-11-16 11:45
閱讀 1790·2021-09-10 10:51
閱讀 2111·2019-08-30 15:43
閱讀 1378·2019-08-30 11:23
閱讀 1489·2019-08-29 11:07
閱讀 1895·2019-08-23 17:05
閱讀 1415·2019-08-23 16:14