那些年,我爬過的北科(五)——數據存儲之使用MongoDB

介紹

在前面咱們介紹瞭如何編寫爬蟲,可是咱們的爬蟲並無把數據保存下來,只是簡單的顯示在控制檯中。在本節,咱們將簡單學習一下數據庫,以及如何在python中操做數據庫。python

最後,咱們將修改上一節的爬蟲框架,使其支持數據庫插入。mongodb

注:若是讀者已經瞭解mongodb,能夠直接跳到最後一個部分:修改咱們的爬蟲框架數據庫

MongoDB數據庫介紹

數據庫其實也就是數據倉庫,用來存儲數據的地方。如下是數據庫在維基百科中的解釋:json

數據庫,簡而言之可視爲電子化的文件櫃——存儲電子文件的處所,用戶能夠對文件中的數據運行新增、截取、更新、刪除等操做。數組

沒有數據庫,咱們可能會把爬取的數據存成一個json文件,插入的時候可能要先把整個json序列化成python的列表,而後再進行增刪改查,並且數據操做的效率可能會比較低。有了數據庫,數據庫會給咱們提供方便的API接口,能夠很容易對數據進行增刪改查操做,而且高效。bash

MongoDB是一種文檔型數據庫,它屬於非關係型數據庫。服務器

MongoDB安裝

若是沒有安裝過MongoDB,須要先對mongodb進行一下安裝。併發

下載MongoDB

MongoDB有商業版也有社區版本,咱們下載免費的社區版本就行了。能夠在此處www.mongodb.com/download-ce…看到各類操做系統的MongoDB安裝包。框架

咱們能夠選擇一款適合本身的操做系統的進行下載安裝。下面咱們分別以Mac和Windows系統舉例來進行MongoDB的安裝。函數

Mac版本安裝與啓動

下載安裝包

首先咱們下載最新版本(4.0.4版本)的mongodb的包,能夠看到下載下來是一個tgz的解壓文件。

而後咱們對tgz文件進行解壓, 並進入到解壓後的目錄

tar zvxf mongodb-osx-ssl-x86_64-4.0.4.tgz
cd mongodb-osx-x86_64-4.0.4
複製代碼

進入目錄後,咱們能夠看到有一個bin目錄文件,這裏面就是mongodb的各類腳本了。

啓動MongoDB服務器

而後咱們須要建立數據存放的目錄,mongodb數據默認存放的路徑是/data/db,若是這個目錄不存在的話,須要本身建立。確保數據建立後,經過mongod命令便可啓動mongodb服務。

./bin/mongod  # 啓動mongodb服務
複製代碼

若是不想用/data/db這個路徑的話,能夠經過--dbpath參數設置想要存放的位置。

mkdir test
./bin/mongod --dbpath ./test  # 數據存放在當前路徑的test目錄下
複製代碼

使用MongoDB客戶端

接下來咱們測測能不能連上服務器,這裏能夠用mongodb自帶的客戶端:mongo命令。在終端中輸入如下命令,嘗試鏈接mongodb服務。

./bin/mongo
複製代碼

若是輸入命令後,成功看到左下角有個代輸入的光標,就說明安裝成功了。

Windows安裝與啓動

下載安裝包

首先咱們下載最新版本(4.0.4版本)的mongodb的包,咱們這個選擇zip安裝包。

下載後進行解壓,而後進入解壓後的目錄,能夠看到有一個bin文件夾,這裏面就是mongodb的各類腳本。

啓動MongoDB服務器

而後咱們須要建立數據存放的目錄,mongodb數據默認存放的路徑是C:\data\db,若是這個目錄不存在的話,須要本身建立。

確保數據建立後,經過mongod.exe命令便可啓動mongodb服務。

若是不想用C:\data\db這個路徑的話,能夠經過--dbpath參數設置想要存放的位置。

使用MongoDB客戶端

接下來咱們測測能不能連上服務器,這裏能夠用mongodb自帶的客戶端。在終端中運行mongo.exe,嘗試鏈接mongodb服務。

若是運行後,成功看到左下角有個代輸入的光標,就說明安裝成功了。

MongoDB的一些概念

