【0基礎學爬蟲】爬蟲基礎之資料儲存

K哥爬蟲發表於2023-04-14

0

大資料時代,各行各業對資料採集的需求日益增多,網路爬蟲的運用也更為廣泛,越來越多的人開始學習網路爬蟲這項技術,K哥爬蟲此前已經推出不少爬蟲進階、逆向相關文章,為實現從易到難全方位覆蓋,特設【0基礎學爬蟲】專欄,幫助小白快速入門爬蟲,本期為資料儲存。

概述

上期我們介紹到了檔案儲存,講到了如何將資料存入各種文字檔案之中,這種資料儲存方式雖然很簡便,但是存在很多問題,如:資料容易丟失、檔案容易損壞、資料不易共享。因此本期將介紹更加實用的資料庫儲存方式。

本文將介紹三種流行的資料儲存技術:

MySQL:一種關係型資料庫管理系統,廣泛用於企業級應用程式中

MongoDB:一種文件型資料庫,適合處理半結構化資料和大規模資料集。

Redis:一種記憶體資料庫,用於處理高速讀寫操作和快取資料。

在本文中,我們將分別介紹 MySQL、MongoDB 和 Redis 的優缺點、適用場景以及如何選擇最適合自己的資料庫儲存技術。作為爬蟲初學者,本文將幫助你更好地理解這三種資料庫儲存技術的工作原理,以及如何選擇適合你的應用程式的資料庫。

MySQL

介紹

MySQL 是一種開源的關係型資料庫管理系統,是目前最流行的關係型資料庫之一。MySQL 是一個快速、高效的資料庫系統,能夠處理大量的資料和請求。另一個優點是它的靈活性和可擴充套件性,可以根據需要進行配置和調整,以滿足不同應用的需求。MySQ L使用SQL(結構化查詢語言)作為其查詢和管理語言,SQL 是一種標準的關係型資料庫語言,用於定義、操作和查詢資料。

MySQL 被廣泛用於 Web 開發、資料分析和資料儲存等領域,是一個非常強大和受歡迎的資料庫系統。同時,由於它是開源軟體,因此可以在不支付任何費用的情況下使用和修改,這也使得它成為了很多開發者的首選資料庫系統。

安裝

首先需要安裝 MySQL 資料庫,在 MySQL官網 下載對應版本的檔案進行安裝。

安裝好 MySQL 並確保 MySQL 能夠正常執行後需要安裝 Python 的第三方庫 PyMySQL。

pip install pymysql

使用

在使用 Python 操作 MySQL 資料庫前,我們需要先了解一下基本的 sql 語句。

sql 語句

SQL 即結構化查詢語言 (Structured Query Language),是一種特殊目的的程式語言,是一種資料庫查詢和程式設計語言。

資料庫操作

-- 建立資料庫
create database 資料庫庫名;
-- 檢視所有資料庫
show databases;
-- 使用資料庫
use 資料庫庫名;
-- 刪除資料庫
drop database 資料庫庫名;

表操作

-- 建立表
create table 表名(
    屬性名 資料型別 約束,
    .
    .
);
-- 檢視錶結構
desc 表名;
-- 修改表名
alter table 表名 rename to 新的表名;
-- 新增新欄位
alter table 表名 add 屬性;資料型別;約束;
-- 刪除一個欄位
alter table 表名 drop 屬性名;
-- 刪除表
drop table 表名;
約束描述
PRIMARY KEY主鍵約束。第一正規化要求每一張表都應該有一個主鍵作為表的唯一標識,主鍵具有唯一性。
UNIQUE唯一約束。標識該屬性的值是唯一的。
NOT NULL非空約束。標識該屬性的值不能為空。
FOREIGN KEY外來鍵約束。 標識該屬性為該表的外來鍵,與某表的主鍵關聯。
AUTO_INCREMENT標識該屬性的值自動增加
DEFAULT為該屬性設定預設值

