首页 > 数据库 >第5章 用数据库存储数据

第5章 用数据库存储数据

时间:2024-02-07 22:00:46浏览次数:39  
标签:存储 redis name data 数据库 db 数据 id

第5章 用数据库存储数据

5.1 MySQL 数据库

用 CSV 和 Excel 存储数据有两个优点:

  • 非开发人员也能看到数据,不需要额外的学习成本。
  • 使用方便,数据存储在文件里,复制到其他设备上可以直接查看。

这种表格存储文件的形式适用于少量数据的情况,当记录很多、字段很多时,打开文件会非常慢,而且卡顿,多个 Sheet 之间不能设计复杂的数据关系,这时就要使用数据库了。

  1. 什么是数据库
    数据库是按照数据结构来组织、存储和管理数据的仓库,简单点说就是存储数据的仓库。
  2. 关系型数据库和非关系型数据库
    前者通过二维表保存,存储行列组成的表,通过表与表间的关联关系来体现;后者又称 NoSQL,基于键值对,不需要经过 SQL 层解析,数据间没有耦合,性能非常好。关系型数据库中非常流行的MySQL 数据库。

5.1.1 安装 MySQL

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 的基本操作

  1. 用户登录
# 回车后,需要输入在安装MySQL时设置的密码
mysql -u root -p
  1. 查看数据库
show databases;

执行结果如下:

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.01 sec)
  1. 选择数据库
use 数据库名

执行结果如下:

mysql> use mysql;
Database changed
  1. 查看数据库里的所有表
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 数据库语法速成

  1. MySQL 数据类型
    数据类型就是定义存储什么类型的数据,如数字、日期、字符串等。举个简单的例子,想保存一个数字,就要定义一个数字类型的字段,而不能定义一个数字类型的字段,然后往里面放字符串。
    MySQL 中支持以下几种数据类型:
  2. 整型(取值范围如果加了 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。
  1. 浮点型
  • FLOAT(m,d):单精度浮点型,8 位精度(4 字节),m 为总个数,d 为小数位。
  • DOUBLE(m,d):双精度浮点型,16 位精度(8 字节),m 为总个数,d 为小数位。
  1. 字符串
  • 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 个字符。
  1. 二进制数据
  • _BLOB:以二进制方式存储,不分大小写,不用指定字符集,只能整体读出。
  • _TEXT:以文本方式存储,英文存储区分大小写,可以指定字符集。
  1. 日期时间类型
  • DATE:日期。
  • TIME:时间。
  • DATETIME:日期时间。
  • TIMESTAMP:自动存储记录修改时间。
  1. 数据类型的属性
    数据类型的属性就是对字段加一些限定条件,如设置默认值,如果写入记录没有这个
    字段,就把该字段的值设置为默认值。除此之外,还有主键和字段是否为空等,相关的属
    性如下所示。
  • NULL:数据列可包含 NULL 值,就是可以不设置值。
  • NOT NULL:数据列不允许包含 NULL 值。
  • DEFAULT:默认值。
  • PRIMARY KEY:主键。
  • AUTO_INCREMENT:自动递增,适用于整数类型。
  • UNSIGNED:无符号。
  • CHARACTER SET name:指定一个字符集。
  1. 库操作
    MySQL 提供了如下库操作命令。
  2. 建库。
CREATE DATABASE 数据库名;
  1. 删库(删除的数据库无法恢复)。删除不存在的库会报 database doesn't exist 的错误,故删库前要先用 IF EXISTS 进行判断。
DROP DATABASE IF EXISTS 数据库名;
  1. 表操作
    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;
  1. 案例
    下面通过一个实例来演示
# 建新数据库
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;
  1. 事务
    事务就是一系列数据库的操作,要么完全执行,要么完全不执行。举个简单的例子:银行转账,转账后数据库对你的余额扣钱,别人收到钱,数据库对他的余额加钱,如果转账失败,则保证两个人的余额不变,而不是你的钱扣了,他余额没变,或者你的钱没扣,他的钱反而多了。可以通过下述命令开启事务、确认事务或回滚事务。
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)
  1. 连接数据库
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',)
  1. 新建数据库

