很多人学完 Python 的 if、for、函数之后都会卡在同一个地方:语法都懂,可真让你处理一份数据、存个库、画张图,完全不知道该从哪个库下手。实验三“Python 常用类库与数据库访问”要解决的就是这个断层——它把语法知识和真实工作之间的桥搭起来,核心就两件事:一是把高频类库用熟,二是把数据稳稳地送进数据库、再查出来。这篇博文不打算把实验报告重抄一遍,而是按真实做实验的顺序,把环境选型、类库用法、数据库访问和排错经验连起来讲。正在写这门实验的学生可以照着走,工作中需要拿 Python 处理实际数据的测试、运维和数据分析新人,也能在里面找到能直接抄走的套路。
1. 实验到底在练什么:方案选型与整体思路
1.1 三层能力,一条数据链路
这个实验表面上叫“认识几个库”,实际练的是一条完整的数据链路:采集或构造数据 → 在内存里做处理 → 持久化到数据库 → 再读出来做展示和分析。
三个环节各有代表性类库,串起来就是最基础的数据工程闭环。网络上经常有人问“python 如何连接公司系统实现自动拉表”,本质上就是这条链路的前半段:用 requests 拉接口,用 pandas 清洗,再落库,最后汇总成报表。做实验时不用想太复杂,但要清楚每一步在整个链路里的位置,这样以后换到真实环境,不管是做业务报表还是监控数据,脑子里的框架是一样的。
如果只把目标定成“记住几个库的 import 语句”,这个实验基本就白做了。真正要关注的是数据形态的变化:从 JSON 或 CSV 变成 DataFrame,从 DataFrame 变成数据库表,从数据库表再变成图表,这几次转换中各库负责什么,才是核心能力。
1.2 Python 版本与环境准备
先说版本。很多旧教材默认 Python 3.7,但现在的 pandas 2.x 要求 Python 3.9 以上,所以别拿老版本环境硬跑新代码。我的建议是直接装 Python 3.10 或 3.11,兼容性好,遇到问题的概率最低。如果你是在 Linux 服务器上装,思路一样,无非是编译安装或加官方源,注意把 pip 也一起配好。
其次是包安装问题。新人最常见的报错是 pip 安装超时,根源是默认源在境外。换国内镜像源一步到位:
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy pandas matplotlib注意我特意写了python -m pip,不是直接pip install。这样做能确保装到你当前使用的这个 Python 解释器环境里,避免系统里有多个 Python 时装错地方,这是“python 安装 numpy 库的方法”这个问题最常见的坑。
还有一个容易踩的坑:不要往全局环境里无脑装一堆包。实验课虽然不至于出大问题,但养成用虚拟环境的习惯会省很多事:
python -m venv .venv # Windows: .venv\Scripts\activate # Linux/macOS: source .venv/bin/activate如果已经装错环境了,排查方法很简单:在代码里执行import sys; print(sys.executable),看它指向哪个 Python,再用对应的 pip 去装。
1.3 IDE 与调试工具怎么选
选 IDE 不用太纠结。PyCharm 学生版免费,VSCode 加 Python 插件也完全够用。我的建议是:别把精力花在“哪个编辑器好看”上,重点学会用调试器打断点。写数据处理代码时,print大法虽然方便,但遇到长循环和复杂数据结构很容易看花眼,打断点能实时看到变量状态,效率完全不一样。
如果你习惯用 Jupyter 写实验报告,要注意一个问题:Jupyter 的单元格可以乱序执行,很容易出现“这一步用到了上一步已经改过的变量”,导致代码在 Jupyter 里能跑,存成.py文件后从头跑就报错。提交作业前,务必从头到尾完整执行一遍.py文件,保证它是干净的、可复现的。
2. 高频类库逐个过:装库、导入和实战要点
2.1 numpy:把列表换成数组,性能上一个台阶
numpy 的核心是 ndarray,也就是 N 维数组。很多初学者不理解为什么要用数组,直接用列表不就行了?区别在于:列表是对象的集合,运算时要逐个遍历元素,慢;数组是连续内存的数值块,底层用 C 实现,计算时能把循环省掉。你可以类比成:列表像手写清单,逐行看,数组像电子表格的整列求和,一下算完。
创建数组的基本姿势:
import numpy as np a = np.array([1, 2, 3]) zeros = np.zeros((3, 3)) ones = np.ones((2, 4)) seq = np.arange(0, 10, 2) linespace = np.linspace(0, 1, 5)数组切片是个重点,也是容易出 bug 的地方。和一维列表切片相比,二维数组切片更像“按行列切块”:
matrix = np.arange(12).reshape(3, 4) # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]]) sub = matrix[1:, 1:3]这里有个隐藏陷阱:数组切片返回的是原数组的视图,不是副本。sub[0, 0] = 99会连带修改matrix里的值,因为两者共享底层数据。想真正复制一份必须用sub = matrix[1:, 1:3].copy()。
另一个必须理解的概念是广播。简单说,形状不完全相同的数组之间也能直接运算,小的会自动铺开成大尺寸。比如一个(3, 1)数组和一个(1, 4)数组相加,得到(3, 4),相当于把一张小表格按位置铺到一张大表格上做加法。刚开始可能觉得抽象,写两遍代码感受一下就顺了。
还有一个小细节,热词里有“python 构建邻接矩阵”,这在图论实验里很常见。邻接矩阵本质上就是二维数组,一般这样构造:
n = 5 adj = np.zeros((n, n), dtype=int) adj[0, 1] = 1 adj[1, 2] = 1 adj[2, 0] = 1abs函数也可以顺手掌握:内置abs()作用于单个数值,np.abs()能直接作用于整个数组,对数组逐元素取绝对值,一步到位。
2.2 pandas:DataFrame 就是你的 Excel 外挂
pandas 里最重要的数据结构是 DataFrame。你可以直接把它当成 Excel 表格来理解:行是记录,列是字段,能筛选、排序、分组、透视,而且这些操作都能用代码自动化。
读取数据的常用套路:
import pandas as pd df = pd.read_csv("data.csv") df_excel = pd.read_excel("data.xlsx", sheet_name="Sheet1")注意,读 Excel 文件不是 pandas 自带功能,需要先装openpyxl,这是最常漏装的依赖之一。如果你碰到的文件是旧版.xls,还需要xlrd。
拿到 DataFrame 后,第一件事永远是看数据长什么样:
df.head() # 看前 5 行 df.info() # 看字段、非空值数量、数据类型 df.describe() # 看数值列的统计指标筛选数据是日常操作频率最高的功能。按列筛选用df["列名"]或df[["列1", "列2"]];按行筛选用布尔条件:
adult = df[df["age"] >= 18]这里有个很隐蔽的坑:如果你写成df[df["age"] >= 18],得到的是“满足条件的所有行”,而不是“一个布尔序列”。很多新手第一次写会懵,因为括号里的表达式返回的是一整列 True/False,然后再用这个布尔序列去索引 DataFrame,这就是 pandas 的布尔索引机制。
按位置筛选和按标签筛选要分清:df.iloc[0]是按第 0 行位置取,df.loc[0]是按索引标签取。如果你用read_csv读文件,默认索引是 0 到 n-1,两者结果看起来一样,很容易混淆;一旦排序或去重后索引乱了,结果就完全不同。
类型转换也是高频操作。热词里有“python 类型转换”,在 pandas 里最常用的是pd.to_numeric和astype:
df["price"] = pd.to_numeric(df["price"], errors="coerce") df["age"] = df["age"].astype(int)为什么强调这个?因为你从数据库、接口、文本里读到的数字,经常是字符串形态。直接把字符串和数字做运算会报错,或者得到莫名其妙的结果。用errors="coerce"还能把无法转换的值变成NaN,方便后续处理。
另外一个值得知道的点:pandas 是很多上层应用的基础。你以后看到“python 量化交易策略代码”,底层无非是 pandas 处理行情数据、numpy 跑数值计算,再叠一层策略逻辑,本质都离不开这里学的 DataFrame 操作。
2.3 matplotlib:中文与刻度两个经典坑一次解决
matplotlib 是绘图基础库,基本流程固定:建画布、画图、加标签、显示或保存。一个最小示例:
import matplotlib.pyplot as plt plt.plot([1, 2, 3], [4, 5, 6]) plt.xlabel("x") plt.ylabel("y") plt.show()真正让新手崩溃的是两个问题。第一个是中文乱码。matplotlib 默认字体不支持中文,图里所有中文标签都会变成方块。解决方法是画图前加两行配置:
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] plt.rcParams["axes.unicode_minus"] = Falseaxes.unicode_minus这行也必须加,否则坐标轴上的负号会显示成方框。Windows 下 SimHei 一般有效,Linux 服务器上可能需要指定系统中文字体路径,比如"WenQuanYi Zen Hei",或者干脆用"Noto Sans CJK SC"。
第二个是横坐标太密集。日期型横坐标一旦数据量稍大,刻度就挤成一团黑线。热词里搜“python 画图横坐标太密集”的人特别多,解决办法很直接:
plt.xticks(rotation=45) # 旋转 45 度如果旋转后还是挤,就手动减少刻度数量,比如每隔 5 个显示一个:
ticks = range(0, len(df), max(1, len(df) // 10)) plt.xticks(ticks=ticks, rotation=45)如果是日期数据,还可以用matplotlib.dates里的定位器来控制刻度间隔,比如MonthLocator和DateFormatter,会更精细。
2.4 其他常用库的快速认识
除三大件外,实验里还经常用到几个小库,不需要深入研究,但要知道它们是干嘛的。
requests是 HTTP 请求的事实标准,做接口调用和爬虫都靠它。如果你要“连接公司系统自动拉表”,就是用它向内部接口发请求拿数据。
json是标准库,用来解析和生成 JSON 数据,requests 拿到的响应体通常要转成 Python 字典或列表,再交给 pandas。
re是正则表达式库,做文本清洗时很管用,比如从日志里提取时间戳。
collections.Counter适合做频率统计,一行代码就能统计列表里每个元素出现的次数。
scikit-learn(简称 sklearn)是机器学习常用库,实验里如果涉及模型训练前的数据划分,一般就用train_test_split。它在算法层面封装得很好,但前提是你已经会用 pandas 把数据整理成它需要的格式。
再提醒一个包名坑:热词里有“python 下载 cv2”,实际上cv2是 OpenCV 的 Python 模块名,但 pip 安装时的包名是opencv-python。也就是说,代码里写import cv2,终端里却要执行pip install opencv-python。这种“包名和导入名不一致”的情况在 Python 生态里不少,别按导入名去 pip 安装。
最后提一下 OCR 库。如果你在实验里用到本地 OCR 库,比如 rapidocr,发现 CPU 占用特别高,这其实是正常现象,因为本地 OCR 本质是跑深度学习模型,算力消耗天然大。生产环境里一般会用 GPU 加速,或者直接调云上 API,这不是你的代码有问题。
3. 数据库访问的完整姿势:SQLite、MySQL 与 Oracle
3.1 sqlite3:零配置起步,先把事务搞清楚
实验里最推荐的入门库是 Python 内置的sqlite3,它不需要装数据库服务、不需要配置账号密码,一个文件就是一个库,最适合理解数据库的基本操作流程。
最经典的读写模板:
import sqlite3 conn = sqlite3.connect("demo.db") cur = conn.cursor() cur.execute("CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY, name TEXT)") cur.execute("INSERT INTO user (name) VALUES (?)", ("张三",)) conn.commit() cur.execute("SELECT * FROM user") rows = cur.fetchall() print(rows) conn.close()这段代码里有三个关键点。
第一是参数化查询。我用?占位符传参,而不是把变量直接拼接进 SQL 字符串。原因很直接:直接拼接容易引发 SQL 注入,而且当参数里含中文、引号、特殊符号时也容易出错。这个习惯必须从实验阶段就养成,以后连 MySQL、Oracle 也一样。
第二是commit。初学者最大的疑问是“为什么 insert 完,关掉再打开数据没了”,因为没执行conn.commit()。事务机制可以类比成文档编辑:commit相当于点“保存”,没有保存就关闭,改动全部丢失;close只是关窗口,不是保存。
第三是资源释放。最简单的写法是用with语句或try/finally确保关闭连接。如果忘记close,短时实验看不出问题,但脚本长时间跑就容易把连接耗尽。
如果你想读取一张表直接变成 DataFrame,还有一个便捷路子,配合 SQLAlchemy 使用:
from sqlalchemy import create_engine engine = create_engine("sqlite:///demo.db") df = pd.read_sql_query("SELECT * FROM user", engine)这就是下一步要讲的重点:用统一引擎去对接不同数据库。
3.2 PyMySQL:连 MySQL 时的两条实用配置
很多课程后期会把数据库换成 MySQL,Python 这边最常用的是 PyMySQL。安装命令:
python -m pip install pymysql连接写法:
import pymysql conn = pymysql.connect( host="127.0.0.1", port=3306, user="root", password="123456", database="test", charset="utf8mb4", cursorclass=pymysql.cursors.DictCursor ) cur = conn.cursor() cur.execute("SELECT * FROM user WHERE id = %s", (1,)) row = cur.fetchone() conn.close()这里有两条实用配置值得记住。
第一,charset一定要写utf8mb4。它是 MySQL 对 UTF-8 的扩展实现,能正确存储 emoji 和部分特殊字符,也能避免中文乱码问题。很多人连 MySQL 后插入中文变成问号,十有八九就是 charset 没设置。
第二,cursorclass用DictCursor后,查询结果返回的是字典,而不是默认的元组。字典的字段名访问方式row["name"]可读性高很多,特别配合 pandas 时更顺畅。
如果你用的是 MySQL 8.0 以上,默认认证插件可能是caching_sha2_password,老版本的 PyMySQL 可能连不上,升级 PyMySQL 到新版本即可解决。
3.3 新版 Oracle 连接:oracledb 的 thin 模式
Oracle 在企业环境里很常见,热词里“python 连接 oracle 查询数据”的频率也不低。注意一个历史变化:旧教程里写的cx_Oracle已经改名成python-oracledb,导入名也从cx_Oracle变成oracledb。现在安装直接:
python -m pip install oracledb新版最大的好处是默认支持 thin 模式,即纯 Python 实现,不需要额外安装 Oracle Instant Client。以前用 cx_Oracle 时经常报错DPI-1047,就是因为本地缺少 Oracle 客户端库,在新版里大部分场景已经不用管这个了。
连接示例:
import oracledb conn = oracledb.connect( user="scott", password="tiger", dsn=oracledb.make_dsn("192.168.1.10", 1521, "ORCLPDB1") ) cur = conn.cursor() cur.execute("SELECT * FROM emp WHERE deptno = :1", (10,)) rows = cur.fetchall() conn.close()注意占位符是:1、:2这种格式,不是?或%s,这是 Oracle 和 MySQL 不同的地方。
另一个容易搞混的是 SID 和 Service Name。make_dsn(host, port, "ORCLPDB1")里的第三个参数是 Service Name,不是 SID。如果 DBA 给你的是 SID,可以尝试用oracledb.connect(user, password, host + '/' + sid)这种形式。连不上时先确认这两个概念,比乱改代码更有效。
3.4 生产环境还要考虑的事:连接池、ORM 与特殊数据库
实验阶段用裸连接就够了,但如果你以后写真正的服务,有两点必须知道:连接池和 ORM。
连接池的意思是不要把每个请求都新建一个数据库连接,而是复用一批已经建立好的连接。Python 里可以用 DBUtils 的PooledDB,也可以直接让 SQLAlchemy 的 engine 帮你管理。数据库连接的建立很昂贵,相当于每次打电话都要重新拨号,连接池就是保持通话不断线。
ORM 是对象关系映射。它的价值在于用 Python 类来描述表结构,让代码不再直接写 SQL 字符串。SQLAlchemy 是 Python 里最主流的 ORM 方案,既有 ORM 的便利性,又能保留执行原生 SQL 的能力。比如读取 CSV 后直接入库:
df.to_sql("user", engine, if_exists="replace", index=False)这一行就把 DataFrame 整体写进了数据库表,是实验和日常工作中都极高频的操作。
最后提一下时序数据库。有些场景会用到专门存时间序列数据的库,比如热词里出现的松果时序数据库。这类数据库一般有官方 SDK,或者提供兼容 InfluxDB 的查询接口,可视化层面也有自己的控制台。你不需要在这个实验里深入研究它们,但要明白定位差异:关系库强在事务和复杂查询,时序库强在高吞吐写入和时间范围聚合,两者不是一个赛道的工具。
4. 综合案例:自动拉表、入库、成图、导出 Excel
4.1 准备数据与清洗逻辑
下面这个案例串起前面所有内容。场景模拟“连接公司系统自动拉表”:从接口拿数据,清洗后入库,最后生成报表图。为了不依赖外部网络和真实服务,我这里用本地 CSV 模拟接口返回,字段结构和真实场景一致。
假设有一个sales_data.csv,包含订单日期、销售区域、销售额、客户等级四个字段:
import pandas as pd import sqlite3 import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] plt.rcParams["axes.unicode_minus"] = False # 真实场景:用 requests 请求接口拿到 JSON # resp = requests.get("http://127.0.0.1:8000/api/sales", timeout=5) # df = pd.DataFrame(resp.json()) # 这里用本地 CSV 模拟,效果等价 df = pd.read_csv("sales_data.csv", parse_dates=["date"]) print(df.head())parse_dates=["date"]这一行很关键,它把日期字符串解析成真正的日期类型。如果不转,后面做按月聚合时 pandas 会不知道这是时间列,也就分不了组。
清洗逻辑按常见场景做三步:去重、缺失值处理、类型修正。
# 1. 去重 df = df.drop_duplicates() # 2. 缺失值:销售额为空的行直接丢弃,区域为空则填充"未知" df = df.dropna(subset=["amount"]) df["region"] = df["region"].fillna("未知") # 3. 类型修正:确保销售额是数值 df["amount"] = pd.to_numeric(df["amount"], errors="coerce") df = df.dropna(subset=["amount"])drop_duplicates默认对所有列判断重复,如果只想按订单号去重,可以传subset=["order_id"]。to_numeric加上errors="coerce"会把脏数据变成NaN,所以后面必须再dropna一次,否则脏数据会留在表里。
4.2 入库与查询回读
把清洗后的 DataFrame 写入 SQLite。这里我推荐用 SQLAlchemy 引擎,因为 pandas 在最新版本里对to_sql的依赖要求更明确了,直接用引擎最省心:
from sqlalchemy import create_engine engine = create_engine("sqlite:///report.db") df.to_sql("sales", engine, if_exists="replace", index=False) print("写入完成,行数:", len(df))if_exists="replace"的意思是如果表已经存在就删掉重建。实验阶段这样用没问题,但真做增量任务时要注意别把历史数据冲掉,那时需要改成if_exists="append"。
如果不想引入 SQLAlchemy,也可以手动一条条插入:
conn = sqlite3.connect("report.db") for row in df.itertuples(index=False): conn.execute( "INSERT INTO sales (date, region, amount, level) VALUES (?, ?, ?, ?)", (row.date, row.region, row.amount, row.level) ) conn.commit() conn.close()两种方式都能用,但to_sql一行就搞定,我建议你直接上 SQLAlchemy,早晚都要学的。
入库之后回读,再加个筛选条件:
df_read = pd.read_sql_query( "SELECT * FROM sales WHERE amount > 1000", engine )读到的是一个新的 DataFrame,后面的分析和绘图都用这份数据。
4.3 可视化与结果输出
最后一步是生成月度汇总图,并导出 Excel。按月份分组求和,然后画折线图:
# 按月聚合销售额 monthly = df_read.groupby(pd.Grouper(key="date", freq="ME"))["amount"].sum() plt.figure(figsize=(10, 5)) plt.plot(monthly.index, monthly.values, marker="o") plt.title("月度销售额走势") plt.xlabel("月份") plt.ylabel("销售额") plt.xticks(rotation=45) plt.tight_layout() plt.savefig("monthly_report.png", dpi=150) plt.show()这里要注意一个版本细节:pandas 2.2 之后,freq="M"已经弃用,推荐用"ME"(month end)。如果你装的是新版 pandas,照抄老教程写"M"会收到 FutureWarning,虽然不是致命错误,但属于典型的版本迭代坑。以后查资料发现警告,先看看是不是 API 改名了。
导出 Excel 需要openpyxl,别忘了:
df_read.to_excel("filtered_report.xlsx", index=False, sheet_name="销售明细") print("已导出 Excel 和图片")这样整个流程就闭环了:数据源进来,清洗,入库,查询,分析,出图,出 Excel。以后你在工作中处理任何报表,把数据源一换、字段名一改,这个框架可以直接复用。
5. 排错实录:我常遇到的几个坑和对应解法
5.1 安装与导入类问题
安装类报错集中在ModuleNotFoundError和pip timeout。前者基本是装错环境了,后者换镜像源就行。一张表格把最典型的几个场景列出来:
| 报错现象 | 常见原因 | 解决办法 |
|---|---|---|
ModuleNotFoundError: No module named 'cv2' | 用导入名去 pip 安装 | pip install opencv-python |
pip install 超时/下载慢 | 默认官方源在境外 | python -m pip install -i 清华源 包名 |
ModuleNotFoundError: No module named 'openpyxl' | pandas 读写 Excel 需要额外依赖 | python -m pip install openpyxl |
代码在 Jupyter 能跑,.py不能跑 | 单元格乱序执行导致变量污染 | 从头完整执行.py文件再做提交 |
5.2 数据操作类问题
数据操作的问题通常不是“报错”,而是“结果不对”,更难排查。比如筛选条件写错,排序后索引错位,类型不匹配等。
| 现象 | 原因 | 处理 |
|---|---|---|
分组聚合报KeyError | 分组字段被设成了索引 | reset_index()把索引还原成列 |
| 中文标签变方框 | matplotlib 字体缺失 | 设置rcParams["font.sans-serif"] |
| 横坐标挤成黑条 | 刻度太多 | plt.xticks(rotation=45)或手动减少刻度 |
| 数字列无法求和 | 字段类型是字符串 | pd.to_numeric(..., errors="coerce") |
| 数组切片改值连原数组一起变了 | 切片是视图不是副本 | 需要独立数据时用.copy() |
5.3 数据库连接与提交类问题
数据库相关的报错信息通常很直接,但新手经常被信息量吓到。几个高频问题:
| 报错信息 | 原因 | 解法 |
|---|---|---|
sqlite3.OperationalError: no such table | 表还没建或用错库 | 检查connect路径和建表语句是否执行成功 |
pymysql.err.OperationalError: 2003 | 连不上 MySQL 服务 | 检查 IP、端口、服务是否启动、防火墙 |
pymysql.err.OperationalError: 1045 | 用户名或密码错误 | 核对账号授权 |
DPI-1047 | Oracle 客户端缺失 | 用 oracledb thin 模式,无需客户端 |
| 插入中文变乱码 | 字符集不一致 | MySQL 连接加charset="utf8mb4" |
| insert 后查不到数据 | 没执行commit | 记得conn.commit() |
5.4 一点实操体会
带实验这几年,我对“类库”这件事最大的体会是:不要背 API,要背“套路”。API 记不住很正常,查文档就行,但“清洗数据有三板斧”、“连接数据库要参数化”、“写完 SQL 记得 commit”这些套路是通用的,换任何库都适用。
还有一个习惯强烈建议培养:把所有依赖写进一个requirements.txt,用python -m pip freeze > requirements.txt导出,换环境时python -m pip install -r requirements.txt一次装齐。这个习惯能从根上解决“这代码在我电脑上明明能跑”这种问题。
最后分享一个排错技巧:报错信息不是每行都要读,重点是最后一段,也就是Error后面的那句话,那才是真正的失败原因。前面堆栈里的调用过程大部分时候不需要逐行看。把最后一段抄进搜索引擎,通常比你自己瞎猜快得多。