插入資料

insert into 表名(屬性1, 屬性2, 屬性3) values(值1,值2,值3);

修改資料

-- 修改指定資料
update 表名 set 屬性1 = 值1, 屬性2 = 值2 where 條件表示式;

刪除資料

-- 刪除表中所有資料
delete from 表名;
-- 刪除指定資料
delete from 表名 where 條件表示式;

查詢資料

-- 查詢所有資料
select * from 表名;
-- 條件查詢
select 欄位1,欄位2 from 表名 where 欄位 in (值1,值2,值3...)
-- 多條件查詢
select 欄位1,欄位2 from 表名 where 欄位1 in (值1,值2,值3...) and 欄位2 in (值4,值5,值6...)
-- 範圍查詢
select 欄位1,欄位2 from 表名 where 欄位 BETWEEN 值1 and 值2; -- 查詢欄位值在值1到值2之間的記錄
-- 模糊查詢
-- % 代表任意字元;
-- _ 代表單個字元;
select * from 表名 where 欄位 like '張%'; -- 查詢欄位值以張開頭的記錄
select * from 表名 where 欄位 like '_張%'; -- 查詢欄位值第二位是張的記錄
-- 排序
select 欄位 from 表名 order by 欄位名 ASC|DESC; -- ASC升序 DESC降序 
-- 去重
select DISTINCT 欄位 from 表名;
-- 分組查詢
select 欄位1,AVG(欄位2) from 表名 group by 欄位1;     -- 按欄位1分組,查詢欄位2的平均值
-- 示例
select gender,avg(grade) from users group by gender; -- 按性別分組,查詢各性別的平均成績

Python 操作 MySQL

連線資料庫

import pymysql

db = pymysql.connect(
    host='localhost',
    user='root',
    database='user',
    password='test123',
    port=3306,
    charset='utf8mb4'
)

#獲取操作遊標
cursor = db.cursor()

host:IP地址

user:使用者名稱

password:密碼

database:庫名

port:資料庫埠

charset:字符集編碼

連線資料庫後,呼叫 cursor() 方法獲取對資料庫的操作遊標,透過遊標可以執行 sql 語句。

建立表

#sql 語句
sql = 'CREATE TABLE students (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) NOT NULL, age INT NOT NULL, grade INT)'

# 透過遊標執行 sql 語句
cursor.execute(sql)

#關閉資料庫連線
db.close()

插入資料

上一步中我們建立了一張 students 表,現在我們要向 students 表中插入資料。

#插入語句
sql = 'INSERT INTO students(id, name, age, grade) values ("%(id)s", "%(name)s", %(age)d, %(grade)d)'

try:
    #執行語句
    cursor.execute(sql % {'id': '1001', 'name': '張三', 'age': 25, 'grade': 92})
    #提交
    db.commit()
except:
    #插入異常則回滾資料
    print('插入異常')
    db.rollback()

db.close()

插入資料時,字串型別的資料應被單引號或雙引號包裹,否則會導致程式異常。在執行語句和提交語句時應該進行異常處理,發生異常時回滾資料,確保事務的原子性。

更新資料

sql = 'UPDATE students SET grade = %(grade)d WHERE id = "%(id)s"'

try:
    cursor.execute(sql % {'id':'1001', 'grade': 90})
    db.commit()
except:
    db.rollback()

db.close()

刪除資料

sql = 'DELETE FROM students WHERE id = "%(id)s"'

try:
    cursor.execute(sql % {'id':'1001'})
    db.commit()
except:
    db.rollback()

db.close()

查詢資料

在查詢資料之前,我們可以重新建立一個新的表,插入一些資料來作為案例。

# 建立表
create_table_sql = """CREATE TABLE students (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  age INT NOT NULL,
  gender ENUM('男', '女') DEFAULT '男' NOT NULL,
  grade INT DEFAULT NULL
);
"""