代码示例如下(判断数据库是否存在,不存在的话则新建,同时设置编码类型):

cursor.execute("Create Database If Not Exists test Character Set UTF8MB4")
  1. 新建数据库表
    代码示例如下(判断表是否存在,不存在的话则新建):
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)
  1. 插入数据
    代码示例如下:
# 插入数据
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)
  1. 删除数据
    代码示例如下:
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)
  1. 修改数据
    代码示例如下:
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数据库

  1. 创建数据库
create  database douban_music
  1. 创建表
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 数据库可视化工具

数据库可视化工具是指那些帮助用户通过图形界面来管理、查询和展示数据库中数据的软件应用。这类工具大大降低了数据库管理的复杂性,使得即使是非技术用户也能轻松地进行数据操作和分析。下面介绍几种常见的数据库可视化工具:

  1. MySQL Workbench

适用数据库:MySQL

主要特点:

  • 官方出品,支持数据库设计、开发、管理和维护。
  • 提供数据建模、SQL开发和服务器配置等功能。
  • 支持视觉化查询构建器。
  1. phpMyAdmin

适用数据库:MySQL

主要特点:

  • 基于Web的工具,方便在浏览器中使用。
  • 支持各种操作,如数据库管理、表管理、索引管理等。
  • 提供导入导出数据的功能。
  1. PgAdmin
    适用数据库:PostgreSQL

主要特点:

  • 官方图形界面工具,支持所有PostgreSQL版本。
  • 可以运行在Windows、Linux和Mac OS X平台。
  • 提供查询工具、服务器监控等高级功能。
  1. Oracle SQL Developer

适用数据库:Oracle

主要特点:

  • 官方出品,免费提供给Oracle数据库用户。
  • 支持SQL语句编辑、数据库对象管理、性能监控等。
  • 提供数据模型设计工具。
  1. Microsoft SQL Server Management Studio (SSMS)

适用数据库:SQL Server

主要特点:

  • 微软官方工具,集成环境用于管理SQL Server。
  • 提供数据库配置、管理、开发等全方位功能。
  • 支持T-SQL脚本编辑、执行计划分析等。
  1. DBeaver

适用数据库:多数据库(MySQL, PostgreSQL, SQLite, Oracle, DB2, SQL Server等)
主要特点:

  • 开源跨平台数据库工具。
  • 支持几乎所有数据库,通过JDBC驱动连接。
  • 功能丰富,包括数据管理、SQL编辑器、数据导出导入等。
  1. Navicat

适用数据库:多数据库(MySQL, PostgreSQL, Oracle, SQLite, SQL Server等)

主要特点:

  • 支持多种数据库,界面友好。
  • 提供数据库管理、开发、维护的全套解决方案。
  • 支持数据迁移、同步、备份等高级功能。-

DataGrip
适用数据库:多数据库(包括但不限于 MySQL, PostgreSQL, SQLite, Oracle, SQL Server, DB2, Sybase, H2 等)

主要特点:

  1. 全面的数据库支持
    支持广泛的数据库系统,几乎涵盖了市场上所有主流数据库。
    通过JDBC驱动连接数据库,方便灵活。
  2. 智能代码编辑
    智能代码补全功能,根据上下文提供精准的补全建议。
    实时代码分析和错误提示,提升代码质量。
    强大的SQL重构工具,简化数据库结构调整过程。
  3. 高效的数据导航和搜索
    快速导航到任意数据库对象,如表、视图、存储过程等。
    支持全局搜索,快速定位所需数据或对象。
  4. 灵活的数据编辑
    直观的数据编辑器,支持直接在表格中编辑数据。
    执行SQL查询,并以多种格式展示结果,支持结果导出。
  5. 版本控制集成
    与Git, SVN, Mercurial等版本控制系统无缝集成。
    方便管理和追踪数据库变更。
  6. 强大的数据库管理工具
    提供数据库对象管理、SQL执行计划分析、数据库连接管理等功能。
    支持数据库用户权限管理和数据库维护任务。
  7. 图形化数据库设计
    提供ER图(实体关系图)功能,帮助可视化数据库结构设计。
  8. 多环境支持
    支持在一个界面中同时管理多个数据库实例。
    方便进行跨数据库或跨环境的操作。

