第5章 用数据库存储数据
5.1 MySQL 数据库
用 CSV 和 Excel 存储数据有两个优点:
- 非开发人员也能看到数据,不需要额外的学习成本。
- 使用方便,数据存储在文件里,复制到其他设备上可以直接查看。
这种表格存储文件的形式适用于少量数据的情况,当记录很多、字段很多时,打开文件会非常慢,而且卡顿,多个 Sheet 之间不能设计复杂的数据关系,这时就要使用数据库了。
- 什么是数据库
数据库是按照数据结构来组织、存储和管理数据的仓库,简单点说就是存储数据的仓库。 - 关系型数据库和非关系型数据库
前者通过二维表保存,存储行列组成的表,通过表与表间的关联关系来体现;后者又称 NoSQL,基于键值对,不需要经过 SQL 层解析,数据间没有耦合,性能非常好。关系型数据库中非常流行的MySQL 数据库。
5.1.1 安装 MySQL
- 官网:https://www.mysql.com/cn/
- 官方文档:https://dev.mysql.com/doc/
- 官网下载地址:https://www.mysql.com/downloads/
- 中文教程(非官方):http://www.runoob.com/mysql/mysql-tutorial.html
5.1.2 MySQL 的安装教程
windows环境下
https://c.biancheng.net/view/7135.html
ubantu环境下
https://zhuanlan.zhihu.com/p/610793026
mac环境下
https://blog.csdn.net/guorenhao/article/details/124508441
5.1.3 MySQL 的基本操作
- 用户登录
# 回车后,需要输入在安装MySQL时设置的密码
mysql -u root -p
- 查看数据库
show databases;
执行结果如下:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.01 sec)
- 选择数据库
use 数据库名
执行结果如下:
mysql> use mysql;
Database changed
- 查看数据库里的所有表
show tables;
执行结果如下:
mysql> show tables;
+---------------------------+
| Tables_in_mysql |
+---------------------------+
| columns_priv |
| component |
| db |
| default_roles |
| engine_cost |
| func |
| general_log |
| global_grants |
| gtid_executed |
| help_category |
| help_keyword |
| help_relation |
| help_topic |
| innodb_index_stats |
| innodb_table_stats |
| password_history |
| plugin |
| procs_priv |
| proxies_priv |
| role_edges |
| server_cost |
| servers |
slave_master_info |
| slave_relay_log_info |
| slave_worker_info |
| slow_log |
| tables_priv |
| time_zone |
| time_zone_leap_second |
| time_zone_name |
| time_zone_transition |
| time_zone_transition_type |
| user |
+---------------------------+
33 rows in set (0.00 sec)
基本的操作就这些,对于数据库的相关操作,在进入数据库后可以通过编写、执行数据库语句完成。
5.1.4 MySQL 数据库语法速成
- MySQL 数据类型
数据类型就是定义存储什么类型的数据,如数字、日期、字符串等。举个简单的例子,想保存一个数字,就要定义一个数字类型的字段,而不能定义一个数字类型的字段,然后往里面放字符串。
MySQL 中支持以下几种数据类型: - 整型(取值范围如果加了 unsigned,则最大值翻倍)
- TINYINT(m):1 字节,范围为-128~127。
- SMALLINT(m):2 字节,范围为-32 768~32 767。
- MEDIUMINT(m):3 字节,范围为-8 388 608~8 388 607。
- INT(m):4 字节,范围为-2 147 483 648~2 147 483 647。
- BIGINT(m):8 字节,范围为±9.22×10^18。
- 浮点型
- FLOAT(m,d):单精度浮点型,8 位精度(4 字节),m 为总个数,d 为小数位。
- DOUBLE(m,d):双精度浮点型,16 位精度(8 字节),m 为总个数,d 为小数位。
- 字符串
- CHAR 类型的字符串检索速度要比 VARCHAR 类型快。
- TEXT 类型不能有默认值,VARCHAR 查询速度快于 TEXT。
- CHAR(n):固定长度,最多 255 个字符。
- VARCHAR(n):可变长度,最多 65 535 个字符。
- TINYTEXT:可变长度,最多 255 个字符。
- TEXT:可变长度,最多 65 535 个字符。
- MEDIUMTEXT:可变长度,最多 2^24-1 个字符。
- LONGTEXT:可变长度,最多 2^32-1 个字符。
- 二进制数据
- _BLOB:以二进制方式存储,不分大小写,不用指定字符集,只能整体读出。
- _TEXT:以文本方式存储,英文存储区分大小写,可以指定字符集。
- 日期时间类型
- DATE:日期。
- TIME:时间。
- DATETIME:日期时间。
- TIMESTAMP:自动存储记录修改时间。
- 数据类型的属性
数据类型的属性就是对字段加一些限定条件,如设置默认值,如果写入记录没有这个
字段,就把该字段的值设置为默认值。除此之外,还有主键和字段是否为空等,相关的属
性如下所示。
- NULL:数据列可包含 NULL 值,就是可以不设置值。
- NOT NULL:数据列不允许包含 NULL 值。
- DEFAULT:默认值。
- PRIMARY KEY:主键。
- AUTO_INCREMENT:自动递增,适用于整数类型。
- UNSIGNED:无符号。
- CHARACTER SET name:指定一个字符集。
- 库操作
MySQL 提供了如下库操作命令。 - 建库。
CREATE DATABASE 数据库名;
- 删库(删除的数据库无法恢复)。删除不存在的库会报 database doesn't exist 的错误,故删库前要先用 IF EXISTS 进行判断。
DROP DATABASE IF EXISTS 数据库名;
- 表操作
MySQL 提供的与表操作相关的命令如下:
# 建表,比如
CREATE TABLE test
(
_id VARCHAR(50) NOT NULL PRIMARY KEY,
dsec TEXT NULL,
images TEXT NULL,
url TEXT NULL,
type VARCHAR(50) DEFAULT '' NULL
);
# 清空表数据,整体删除,速度较快,会重置Identity(标识列、自增字段)
TRUNCATE 表名
# 删除表数据,逐条删除,速度较慢,不会重置Identity,配合WHERE关键字可以删除部分数
据
DELETE FROM 表名
# 删表
DROP TABLE 表名
# 重命名表
ALTER TABLE 原表名 RENAME 新表名;
RENAME TABLE 原表名 TO 新表名;
# 增加列
ALTER TABLE 表名 Add column 新字段 数据类型 AFTER 在哪个字段后添加
# 删除列
ALTER TABLE 表名 DROP 字段名;
# 重命名列/数据类型
ALTER TABLE 表名 CHANGE 原列名 新列名 数据类型;
# 增加主键
ALTER TABLE 表名 ADD PRIMARY KEY (主键名);
# 删除主键
ALTER TABLE 表名 DROP PRIMARY KEY;
# 添加唯一索引
ALTER TABLE 表名 ADD UNIQUE 索引名 (列名);
# 添加普通索引
ALTER TABLE 表名 ADD INDEX 索引名 (列名);
# 删除索引
ALTER TABLE 表名 DROP INDEX 索引名;
# 把表默认的字符集和所有字符列(CHAR, VARCHAR, TEXT)改为新的字符集
ALTER TABLE 表名 CONVERT TO CHARACTER SET utf8;
# 修改表某一列的编码
ALTER TABLE 表名 CHANGE 列名 varchar(255) CHARACTER SET utf8;
# 仅仅改变一个表的默认字符集
ALTER TABLE 表名 DEFAULT CHARACTER SET utf8;
- 案例
下面通过一个实例来演示
# 建新数据库
CREATE DATABASE test
# 新建一个表person,字段有(自增id、名字、年龄、性别)
CREATE TABLE person(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(30) NOT NULL DEFAULT '',
age INT,
sex CHAR(2)
);
# 在表中插入5条数据
INSERT INTO person (name, age, sex) VALUES ('小明', 8, '男');
INSERT INTO person (name, age, sex) VALUES ('小红', 14, '女');
INSERT INTO person (name, age, sex) VALUES ('小白', 4, '男');
INSERT INTO person (name, age, sex) VALUES ('小宝', 6, '男');
INSERT INTO person (name, age, sex) VALUES ('小莉', 16, '女');
# 更新表中的数据(不添加WHERE子句筛选,更新的会是整个表的某列)
UPDATE person SET age = 10, sex = '女' WHERE name = '小明';
# 在表中插入数据,如果已存在则更新数据
INSERT INTO person (id,name, age, sex) VALUES (1,'小明', 20, '男') ON DUPLICATE KEY
UPDATE age = '20';
# 删除特定记录
DELETE FROM person WHERE age < 10;
# 查询数据
SELECT * FROM person; # 查询所有数据
SELECT name,age FROM person; # 查询特定列
SELECT name AS '姓名',age AS '年龄'FROM person; # 为检索出来的列设置别名
SELECT name FROM person WHERE age > 15 AND age <=20; # 条件查询
SELECT name FROM person WHERE age BETWEEN 15 AND 20; # 范围查询
# 数据求总和,平均值,最大值,最小值,记录数
SELECT SUM(age),AVG(age), MAX(age),MIN(age), COUNT(age) FROM person;
# 查询的时候排序:升序(ASC),降序(DESC)
SELECT * FROM person ORDER BY age ASC;
- 事务
事务就是一系列数据库的操作,要么完全执行,要么完全不执行。举个简单的例子:银行转账,转账后数据库对你的余额扣钱,别人收到钱,数据库对他的余额加钱,如果转账失败,则保证两个人的余额不变,而不是你的钱扣了,他余额没变,或者你的钱没扣,他的钱反而多了。可以通过下述命令开启事务、确认事务或回滚事务。
BEGIN # 开始一个事务
COMMIT # 确认事务
ROLLBACK # 回滚事务
5.1.5 Python 连接 MySQL 数据库
Python 中可以使用 pymysql 库来连接 MySQL 数据库,通过 pip 命令安装即可:
pip install pymysql
MySQL 服务默认的端口号是 3306,可以输入下述命令查看端口号:
show global variables like 'port'
执行结果如下:
mysql> show global variables like 'port';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| port | 3306 |
+---------------+-------+
1 row in set, 1 warning (0.01 sec)
- 连接数据库
import pymysql
db = pymysql.connect(host='localhost', user='root', password='root', port=3306)
cursor = db.cursor()
cursor.execute('SELECT VERSION()')
# 获取第一条数据
data = cursor.fetchone()
print('数据库版本号:', data)
db.close()
代码执行结果如下:
数据库版本号:('8.0.12',)
- 新建数据库
代码示例如下(判断数据库是否存在,不存在的话则新建,同时设置编码类型):
cursor.execute("Create Database If Not Exists test Character Set UTF8MB4")
- 新建数据库表
代码示例如下(判断表是否存在,不存在的话则新建):
c.execute("CREATE TABLE IF Not Exists person(id INT AUTO_INCREMENT PRIMARY
KEY,name VARCHAR(30) NOT NULL DEFAULT '',age INT,sex CHAR(2))")
代码执行后,可以在命令行输入 desc 命令查看表结构,结果如下所示
mysql> use test;
Database changed
mysql> desc person;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(30) | NO | | | |
| age | int(11) | YES | | NULL | |
| sex | char(2) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
也可以用 show tables;查看当前数据库中的表,结果如下所示:
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| person |
+----------------+
1 row in set (0.00 sec)
- 插入数据
代码示例如下:
# 插入数据
def insert_data(c, name, age, sex):
c.execute('INSERT INTO person (name, age, sex) VALUES (%s, %s, %s)', (name, age,
sex))
# 调用处
insert_data(cursor, '小明', '8', '男')
insert_data(cursor, '小红', '14', '女')
insert_data(cursor, '小白', '4', '男')
insert_data(cursor, '小宝', '6', '男')
insert_data(cursor, '小莉', '16', '女')
db.commit()
代码执行完成后,输入 select * from person;查看当前的数据情况,结果如下:
mysql> select * from person;
+----+--------+------+------+
| id | name | age | sex |
+----+--------+------+------+
| 1 | 小明 | 8 | 男 |
| 2 | 小红 | 14 | 女 |
| 3 | 小白 | 4 | 男 |
| 4 | 小宝 | 6 | 男 |
| 5 | 小莉 | 16 | 女 |
+----+--------+------+------+
5 rows in set (0.00 sec)
对于增加、删除、修改这类会改变数据库中数据的操作,都需要调用 commit()方法才会生效。另外,还需要在外层添加一层异常处理,如果某个操作执行失败了,则调用 rollback()函数执行数据回滚,相当于什么都没发生。这就涉及事务了,事务可以保持数据的一致性,比如插入多条数据,要么全部插入,要么一条都不插入,不会发生只插入一部分数据的情况。所以可以提炼出增加、删除、修改这类操作的一个模板代码:
try:
c.execute(sql)
db.commit()
except:
db.rollback()
上述这种拼接 SQL 字符串的方式显得有些烦琐,如果有新字段,则又要修改 SQL 语
句。这时可以传入一个动态变化的字典,而不用频繁地修改 SQL 语句,上述操作代码可以
变成这样:
def insert_data(cursor, table, data_dict):
"""
将数据插入到指定的数据库表中。
:param cursor: 数据库游标对象
:param table: 要插入数据的表名
:param data_dict: 一个字典,包含要插入的列名和值
"""
try:
keys = ', '.join(data_dict.keys())
placeholders = ', '.join(['%s'] * len(data_dict))
sql = f'INSERT INTO {table} ({keys}) VALUES ({placeholders})'
cursor.execute(sql, tuple(data_dict.values()))
db.commit()
except Exception as e:
print(f"Error: {e}")
db.rollback()
# 示例调用
data = {
'name': '大黄',
'age': 17, # 注意:如果age是整数类型的列,这里应不加引号
'sex': '男',
}
insert_data(cursor, 'person', data)
代码执行完后,数据库中的数据如下:
mysql> select * from person;
+----+--------+------+------+
| id | name | age | sex |
+----+--------+------+------+
| 1 | 小明 | 8 | 男 |
| 2 | 小红 | 14 | 女 |
| 3 | 小白 | 4 | 男 |
| 4 | 小宝 | 6 | 男 |
| 5 | 小莉 | 16 | 女 |
| 6 | 大黄 | 17 | 男 |
+----+--------+------+------+
6 rows in set (0.00 sec)
- 删除数据
代码示例如下:
def delete_data(cursor, table, condition):
"""
从指定的数据库表中删除符合条件的数据。
:param cursor: 数据库游标对象
:param table: 要删除数据的表名
:param condition: 删除条件
"""
try:
sql = f'DELETE FROM {table} WHERE {condition}'
cursor.execute(sql)
db.commit()
except Exception as e:
print(f"Error: {e}")
db.rollback()
# 示例调用
delete_data(cursor, 'person', 'age < 10')
代码执行完成后,数据库中的数据如下:
mysql> select * from person;
+----+--------+------+------+
| id | name | age | sex |
+----+--------+------+------+
| 2 | 小红 | 14 | 女 |
| 5 | 小莉 | 16 | 女 |
| 6 | 大黄 | 17 | 男 |
+----+--------+------+------+
3 rows in set (0.00 sec)
- 修改数据
代码示例如下:
def update_data(cursor, table, update_dict, condition):
"""
更新指定数据库表中符合条件的数据。
:param cursor: 数据库游标对象
:param table: 要更新数据的表名
:param update_dict: 包含要更新的列名和值的字典
:param condition: 更新条件
"""
try:
set_values = ', '.join([f"{key} = %s" for key in update_dict.keys()])
sql = f'UPDATE {table} SET {set_values} WHERE {condition}'
cursor.execute(sql, tuple(update_dict.values()))
db.commit()
except Exception as e:
print(f"Error: {e}")
db.rollback()
# 示例调用
update_data(cursor, 'person', {'age': 18, 'name': '小明'}, 'id = 1')
代码执行结果如下:
共有 2 行数据
(5, '小莉', 16, '女')
(6, '大黄', 17, '男')
除此之外,还可以用 fetchall()函数一次性获得全部数据(元组),但是如果数据量很大,占用的开销就会很大,建议还是使用上述这种循环的方式一条条获取数据。
5.1.6 MySQL 特殊符号和表情问题
在旧版的 MySQL 中,对于特殊符号和表情,在插入数据的时候会报错:Incorrect string
value: '\xF0\x9F...' for column 'XXX' at row 1。原因:UTF—8 编码有可能是 2 字节、3 字节、4 字节。Emoji 表情或某些特殊字符是 4 字节,而 MySQL 的 utf8 编码最多 3 字节,所以数据插不进去。
MySQL 在 5.5.3 版本之后增加了 utf8mb4 编码,专门用来兼容 4 字节的 unicode,理论上将字符集修改为 utf8mb4 不会对已有的 utf8 编码读取产生任何影响。新版 MySQL 在创建数据库的时候就会提示用户设置为 utf8mb4 编码,如果在存储特殊字符和表情的时候出现了上述问题,可以按照如下流程来解决。
(1)打开终端,输入 locale my.cnf 定位到文件位置(Windows 下是 my.ini),如下所示:
→~locate my .cnf/etc /mysql/my . cnf/etc /mysql/my .cnf~
(2)在 vim etc/mysql/my.cnf 中追加下述内容并保存:
[mysqld]
character-set-server=utf8mb4
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
(3)重启 MySQL 服务器。
(4)进入 MySQL,然后输入 show variables like '%character%';确认设置是否生效,如下所示:
mysql> show variables like '%character%';
+--------------------------+-----------------------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | C:\Coding\mysql-8.0.12-winx64\share\charsets\ |
+--------------------------+-----------------------------------------------+
8 rows in set, 1 warning (0.03 sec)
(5)更改数据库、表、列编码。
ALTER DATABASE 数 据库名 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_
unicode_ci;
ALTER TABLE 表名CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
ALTER TABLE 表名CHANGE 列名VARCHAR(191) CHARACTER SET utf8mb4 COLLATE
utf8mb4_bin;
至此问题就解决了,此时打开数据库表可以看到对应记录已存入,只是显示为问号,读取数据并显示到支持 Emoji 表情的设备页面上就可以了,比如手机。
5.1.7 实战:抓取某技术网站数据
爬取豆瓣音乐排行榜写入MySQL数据库
- 创建数据库
create database douban_music
- 创建表
CREATE TABLE IF NOT EXISTS `music_top250` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`song_name` varchar(255) NOT NULL,
`singer` varchar(255) NOT NULL,
`release_time` varchar(100) NOT NULL,
`rating` varchar(10) NOT NULL,
`number_of_ratings` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
表设计好了,代码如下:
import requests
from lxml import etree
import re
import pymysql
# 连接到MySQL数据库
db = pymysql.connect(
host="localhost", # 数据库主机地址
user="root", # 数据库用户名
password="root", # 数据库密码
database="douban_music" # 数据库名称
)
cursor = db.cursor()
# 循环遍历每一页
for page in range(0, 250, 25):
url = f'https://music.douban.com/top250?start={page}'
headers = {
'Host': 'music.douban.com',
'User-Agent': 'Mozilla/5.0'
}
response = requests.get(url, headers=headers)
html = etree.HTML(response.text)
song_names = html.xpath('//td[2]/div/a/text()')
singer_infos = html.xpath('//td[2]/div/p/text()')
rating_nums = html.xpath('//td[2]/div/div/span[2]/text()')
rating_peoples = html.xpath('//td[2]/div/div/span[3]/text()')
for song_name, singer_info, rating_num, rating_people in zip(song_names, singer_infos, rating_nums, rating_peoples):
song_name = song_name.strip()
singer_name = singer_info.split('/')[0].strip()
song_time = singer_info.split('/')[1].strip()
rating_num = rating_num.strip()
number_of_ratings = re.search(r'\d+', rating_people).group() if re.search(r'\d+', rating_people) else None
# 构造SQL语句并执行
sql = "INSERT INTO music_top250 (song_name, singer, release_time, rating, number_of_ratings) VALUES (%s, %s, %s, %s, %s)"
val = (song_name, singer_name, song_time, rating_num, number_of_ratings)
try:
cursor.execute(sql, val)
db.commit() # 提交事务
except Exception as e:
print(f"Error: {e}")
db.rollback() # 发生错误时回滚
# 关闭数据库连接
db.close()
查询该数据库内容:
import pymysql
def query_database():
# 数据库连接配置
db_config = {
"host": "localhost",
"user": "root",
"password": "root",
"database": "douban_music"
}
try:
# 连接数据库
db = pymysql.connect(**db_config)
cursor = db.cursor()
# SQL 查询语句
sql = "SELECT * FROM music_top250"
# 执行SQL查询
cursor.execute(sql)
# 获取查询结果
results = cursor.fetchall()
# 遍历结果
for row in results:
id = row[0]
song_name = row[1]
singer = row[2]
release_time = row[3]
rating = row[4]
number_of_ratings = row[5]
# 打印每行的信息
print(f"ID: {id}, Song Name: {song_name}, Singer: {singer}, Release Time: {release_time}, Rating: {rating}, Number of Ratings: {number_of_ratings}")
except pymysql.Error as e:
print(f"Error: {e}")
finally:
# 关闭数据库连接
if db:
db.close()
# 调用函数执行查询
query_database()
查询结果如下:
数据库版本号: ('5.7.43-log',)
ID: 1, Song Name: We Sing. We Dance. We Steal Things., Singer: Jason Mraz, Release Time: 2008-05-13, Rating: 9.1, Number of Ratings: 116284
ID: 2, Song Name: Viva La Vida, Singer: Coldplay, Release Time: 2008-06-17, Rating: 9.0, Number of Ratings: 118742
ID: 3, Song Name: , Singer: 陈绮贞, Release Time: 2005-09-23, Rating: 9.0, Number of Ratings: 90514
ID: 4, Song Name: 华丽的冒险, Singer: 周杰伦, Release Time: 2001-09-14, Rating: 9.5, Number of Ratings: 178918
ID: 5, Song Name: , Singer: 五月天, Release Time: 2008-10-23, Rating: 9.0, Number of Ratings: 95827
ID: 6, Song Name: 范特西, Singer: 孙燕姿, Release Time: 2011-03-08, Rating: 8.7, Number of Ratings: 82260
ID: 7, Song Name: , Singer: Lenka, Release Time: 2008-09-23, Rating: 8.6, Number of Ratings: 83470
ID: 8, Song Name: 後。青春期的詩, Singer: 王若琳, Release Time: 2008-01-11, Rating: 8.8, Number of Ratings: 76001
ID: 9, Song Name: , Singer: 陈绮贞, Release Time: 2004-02-02, Rating: 9.1, Number of Ratings: 99640
ID: 10, Song Name: 是时候, Singer: 陈绮贞, Release Time: 2009-01-22, Rating: 8.7, Number of Ratings: 76721
5.2 数据库可视化工具
数据库可视化工具是指那些帮助用户通过图形界面来管理、查询和展示数据库中数据的软件应用。这类工具大大降低了数据库管理的复杂性,使得即使是非技术用户也能轻松地进行数据操作和分析。下面介绍几种常见的数据库可视化工具:
- MySQL Workbench
适用数据库:MySQL
主要特点:
- 官方出品,支持数据库设计、开发、管理和维护。
- 提供数据建模、SQL开发和服务器配置等功能。
- 支持视觉化查询构建器。
- phpMyAdmin
适用数据库:MySQL
主要特点:
- 基于Web的工具,方便在浏览器中使用。
- 支持各种操作,如数据库管理、表管理、索引管理等。
- 提供导入导出数据的功能。
- PgAdmin
适用数据库:PostgreSQL
主要特点:
- 官方图形界面工具,支持所有PostgreSQL版本。
- 可以运行在Windows、Linux和Mac OS X平台。
- 提供查询工具、服务器监控等高级功能。
- Oracle SQL Developer
适用数据库:Oracle
主要特点:
- 官方出品,免费提供给Oracle数据库用户。
- 支持SQL语句编辑、数据库对象管理、性能监控等。
- 提供数据模型设计工具。
- Microsoft SQL Server Management Studio (SSMS)
适用数据库:SQL Server
主要特点:
- 微软官方工具,集成环境用于管理SQL Server。
- 提供数据库配置、管理、开发等全方位功能。
- 支持T-SQL脚本编辑、执行计划分析等。
- DBeaver
适用数据库:多数据库(MySQL, PostgreSQL, SQLite, Oracle, DB2, SQL Server等)
主要特点:
- 开源跨平台数据库工具。
- 支持几乎所有数据库,通过JDBC驱动连接。
- 功能丰富,包括数据管理、SQL编辑器、数据导出导入等。
- Navicat
适用数据库:多数据库(MySQL, PostgreSQL, Oracle, SQLite, SQL Server等)
主要特点:
- 支持多种数据库,界面友好。
- 提供数据库管理、开发、维护的全套解决方案。
- 支持数据迁移、同步、备份等高级功能。-
DataGrip
适用数据库:多数据库(包括但不限于 MySQL, PostgreSQL, SQLite, Oracle, SQL Server, DB2, Sybase, H2 等)
主要特点:
- 全面的数据库支持
支持广泛的数据库系统,几乎涵盖了市场上所有主流数据库。
通过JDBC驱动连接数据库,方便灵活。 - 智能代码编辑
智能代码补全功能,根据上下文提供精准的补全建议。
实时代码分析和错误提示,提升代码质量。
强大的SQL重构工具,简化数据库结构调整过程。 - 高效的数据导航和搜索
快速导航到任意数据库对象,如表、视图、存储过程等。
支持全局搜索,快速定位所需数据或对象。 - 灵活的数据编辑
直观的数据编辑器,支持直接在表格中编辑数据。
执行SQL查询,并以多种格式展示结果,支持结果导出。 - 版本控制集成
与Git, SVN, Mercurial等版本控制系统无缝集成。
方便管理和追踪数据库变更。 - 强大的数据库管理工具
提供数据库对象管理、SQL执行计划分析、数据库连接管理等功能。
支持数据库用户权限管理和数据库维护任务。 - 图形化数据库设计
提供ER图(实体关系图)功能,帮助可视化数据库结构设计。 - 多环境支持
支持在一个界面中同时管理多个数据库实例。
方便进行跨数据库或跨环境的操作。
5.3 Redis 数据库
Redis 是一个使用 ANSI C 编写的开源、支持网络、基于内存、可选持久化 Key-Value的非关系型存储数据库。它具有下述特性:
- 速度快(数据存在内存中,C 语言编写,单线程)
- 持久化(对数据的更新将异步保存到磁盘上)
- 支持多种数据结构(支持 8 种数据类型,常用 SString、Hash、List、Set 和 SortSet)
- 支持多种编程语言(Python、PHP、Java、Golang 等)
- 功能丰富(发布订阅、lua 脚本、事务、pipeline)
- 简单(代码短小精悍,不依赖外部库,单线程模型)
- 主从复制
- 高可用、分布式
以上就是关于 Redis 的简介,你可以把它简单地理解成一个通过键值对保存数据的非
关系型内存数据库。下面是与 Redis 有关的一些链接。
- 官网:http://redis.io/
- 官方文档:https://redis.io/documentation
- 官方下载:https://redis.io/download
- 中文官网:http://www.redis.cn/
- 中文教程(非官方):http://www.runoob.com/redis/redis-tutorial.html
5.3.1 安装 Redis
windows环境下
https://blog.csdn.net/qq_45609369/article/details/131449605
mac环境下
https://www.cnblogs.com/hanease/p/15962271.html
ubantu 环境下
https://blog.csdn.net/heyl163_/article/details/132981470
5.3.2 redis-py 库的安装
通过 pip 命令安装即可,如下所示:
pip install redis
5.3.3 redis-py 基本操作示例
- 连接 Redis 数据库
redis-py 库提供了 Redis 类和 StrictRedis 类来实现 Redis 的相关操作,前者是后者的子类,主要用于兼容旧版本库里的几个方法,官方推荐使用 StrictRedis 类。
import redis
(1)普通连接。
r = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
(2)连接池(建议使用这种方式)。
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, password='Zpj12345')
r = redis.StrictRedis(connection_pool=pool)
(3)管道。
redis-py 在默认情况下,每次都会进行连接池的连接和断开。若想一次执行多条命令,
进行事务性操作,就要使用管道。
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.StrictRedis(connection_pool=pool)
pipe = r.pipeline(transaction=True)
# 执行多条命令
pipe.execute()
- 通用操作
r.delete('name') # 根据键删除Redis中的任意数据类型
r.exists('name') # 检测Redis的键是否存在
r.keys(pattern='*') # 根据*、?等通配符匹配获取Redis的键
r.expire('name' ,time=3000) # 为某个键设置超时时间
r.rename('name', 'name1') # 重命名键
r.move('name', 'db1') # 将Redis的某个值移动到指定的db下
r.randomkey() # 随机获取一个Redis的键(不删除)
r.type('name') # 获取键对应值的类型
r.dbsize() # 获得当前数据库中键的数目
r.ttl('name') # 获得键的过期时间
r.flushdb() # 删除当前选择数据库中所有的键
r.flushall() # 删除所有数据库中所有的键
- String 操作
设置键值对,默认不存在则创建,存在则修改:
set(name, value, ex=None, px=None, nx=False, xx=False)
- ex 为过期时间(秒)。
- px 为过期时间(毫秒)。
- nx 如果设置为 True,则只有 name 不存在时,当前 set 操作才执行,同 setnx(name,
- value)。
- xx 如果设置为 True,则只有 name 存在时,当前 set 操作才执行。
r.set(name, value) # 设置值
r.setnx(name,value) # 如果name键不存在,把这个键对应的值设置为value
r.setex(name, value, time) # 设置值,并指定此键值的有效期
r.setrange(name, offset, value) # 修改字符串内容,从指定字符串索引开始向后替换
r.mset({"name3":'xxx', "name4":'xxx'}) # 批量设置值
r.msetnx({"name3":'xxx', "name4":'xxx'}) # 键都不存在时才批量赋值
r.get(name) # 获取值
r.getset(name, new_value) # 将name赋值为new_value,并返回上次的值old_value
r.mget(['name1','name2']) # 返回多个键对应的值
r.getrange(key, start, end) # 返回键为name的值的字符串,截取索引为start到end的字符
r.strlen(name) # 返回name对应值的字节长度(一个汉字3字节)
r.append(name,value) # 在键为name的值后追加value
r.incr(name,amount) # 字符串转化为整型,再自增属性name对应的值,当属性name不存在时,
# 则创建name=amount,否则自增,amount为自增数(整数)
r.decr(name,amount) # 自减name对应的值,当name不存在时,则创建name=amount,
# 否则自减,amount为自减数(整数)
r.substr(name,start, end) # 返回键为name的值的字符串,截取索引为start到end的字符
- List 操作
(1)增改操作。
r.lpush(name,1,2,3) # 在name对应的list中添加元素,每个新的元素都添加到列表的最左边,不存在列表则创建列表,保存在表中的顺序为3,2,1
r.rpush(name,values) # 同lpush,但每个新的元素都添加到列表的最右边
r.lpushx(name,value) # 在name对应的list中添加元素,只有name已经存在时,值添加到列
# 表的最左边
r.rpushx(name,value) # 在name对应的list中添加元素,只有name已经存在时,值添加到列
# 表的最右边
r.linsert(name,value,"2","YY") # 在列表内找到第一个元素2,在它前面插入YY
r.lset(name,0,"YYY") # 对list中的某一个索引位置重新赋值
r.lpop(name) # 移除列表左侧的第一个元素,返回值则是第一个元素
r.rpoplpush('list1_name', 'list2_name') # 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
(2)查询操作。
r.llen(name) # name对应的list元素的个数
r.lindex(name,1) # 根据索引获取列表内元素
r.lrange(name,0,-1) # 分片获取元素
(3)删除操作。
r.lrem(name,count,value) # 删除多个列表中特定键值的元素,count为删除个数,value为指
定的键值
r.ltrim(name, start, end) # 截取name的列表,只保留索引为start到end的内容
r.lpop(name) # 返回并删除键为name的列表的首个元素
r.rpop(name) # 返回并删除键为name的列表的结尾元素
r.blpop([name1,name2],timeout=0) # 将多个列表进行排列,从左到右移除各个列表内的元素timeout为超时时间,获取完所有列表的元素之后,阻塞等待列表内有数据的时间(秒),0表示永远阻塞
r.brpop(keys, timeout) # 同blpop,将多个列表排列,从右向左移除各个列表内的元素
- Set 操作
(1)增改操作。
r.sadd(name,"aa","bb") # 向键为name的集合中添加元素
r.smove(name1, name2, value) # 将某个元素从一个集合中移动到另外一个集合
r.spop(name) # 从集合的右侧移除一个元素,并将其返回
(2)查询操作。
r.smembers(name) # 获取name对应的集合的所有成员
r.scard(name) # 获取name对应的集合中的元素个数
r.sdiff([name1,name2]) # 返回给定键的集合的差集
r.sdiffstore(dest, keys, *args) # 求差集并把结果保存到dest集合中
r.sinter([name1,name2]) # 返回多个name对应集合的交集
r.sinterstore(dest, keys, *args) # 求交集并把结果保存到dest集合中
r.sunion([name1,name2]) # 返回多个name对应的集合的并集
r.sunionstore(dest,keys, *args) # 求并集并把结果保存到dest集合中
r.srandmember(name,num) # 从name对应的集合中随机获取numbers个元素
r.sismember(name, value) # 检查value是不是name对应的集合内的元素
(3)删除操作。
r.srem(name,[value1, value2]) # 删除name对应的集合中的某些值
- 有序集合操作(SortedSet)
在集合的基础上,为每个元素排序,元素的排序需要根据另一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即值和分数,分数专门用于排序。
(1)增改操作。
r.zadd(name, member, score) # 在name对应的有序集合中添加元素
(2)查询操作。
r.zcard(name) # 获取有序集合内元素的数量
r.zcount(name,min,max) # 获取有序集合中分数在[min,max]之间的个数
r.zincrby(name,"a1",amount=2) # 自增zset_name对应的有序集合里a1对应的分数
# 返回键为name的zset中score在给定区间的元素
r.zrangebyscore(name, min, max, start=None, num, withscores=False)
# 返回键为name的zset(按score从大到小排序)中index从start到end的所有元素
r.zrevrange(name, start, end, withscores=False)
r.zscore(name,value) # 获取name对应有序集合中value对应的分数
r.zrank(name, value) # 获取value值在name对应的有序集合中的排行位置(从0开始)
r.zrevrank(name, value) # 从大到小排序
(3)删除操作。
r.zrem(name,value,value) # 删除name对应的有序集合中值是value的成员
r.zremrangebyrank(name, 3, 5) # 根据排行范围删除
r.zremrangebyscore(name, 3, 5) # 根据分数范围删除
- 散列表操作(键值对)
(1)增改操作。
r.hset(name, key, value) # 在name对应的hash中设置一个键值对(不存在则创建,否则修改)
r.hmset(name,mapping) # 在name对应的hash中批量设置键值对,mapping为字典
r.hincrby(name,key,amount=2) # 自增hash中key对应的值,不存在则创建key=amount(amount为整数)
r.hincrbyfloat(name,key,amount=1.0) # 自增hash中key对应的值,不存在则创建key=amount
(amount为浮点数)
(2)查询操作。
r.hget(name,key) # 在name对应的hash中根据key获取value
r.hmget(name,keys, *args) # 在name对应的hash中获取多个key的值
r.hgetall(name) # 获取name对应hash的所有键值
r.hlen(name) # 获取hash中键值对的个数
r.hkeys(name) # 获取hash中所有的key的值
r.hvals(name) # 获取hash中所有的value的值
r.hexists(name,key) # 检查name对应的hash是否存在当前传入的key
(3)删除操作。
r.hdel(name,key) # 删除指定name对应的key所在的键值对
5.3.4 实战:爬取视频弹幕并保存到 Redis
本节我们来编写爬虫爬取某个视频的所有弹幕,并保存到 Redis 数据库中。
爬取流程解析:
(1)该视频站点的弹幕保存在 XML 文件中,每个视频有其对应的 cid 和 aid,aid 是视
频的编号,cid 是弹幕 XML 文件的编号。
from redis import *
import requests as r
from bs4 import BeautifulSoup
import redis
# 配置Redis连接
redis_host = 'localhost' # Redis服务器地址
redis_port = 6379 # Redis服务器端口
redis_db = 0 # 使用的Redis数据库编号
# 创建Redis连接
db = redis.Redis(host=redis_host, port=redis_port, db=redis_db, decode_responses=True)
cid = '1429275928'
xml_base_url = 'http://comment.bilibili.com/'
def analysis_d(cid):
url = xml_base_url + cid + '.xml'
resp = r.get(url)
resp.encoding = 'utf-8'
bs = BeautifulSoup(resp.text, 'lxml-xml')
d_s = bs.find_all('d')
# 定义一个列表来存储所有弹幕,方便一次性写入Redis
danmus = []
for d in d_s:
danmus.append(d.text)
# 将弹幕列表写入Redis,这里使用了列表数据结构,key为"danmus:{cid}"
if danmus: # 如果danmus列表不为空
db.rpush(f"danmus:{cid}", *danmus) # 使用rpush将所有弹幕添加到列表中
print(f"Successfully added {len(danmus)} danmus to Redis.")
else:
print("No danmus found.")
analysis_d(cid)
运行结果如下:
Successfully added 1800 danmus to Redis.
查询结果:
import redis
# 配置Redis连接
redis_host = 'localhost' # Redis服务器地址
redis_port = 6379 # Redis服务器端口
redis_db = 0 # 使用的Redis数据库编号
# 创建Redis连接
db = redis.Redis(host=redis_host, port=redis_port, db=redis_db, decode_responses=True)
cid = '1429275928' # 使用相同的CID或根据需要更改
key = f"danmus:{cid}"
# 获取列表中的所有弹幕
danmus = db.lrange(key, 0, -1)
# 打印弹幕列表
for danmu in danmus:
print(danmu)
keys = db.keys("danmus:*")
for key in keys:
danmus = db.lrange(key, 0, -1)
print(f"{key} has {len(danmus)} danmus:")
for danmu in danmus:
print(danmu)
print("-" * 20) # 打印分隔线以区分不同的键
运行结果如下:+
有种看我爱发明的赶脚
怎用我的姓
来晚了!
好想法!
手工耿:?
...
5.4 MongoDB 数据库
5.4.1 安装 MongoDB
Windows 环境下
https://blog.csdn.net/weixin_44058223/article/details/125311256
mac环境下
https://blog.csdn.net/weixin_42205101/article/details/124610067
ubantu环境下
https://blog.csdn.net/weixin_52799373/article/details/130839883
5.4.2 安装 PyMongo 库
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pymongo
5.4.3 PyMongo 基本操作示例
PyMongo 库中提供的连接数据库的方式有两种,官方建议使用 MongoClient 方式,以及Connection 方式。
- 连接 MongoDB 数据库
连接 MongoDB 数据库,默认没有密码,如果设置了密码,要调用 db.auth("用户名","密码")。
conn = pymongo.MongoClient(host='localhost', port=27017)
或者采用 MongoDB 连接字符串的形式也可以:
conn = pymongo.MongoClient('mongodb://localhost:27017')
- 选择数据库
选择数据库,也可以使用 conn['test']这一方式,等价于:
db = conn.test
- 选择 collection(集合)
选择 collection(集合),类似于关系型数据库中的表:
collection = db.user
print(collection)
完整代码
import pymongo
conn = pymongo.MongoClient(host='localhost', port=27017)
db = conn.test
collection = db.user
print(collection)
代码执行结果如下:
Collection(Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'test'), 'user')
- 创建数据库
编写代码来创建一个数据库:
db = conn['test_db']
- 创建 collection
collection = db['test_collection']
- 插入数据(一条)
有一点要注意,MongoDB 是有惰性的,在插入第一条数据之前,不会真生成数据库和collection。
db = conn['test_db']
collection = db['test_collection']
dic = {'id': '1', 'name': 'Jay'}
# 文档在MongoDB中的存储格式是JSON,在PyMongo中一般传入字典
collection.insert_one(dic)
- 插入数据(多条)
db = conn.test_db
collection = db.test_collection
data_list = [{'id': '2', 'name': 'Tom'},{'id': '3', 'name': 'Jack'}]
collection.insert_many(data_list)
代码执行结果如下:
{"_id":"5b7a98a6b1ad712f001b0f6e","id":"1","name":"Jay"}
{"_id":"5b7a9a69b1ad712a08671436","id":"2","name":"Tom"}
{"_id":"5b7a9a69b1ad712a08671437","id":"3","name":"Jacky"}
- 查询数据
# 查找一条
print(collection.find_one({'name': 'Tom'}))
# 查找多条
data_list = [{'id': '4', 'name': 'Mary'},{'id': '4', 'name': 'Lucy'}]
collection.insert_many(data_list)
results = collection.find({'id':'4'})
for result in results:
print(result)
代码执行结果如下:
{'_id': ObjectId('5b7a9a69b1ad712a08671436'), 'id': '2', 'name': 'Tom'}
{'_id': ObjectId('5b7aa083b1ad7111c046ef88'), 'id': '4', 'name': 'Mary'}
{'_id': ObjectId('5b7aa083b1ad7111c046ef89'), 'id': '4', 'name': 'Lucy'}
另外,有时我们需要做一些数字比较,比如查找 id 小于 4 的所有数据,可以采用比较符号。
除此之外,还支持正则匹配查询,使用$regex 来执行,比如查询 J 开头的名字,代码示例如下:
# 正则匹配
for result in collection.find({'name':{'$regex':'^J.*'}}):
print(result)
代码执行结果如下:
{'_id': ObjectId('5b7a98a6b1ad712f001b0f6e'), 'id': '1', 'name': 'Jay'}
{'_id': ObjectId('5b7a9a69b1ad712a08671437'), 'id': '3', 'name': 'Jack'}
除了正则表达式,还有其它功能符号。
9. 修改数据
(1)方法一:需要整条记录参与。
person = collection.find_one({'name':'Jack'})
person['name'] = 'Jacky'
collection.update({'name':'Jack'}, person)
代码执行结果如下:
{"_id":"5b7a9a69b1ad712a08671437","id":"3","name":"Jacky"}
执行后,有一个警告:
DeprecationWarning: update is deprecated. Use replace_one, update_one or update_many instead.
官方并不推荐我们使用 update()函数去更新数据,而建议使用 update_one()和update_many()函数,它们的第二个参数需要使用$类型操作符作为字典的键名。
(2)方法二:部分修改字段内容的方式。
result = collection.update_one({'name': 'Tom'}, {'$set': {"name": "Tony"}})
print(result)
print("匹配的数据条数:",result.matched_count, "受影响的数据条数:",result.modified_count)
代码执行结果如下:
<pymongo.results.UpdateResult object at 0x082456C0>
匹配的数据条数: 1 受影响的数据条数: 1
- 删除数据
可以调用 remove()方法移除满足条件的数据,推荐使用 delete_one()和 delete_many()函
数进行删除,代码示例如下:
result= collection.delete_many({'id':{'$lte':3}})
print("删除的数据条数:",result.deleted_count)
代码执行结果如下:
删除的数据条数:3
删除后剩下的数据:
{"_id":"5b7aa083b1ad7111c046ef88","id":4,"name":"Mary"}
{"_id":"5b7aa083b1ad7111c046ef89","id":4,"name":"Lucy"}
- 计数
print("数据库中有%d条记录。" % collection.find().count())
代码执行结果如下:
数据库中有2条记录。
- 排序
data_list = [{'id': 2, 'name': 'Tom'},{'id': 3, 'name': 'Jack'},{'id': 5, 'name': 'Daisy'}]
collection.insert_many(data_list)
# 降序排列,升序可以传入pymongo.ASCENDING
results = collection.find().sort('id', pymongo.DESCENDING)
for result in results:
print(result)
代码执行结果如下:
{'_id': ObjectId('5b7ab69bb1ad7125c03696d8'), 'id': 5, 'name': 'Daisy'}
{'_id': ObjectId('5b7aa083b1ad7111c046ef88'), 'id': 4, 'name': 'Mary'}
{'_id': ObjectId('5b7aa083b1ad7111c046ef89'), 'id': 4, 'name': 'Lucy'}
{'_id': ObjectId('5b7ab6c4b1ad7124d0dd8a72'), 'id': 3, 'name': 'Jack'}
{'_id': ObjectId('5b7ab69bb1ad7125c03696d6'), 'id': 2, 'name': 'Tom'}
- 偏移
有时可能只想获取某几个元素,可以利用skip()函数偏移几个位置,代码示例如下:
# 跳过第一个
results = collection.find().sort('id', pymongo.ASCENDING).skip(1)
for result in results:
print(result)
代码执行结果如下:
{'_id': ObjectId('5b7ab69bb1ad7125c03696d7'), 'id': 3, 'name': 'Jack'}
{'_id': ObjectId('5b7aa083b1ad7111c046ef88'), 'id': 4, 'name': 'Mary'}
{'_id': ObjectId('5b7aa083b1ad7111c046ef89'), 'id': 4, 'name': 'Lucy'}
{'_id': ObjectId('5b7ab69bb1ad7125c03696d8'), 'id': 5, 'name': 'Daisy'}
此外,还可以使用 limit()函数限制返回结果数。
5.4.4 实战:爬取某电商网站并保存到 MongoDB
import requests
import parsel
import time
from pymongo import MongoClient
# MongoDB 设置
client = MongoClient('localhost', 27017) # 连接到本地MongoDB
db = client['dangdang'] # 选择数据库,如果不存在,将会自动创建
collection = db['bestsellers'] # 选择集合,如果不存在,将会自动创建
def fetch_data(page):
url = f'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-24hours-0-0-1-{page}'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'
}
response = requests.get(url=url, headers=headers)
return response.text
def parse_data(html):
selector = parsel.Selector(html)
lis = selector.css('ul.bang_list li')
for li in lis:
data = {
'书名': li.css('.name a::attr(title)').get(),
'评论数': li.css('.star a::text').get().replace('条评论', ''),
'推荐量': li.css('.star .tuijian::text').get().replace('推荐', ''),
'作者': li.css('.publisher_info a:nth-child(1)::attr(title)').get(),
'出版社': li.css('div:nth-child(6) a::text').get(),
'售价': li.css('.price .price_n::text').get(),
'原价': li.css('.price .price_r::text').get(),
'折扣': li.css('.price .price_s::text').get(),
'电子书价格': li.css('.price .price_e .price_n::text').get(),
'详情页': li.css('.name a::attr(href)').get(),
}
save_to_mongo(data)
def save_to_mongo(data):
if collection.insert_one(data):
print('Saved to MongoDB:', data['书名'])
def main():
for page in range(1, 26):
print(f'正在爬取第{page}页的数据内容')
html = fetch_data(page)
parse_data(html)
time.sleep(1.5)
if __name__ == "__main__":
main()
运行结果如下:
正在爬取第1页的数据内容
Saved to MongoDB: 德米安 : 彷徨少年时(告别彷徨,坚定地做你自己,诺贝尔文学奖得主黑塞纪念碑式名作!德语名家德文原版翻译!)
Saved to MongoDB: 小王子(原版精美插图,傅雷翻译出版奖得主郑克鲁法文直译。揭秘作者传奇人生,展示与《小王子》有关的钱币、纪念章、邮票等)
Saved to MongoDB: 昆虫记(语文教材“名著导读”经典・八年级上。内容更丰富版本:33张彩色照片图+系统分类目录+昆虫谱系表+大量详注+颠覆性纠错,帮你构筑系统的知识体系)
查看前五条数据
from pymongo import MongoClient
# MongoDB 设置
client = MongoClient('localhost', 27017) # 连接到本地MongoDB
db = client['dangdang'] # 连接到数据库
collection = db['bestsellers'] # 连接到集合
def fetch_top_five_from_mongo():
documents = collection.find().limit(5) # 查询前五个文档
# documents = collection.find() # 查询所有文档
for document in documents:
print(document)
if __name__ == "__main__":
fetch_top_five_from_mongo()
运行结果如下:
{'_id': ObjectId('65c388caa1635fe262c12340'), '书名': '我们生活在巨大的差距里(余华十年杂文集)', '评论数': '73015', '推荐量': '99.9%', '作者': '余华作品', '出版社': '北京十月文艺出版社', '售价': '¥17.50', '原价': '¥35.00', '折扣': '5.0折', '电子书价格': '¥21.65', '详情页': 'http://product.dangdang.com/23636135.html'}
{'_id': ObjectId('65c388caa1635fe262c12341'), '书名': '额尔古纳河右岸(茅盾文学奖获奖作品全集28)', '评论数': '685694', '推荐量': '100%', '作者': '迟子建', '出版社': '人民文学出版社', '售价': '¥16.00', '原价': '¥32.00', '折扣': '5.0折', '电子书价格': None, '详情页': 'http://product.dangdang.com/27878108.html'}
{'_id': ObjectId('65c388caa1635fe262c12342'), '书名': '我与地坛(纪念版)(2024年百班千人寒假书单 九年级推荐阅读)', '评论数': '1086587', '推荐量': '100%', '作者': '史铁生', '出版社': '人民文学出版社', '售价': '¥16.20', '原价': '¥29.00', '折扣': '5.6折', '电子书价格': '¥9.99', '详情页': 'http://product.dangdang.com/21055821.html'}
{'_id': ObjectId('65c388caa1635fe262c12343'), '书名': '认知破局 怎样突破能力、视野和人脉 全民商业导师张琦给大家的人生破局指南', '评论数': '108761', '推荐量': '100%', '作者': '张琦 著,磨铁文化 出品', '出版社': '北京联合出版有限公司', '售价': '¥39.00', '原价': '¥69.80', '折扣': '5.6折', '电子书价格': None, '详情页': 'http://product.dangdang.com/29599268.html'}
{'_id': ObjectId('65c388caa1635fe262c12344'), '书名': '病隙碎笔 2021纪念版(史铁生充满灵性光辉的生命笔记,启迪无数读者的长篇哲思散文经典)', '评论数': '276166', '推荐量': '100%', '作者': '史铁生 著,博集天卷 出品', '出版社': '湖南文艺出版社', '售价': '¥24.00', '原价': '¥48.00', '折扣': '5.0折', '电子书价格': '¥17.99', '详情页': 'http://product.dangdang.com/29325113.html'}
使用此类脚本下载网站内容时应遵守网站的使用条款,以及相关的法律法规。
本系列文章皆做为学习使用,勿商用。