# 插入資料
insert_sql = """
INSERT INTO students (id, name, age, gender, grade)
VALUES
  (1, '張三', 20, '男', 80),
  (2, '李四', 19, '男', 75),
  (3, '王五', 21, '男', 88),
  (4, '趙六', 18, '女', 92),
  (5, '錢七', 20, '女', 85),
  (6, '孫八', 19, '男', 78),
  (7, '周九', 21, '女', 90),
  (8, '吳十', 18, '男', 86),
  (9, '鄭一', 20, '女', 81),
  (10, '王二', 19, '男', 77);
"""

執行查詢語句後,可以呼叫 fetchall 方法獲取查詢結果。

# 查詢年齡大於20歲的記錄
sql = """
SELECT * FROM students WHERE age > 20
"""

cursor.execute(sql)
# 返回一條查詢結果
# result = cursor.fetchall()
# 返回所有查詢結果
result = cursor.fetchall()
print(result)
# ((3, '王五', 21, '男', 88), (7, '周九', 21, '女', 90))
# 查詢姓名以張開頭的記錄
select_like_sql = """
SELECT * FROM students WHERE name like '張%'
"""
((1, '張三', 20, '男', 80),)

# 以性別分組查詢平均分
select_group_sql = """
SELECT gender, avg(grade) FROM students group by gender
"""
(('男', Decimal('80.6667')), ('女', Decimal('87.0000')))

MongoDB

介紹

上文中講到的 MySQL 是一種關係型資料庫,而 MongoDB 與下文中的 Redis 都屬於非關係型資料庫,也被稱為 NoSQL(Not Only SQL)。MongoDB 是一個基於分散式檔案儲存的資料庫。由 C++ 語言編寫。旨在為 WEB 應用提供可擴充套件的高效能資料儲存解決方案。

MongoDB 是一個介於關聯式資料庫和非關聯式資料庫之間的產品,是非關聯式資料庫當中功能最豐富,最像關聯式資料庫的。它將資料儲存為一個文件,資料結構由鍵值(key=>value)對組成。MongoDB 文件類似於 JSON 物件。欄位值可以包含其他文件,陣列及文件陣列。

安裝

首先需要安裝 MongoDB 資料庫,在 MongoDB官網 下載對應版本的檔案進行安裝。

安裝好 MongoDB 並確保 MongoDB 能夠正常執行後需要安裝 Python 的第三方庫 PyMongo。

pip install pymongo

使用

首先需要了解一下 MongoDB 的常用命令

-- 檢視當前資料庫
db
-- 檢視所有資料庫
show dbs
-- 切換資料庫(不存在則建立)
use 資料庫名
-- 刪除資料庫
db.dropDatabase()
-- 建立集合
db.createCollection(集合名稱, 建立引數)
-- 檢視集合(與 MySQL 中的表相似)
show tables
show collections
-- 刪除集合
db.集合名稱.drop()

Python 操作 MongoDB

連線資料庫

import pymongo

client = pymongo.MongoClient('mongodb://localhost:27017/')

插入資料

# 使用test庫(沒有則建立)
db = client['test']

# 建立一個集合
students = db['students']

data = {'id':'1001','name':'張三','age':20,'gender':'男'}
# 插入一條
result_one = students.insert_one(data) 
# 插入多條
resutl_many = students.insert_many([{'id':'1002','name':'李四','age':22,'gender':'男'},{'id':'1003','name':'王五','age':24,'gender':'女'}]) 

print(result)
print(resutl_many)
# <pymongo.results.InsertOneResult object at 0x00000200BB19AA88>
# <pymongo.results.InsertManyResult object at 0x00000200BB1D6E08>

查詢資料

# 查詢一條name為李四的資料
result_one = students.find_one({'name':'李四'})
# {'_id': ObjectId('64375e380fa1b587bc84e32d'), 'id': '1002', 'name': '李四', 'age': 22, 'gender': '男'}