MongoDB以BSON格式的文檔(Documents)形式存儲。Databases中包含集合(Collections),集合(Collections)中存儲文檔(Documents)。接下來咱們簡單瞭解一下這幾個概念。

Databases: 數據庫

Databases是數據庫,咱們通常會把不一樣的項目劃分紅不一樣的數據庫。

咱們可使用show dbs查看已有的數據庫,使用use db_name進入某個數據庫。下面是咱們進入test數據庫的截圖。

Collections: 集合

Collections是集合,一個項目中,也會有不一樣格式的數據。咱們通常會將同一種類型的數據放在一個集合裏面。好比說咱們開發網站有新聞,可能會建立一個news集合;也須要有用戶,再建立一個user集合。

集合的概念就如同關係型數據庫裏面的表同樣。

Documents: 文檔

Documents是文檔,文檔是由field和value對的結構組成,以下結構。

{
   field1: value1,
   field2: value2,
   field3: value3,
   ...
   fieldN: valueN
}
複製代碼

其中field名是個字符串,而value值能夠是任何BSON數據類型,包括:其餘document,數字,和document數組。

在MongoDB中集合不須要建立,直接使用就能夠,同理數據庫也不須要建立,直接使用use就能夠。

下面咱們在test數據庫下面的news集合中插入一條數據看看。

這裏能夠看到咱們的數據以下:

{ 
    "_id" : ObjectId("5c0aa0b51b0eb2e557167a5b"), 
    "title" : "hello world" 
}
複製代碼

除了咱們插入的title字段外,還有一個_id字段,這是一個索引字段,做爲一個文檔的惟一標識。咱們能夠經過_id對某一個文檔進行查找。

pymongo的使用

咱們這裏是一個python的教程,因此主要要學習一下如何在python中操做mongodb。在瞭解前,先安裝一下mongodb的python包:pymongo。

pip install pymongo
複製代碼

鏈接數據庫

在數據庫操做前,咱們首先要鏈接數據庫。這裏鏈接數據庫的代碼以下:

import pymongo

client = pymongo.MongoClient(host="localhost", port=27017)
複製代碼

上面咱們鏈接了咱們本地的mongodb數據庫,mongodb默認使用的端口是27017。當咱們不使用數據庫的時候,記得要把數據庫的鏈接關閉掉。

client.close()  # 關閉數據庫
複製代碼

接下來,咱們就能夠選擇咱們須要操做的數據庫和集合了。可使用字典或者點的方式拿到數據庫和集合的實例。

# 使用字典的方式
db = client['test']
items = db["items"]

# 使用點的方式
db = client.test
items = db.items
複製代碼

這裏咱們使用test數據庫下面的items集合進行示意。

插入數據

接下來,咱們先來插入幾條數據到啊items集合。這裏咱們先定義一個list_items函數用來列出items集合中全部的數據來。其實就是調用find方法,就能夠直接找出全部items下的數據了,返回對象是個迭代器,能夠經過for...in...拿到裏面全部的元素。

def list_items():
    """ 列出全部數據 """
    for item in items.find():
        print(item)
複製代碼

插入數據也很簡單,和在mongo命令使用的基本上是同樣的。就是一個insert方法,插入一個字典便可。

# 增
print("添加數據")
items.insert({"id": 1, "name": "test1"})
items.insert({"id": 2, "name": "test2"})
items.insert({"id": 3, "name": "test3"})
list_items()
複製代碼

mongodb一個集合中的數據能夠不徹底同樣,好比說可能有的文檔有name字段,而有的文檔沒有name字段。不過咱們最好不要這麼作,雖然mongodb容許,由於這樣可能會讓本身容易混亂。

items.insert({"id": 4, "no_name": "test4"})
list_items()
複製代碼

刪除數據

在mongodb中刪除數據直接使用remove方法就行了,remove的參數就是要刪除元素的條件,好比下面是刪除id爲1的數據。

print("刪除id爲1")
items.remove({"id": 1})
list_items()
複製代碼

不過上面演示的id實際上是假的id,由於它能夠不是惟一的。在mongodb中使用的_id字段做爲索引,這個索引是自動建立的,它是一個ObjectId類型。咱們能夠經過下面的代碼操做一個惟一的文檔。