5.3 Redis 数据库

Redis 是一个使用 ANSI C 编写的开源、支持网络、基于内存、可选持久化 Key-Value的非关系型存储数据库。它具有下述特性:

  • 速度快(数据存在内存中,C 语言编写,单线程)
  • 持久化(对数据的更新将异步保存到磁盘上)
  • 支持多种数据结构(支持 8 种数据类型,常用 SString、Hash、List、Set 和 SortSet)
  • 支持多种编程语言(Python、PHP、Java、Golang 等)
  • 功能丰富(发布订阅、lua 脚本、事务、pipeline)
  • 简单(代码短小精悍,不依赖外部库,单线程模型)
  • 主从复制
  • 高可用、分布式

以上就是关于 Redis 的简介,你可以把它简单地理解成一个通过键值对保存数据的非
关系型内存数据库。下面是与 Redis 有关的一些链接。

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 基本操作示例

  1. 连接 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()
  1. 通用操作
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() # 删除所有数据库中所有的键
  1. 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的字符
  1. 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,将多个列表排列,从右向左移除各个列表内的元素
  1. 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对应的集合中的某些值
  1. 有序集合操作(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. 散列表操作(键值对)
    (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 方式。

  1. 连接 MongoDB 数据库
    连接 MongoDB 数据库,默认没有密码,如果设置了密码,要调用 db.auth("用户名","密码")。
conn = pymongo.MongoClient(host='localhost', port=27017)

或者采用 MongoDB 连接字符串的形式也可以:

conn = pymongo.MongoClient('mongodb://localhost:27017')
  1. 选择数据库
    选择数据库,也可以使用 conn['test']这一方式,等价于:
db = conn.test
  1. 选择 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')
  1. 创建数据库
    编写代码来创建一个数据库:
db = conn['test_db']
  1. 创建 collection
collection = db['test_collection']
  1. 插入数据(一条)
    有一点要注意,MongoDB 是有惰性的,在插入第一条数据之前,不会真生成数据库和collection。
db = conn['test_db']
collection = db['test_collection']
dic = {'id': '1', 'name': 'Jay'}
# 文档在MongoDB中的存储格式是JSON,在PyMongo中一般传入字典
collection.insert_one(dic)
  1. 插入数据(多条)
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"}
  1. 查询数据
# 查找一条
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 的所有数据,可以采用比较符号。
image

除此之外,还支持正则匹配查询,使用$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'}

除了正则表达式,还有其它功能符号。
image
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
  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"}
  1. 计数
print("数据库中有%d条记录。" % collection.find().count())

代码执行结果如下:

数据库中有2条记录。
  1. 排序
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'}
  1. 偏移
    有时可能只想获取某几个元素,可以利用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'}

使用此类脚本下载网站内容时应遵守网站的使用条款,以及相关的法律法规。
本系列文章皆做为学习使用,勿商用。

标签:存储,redis,name,data,数据库,db,数据,id
From: https://www.cnblogs.com/zx-demo/p/18011345

相关文章

  • DataSpell 2023:专注于数据,加速您的数据科学之旅 mac/win版
    JetBrainsDataSpell2023是一款专为数据科学家和数据分析师设计的集成开发环境(IDE)。这款IDE提供了强大的数据分析和可视化工具,旨在帮助用户更快速、更高效地进行数据科学工作。→→↓↓载DataSpell2023mac/win版 DataSpell2023在保持其一贯的数据处理、数据清洗、数据探......
  • 第 4章 用 CSV 和 Excel 存储数据
    第4章用CSV和Excel存储数据4.1用CSV文件存储数据CSV(Comma-SeparatedValues)其实就是纯文本,用逗号分隔值,可以分隔成多个单元格。CSV文件除了可以用普通的文本编辑工具打开,还能用Excel打开,但CSV和Excel有以下不同:所有值都是字符串类型。不支持设置字体颜色和样......
  • 学习 Redis 基础数据结构,不讲虚的。
    学习Redis基础数据结构,不讲虚的。一个群友给我发消息,“该学的都学了,怎么就找不到心意的工作,太难了”。很多在近期找过工作的同学一定都知道了,背诵八股文已经不是找工作的绝对王牌。企业最终要的是可以创造价值,或者首先需要干活的人,所以实战很重要。今天这篇文章就是给大家分享......
  • Go语言的100个错误使用场景(30-40)|数据类型与字符串使用
    目录前言4.控制结构4.1忽视元素在range循环中是拷贝(#30)4.2忽略在range循环中如何评估表达式(#31)4.3忽略在range中使用指针元素的影响(#32)4.4对map遍历的错误假设(#33)4.5忽略break的作用(#34)4.6在循环中使用defer(#35)5.字符串5.1不理解rune的概念(#36)5.2不准确的字......
  • 【转帖】数据库传奇:MySQL创世之父的两千金My、Maria
    https://zhuanlan.zhihu.com/p/672142719 1人赞同了该文章《数据库传奇:MySQL创世之父的两千金My、Maria》一、前言  MySQL是一款备受欢迎的关系型数据库管理系统(RDBMS),最初由瑞典公司MySQLAB开发,目前隶属于OracleCorporation。在DB-Engines的排名中,MySQL稳......
  • 如何实现Vuex本地存储
    在前端开发中,Vuex是一款非常强大的状态管理工具,但是默认情况下,Vuex的数据是存储在内存中的,刷新页面后数据将会丢失。这往往会导致用户在刷新页面后需要重新登录等繁琐的操作。本篇文章将教会您如何实现Vuex的本地存储,让您的应用程序能够在刷新页面后依然保持用户的状态和数据。一、......
  • 【专题】2023旅游行业洞察报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=33833原文出处:拓端数据部落公众号根据文化和旅游部的数据统计,2023年"五一"假期全国国内共有2.74亿人次进行了旅游,同比增长了70.83%。而端午节假期期间,全国国内出游人数达到1.06亿人次,同比增长了32.3%。消费者对于旅游的热情高涨,文化和旅游行业呈现......
  • 【专题】绿色5G白皮书报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=33736原文出处:拓端数据部落公众号气候变化是一个全球性挑战,需要国际合作来解决。目前,已有189个国家加入了《巴黎协定》,并且各大经济体纷纷承诺实现碳中和目标,如欧盟委员会的长期战略愿景,中国的碳达峰和碳中和目标,以及美国重新加入巴黎协定。阅读......
  • offline 2 online | 重要性采样,把 offline + online 数据化为 on-policy samples
    论文标题:Offline-to-OnlineReinforcementLearningviaBalancedReplayandPessimisticQ-EnsembleCoRL2021,4个weakaccept。pdf:https://arxiv.org/pdf/2107.00591.pdfhtml:https://ar5iv.labs.arxiv.org/html/2107.00591openreview:https://openreview.net/forum?id=......
  • 【专题】2023年房地产行业报告汇总PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=35157原文出处:拓端数据部落公众号中国房地产行业是国民经济的重要支柱之一,对经济增长和就业创造起着重要作用。随着经济的发展和城市化进程的推进,房地产市场的供需状况成为人们关注的焦点。本报告合集通过对当前国内房地产行业的供需状况进行全面......