# 查詢多條,返回一個生成器
result_many = students.find({'gender':'男'})
# <pymongo.cursor.Cursor object at 0x000002527225A888>
for result in result_many:
    print(result)
# {'_id': ObjectId('64375e3b89cfb1bb0c54b1c3'), 'id': '1001', 'name': '張三', 'age': 20, 'gender': '男'}
# {'_id': ObjectId('64375e3b89cfb1bb0c54b1c4'), 'id': '1002', 'name': '李四', 'age': 22, 'gender': '男'}

比較符

# 查詢年齡大於20的資料
students.find({'age':{'$gt':20}})
# 查詢年齡不等於20的資料
students.find({'age':{'$ne':20}})
符號含義
$lt小於
$gt大於
$lte小於等於
$gte大於等於
$ne不等於
$in在範圍內
$nin不在範圍內

更新資料

query = {"name":"張三"}
new_values = {"$set":{ "age":25 }}
# 更新第一條符合條件的資料
# students.update_many(query, new_values)
# 更新所有符合條件的資料
result = students.update_many(query, new_values)
# <pymongo.results.UpdateResult object at 0x0000022C92BA8E08>

刪除資料

# 刪除一條
query = {"name": "張三"}
students.delete_one(query)
# 刪除多條
query = {"age": {"$gt":22}}
students.delete_many(query)
# 刪除所有
students.delete_many({})
# 刪除集合
students.drop()

Redis

介紹

Redis是一個基於記憶體的鍵值對儲存系統,也被稱為資料結構伺服器,支援多種資料結構。它被廣泛用於快取、會話管理、訊息佇列等應用程式中。

安裝

首先需要安裝 Redis 資料庫,安裝好 Redis 並確保 Redis 能夠正常執行後需要安裝 Python 的第三方庫 redis-py。

pip install redis

使用

Redis 基本資料型別

字串:字串(string)是 redis 最基本的資料型別,它可以包含任意資料。

雜湊:雜湊(hash)是一個鍵值對集合,是一個 string 型別的 field 和 value 的對映表。

列表:列表(list)是簡單的字串列表,按插入順序排序,reids 列表支援在它的頭尾部插入資料。

集合:集合(set)是字串型別的無序集合,集合內的元素具有唯一性。

有序集合:有序集合(zset)與集合一樣也是字串型別的集合。不同的是有序集合中每個元素都會關聯一個 double 型別的分數,它會透過分數來對元素進行升序排序。

Python 操作 Redis

連線資料庫

from redis import StrictRedis

redis = StrictRedis(host='localhost',port=6379,decode_responses=True)
# Redis<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>

字串操作

redis 預設返回結果是位元組,在連線時設定 decode_responses=True 可以將返回結果改為字串。

# 新增一條資料,ex 為過期時間(秒),過期後鍵name的值就變為None
redis.set('name','張三', ex=3)
redis.set('nick','張三三三')
# 返回指定鍵的值
redis.get('name') # 張三
# 設定新值,返回舊值
redis.getset('name','李四') # 張三
# 根據位元組取值,(0,2)取前三位的位元組,一個漢字三個位元組,一個字母一個位元組
print(redis.getrange('nick', 0, 2)) # 張
# 從指定位置開始修改內容
redis.setrange('nick',3,'五五五')
redis.get('nick') # 張五五五三
# 批次取值
redis.mget('name','nick') # ['李四', '張五五五']
# 批次賦值
redis.mset({'key1':'value1','key2':'value2'})
redis.mget('key1', 'key2') # ['value1', 'value2']

雜湊操作