更新數據

更新數據的話是用的update方法,update方法接受多個參數,主要有三個。

  • 第一個參數:spec,指定要更新的數據。
  • 第二個參數:document,要修改的數據。
  • 第三個參數:multi,是否要更新多條數據,默認爲False,也就是說默認只更新一條數據。

咱們先來看看更新一條數據。

print("修改id爲2的name")
items.update({"id": 2}, {
    '$set': {'name': "test2_modified"}
})
list_items()
複製代碼

這裏第二個參數以下:

{
    '$set': {
        'field1': value1,
        'field2': value2,
        'field3': value3,
        ...
        'fieldN': valueN
    }
}
複製代碼

這裏能夠看到咱們修改了id爲2的name字段。

接下來再來試試更新多條數據。

print("修改全部數據,添加一個title字段")
items.update({}, {
    '$set': {'title': "update title"}
}, multi=True)
list_items()
複製代碼

查找數據

最後是查找數據了,這裏能夠經過find方法找到多條數據,find_one方法找到一條數據。對於find_one方法,若是沒有找到的話,會返回None。

print("查找id爲2")
print(items.find_one({"id": 2}))
複製代碼

修改咱們的爬蟲框架

關於數據庫的介紹就到這裏了,若是讀者對mongodb操做感興趣能夠查閱更多相關資料。

下面,咱們把mongodb數據融入到咱們的爬蟲框架中,並經過框架把上一節的爬蟲爬取的內容存入數據庫中。這裏其實在框架裏面添加一行代碼便可。

進程間鎖

考慮一個場景,咱們在爬取數據的時候,確定不但願有重複的數據添加到數據庫裏面,因此咱們可能須要在插入數據庫以前,判斷一下這條數據有沒有插入過。判斷插入再插入的代碼以下:

insert_id = 2
if items.find_one({"id": insert_id}) is None:  # 步驟①:判斷
    items.insert({{"id": insert_id}})  # 步驟②:插入
複製代碼

可是,這裏考慮一個場景,在多進程操做時候,咱們兩個進程:進程1和進程2,同時使得insert_id爲2。這個時候數據都尚未插入,因此find_one以後獲得的都是None。可是下一時刻,進程1先插入了數據,這個時候進程2由於先前進行find_one也獲得的是None,因此就會插入兩條id爲2的數據。

雖然這個可能性很是低,可是不能排除。這裏咱們使用鎖就行了。

lock = Manager().Lock()

with lock:
    if items.find_one({"id": insert_id}) is None:  # 步驟①:判斷
        items.insert({{"id": insert_id}})  # 步驟②:插入
複製代碼

在操做插入代碼塊的時候,進程1和進程2要獲取鎖,才能執行。好比說這個時候進程1拿到鎖了,進程2沒有拿到,那麼進程2就會等待。進程1把步驟①和步驟②都完成以後,纔會釋放鎖。進程②拿到鎖,再進行步驟①和步驟②。

這樣就不會出現上面的那種狀況了。

因此,咱們能夠在咱們的框架中也添加一個這樣的鎖。咱們須要再咱們的框架中添加這個鎖,而後把這個鎖傳給worker,worker在須要數據庫操做的時候再使用這個鎖就行了。

爬蟲實踐

接下來咱們須要對上一節的worker進行稍微修改,首先添加lock參數。而後再把數據插入到數據庫中。

以後運行爬蟲,使用mongo客戶端檢驗一下爬取的數據是否存入數據庫。

在MongoDB自帶的客戶端中寫代碼查看有的時候有些麻煩,讀者也能夠裝一個MongoDB的可視化客戶端,我這裏用的是Robo 3T,也是有免費版本的。

補充說明

咱們上面的操做其實至關於同一時間只會操做一條數據,方式比較簡單粗暴。實際上,mongodb是能夠支持多鏈接的,也就是說能夠併發操做。

讀者感興趣的話,能夠考慮在框架中添加一個pipeline,在pipeline中構建多個mongodb數據庫的鏈接,用於專門操做數據庫數據。

相關文章
相關標籤/搜索