大數據時代,各行各業對數據採集的需求日益增多,網路爬蟲的運用也更為廣泛,越來越多的人開始學習網路爬蟲這項技術,K哥爬蟲此前已經推出不少爬蟲進階、逆向相關文章,為實現從易到難全方位覆蓋,特設【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 一樣不適合存儲大量數據。 因此在開發時,選擇哪種資料庫來存儲數據需要以自己的實際需求為準。