# 單個新增,向 hash1 中設定一個鍵值對(hash1存在則修改,不存在則建立)
redis.hset('hash1','key1','value1')
redis.hset('hash1','key2','value2')
# 取hash1中所有的key
redis.hkeys('hash1') # ['key1', 'key2']
# 單個取hash1的key對應的值
redis.hget('hash1', 'key1') # value1
# 多個取hash1的key對應的值
# 批次新增
redis.hmset('hash2', {'key3': 'value3', 'key4': "value4"})
# 批次取出
redis.hmget('hash2','key3','key4') # ['value3', 'value4']
# 取出所有鍵值對
redis.hgetall('hash2') # {'key3': 'value3', 'key4': 'value4'}
# 取出所有值
redis.hvals('hash2') # ['value3', 'value4']
# 取出所有鍵
redis.hkeys('hash2') # ['key3', 'key4']

列表操作

# 將元素新增到列表最左邊,列表不存在則新建
redis.lpush('grade', 88, 87, 92)
# 將元素新增到列表最右邊,列表不存在則新建
redis.rpush('grade', 78, 67, 99)
# 同字串切片
redis.lrange('grade', 0, -1) # ['92', '87', '88', '78', '67', '99']
# 向已有列表新增資料,列表不存在不會新建
redis.lpushx('age',22)
# 返回列表長度
redis.llen('age') # 0
# 在某個值的前或後插入一個值
# 在左邊一個元素 88 前插入元素 66 
redis.linsert('grade','before',88,66)
# 修改列表中某個位置的值
# 將索引號為0的元素值修改為77
redis.lset('grade', 0, 77) 
# 刪除指定值
# 刪除左邊第一次出現的 87
redis.lrem('grade', 87, 1)
# 刪除所有87
redis.lrem('grade', 87, 0)
# 刪除並返回
redis.lpop('grade') # 刪除最左邊的元素並返回

集合操作

# 新增元素
redis.sadd('count', 88, 87, 92)
# 集合長度
redis.scard('count') # 3
# 獲取所有元素
redis.smembers('count') # {'92', '87', '88'}
# 刪除隨機元素並返回
redis.spop('count')  # 87
# 刪除指定元素
redis.srem('count',88)
# 差集,返回在集合1中且不在集合2中的元素集合
redis.sadd('set1', 12, 13, 14, 15)
redis.sadd('set2', 12, 15, 18, 21)
# 在set1中且不在set2中的元素
redis.sdiff('set1','set2') # {'13', '14'}
# 交集,返回多個集合相同的元素集合
redis.sinter('set1','set2') # {'15', '12'}
# 並集,返回多個集合的並集
redis.sunion('set1','set2') # {'13', '12', '18', '21', '14', '15'}

有序集合操作

# 新增元素
redis.zadd('fruit',{'apple':10,'banana':6})
# 集合長度
redis.zcard('fruit') # 2
# 獲取所有元素
redis.zrange('fruit',0,-1) # ['banana', 'apple']
# 從大到小排序
redis.zrevrange('fruit',0,-1) # ['apple', 'banana']
# 獲取在某個區間中的元素個數
redis.zcount('fruit', 5, 8) # 1
# 刪除指定值
redis.zrem('fruit', 'apple')
# 根據範圍刪除
redis.zremrangebyscore('fruit', 5, 8)
# 獲取值對應的分數
redis.zscore('fruit', 'apple') # 10.0

總結

以上講到了三種資料庫的基本使用方法以及它們各自的特點,MySQL 使用 sql 語句來對資料進行操作,比較成熟,但是在海量資料處理時效率會顯著變慢。MongoDB是一個面向集合的,模式自由的文件型資料庫,採用虛擬記憶體與持久化的儲存方式,能夠儲存 JSON 風格的資料,能做到資料的高速讀寫,但是它不支援事務操作,且佔用空間過大。Redis 則是一個純粹的記憶體資料庫,所有資料存放在記憶體中,它也支援資料的持久化,可以將資料存到磁碟中,它擁有多種資料型別,效能極高,但與 MongoDB 一樣不適合儲存大量資料。 因此在開發時,選擇哪種資料庫來儲存資料需要以自己的實際需求為準。

相關文章