
1. 桌面那个 db 文件到底怎么用 Python 打开你桌面上躺着一个账号密码.db双击打不开用记事本打开全是乱码但你又确实需要把里面的数据读出来。这种场景太常见了可能是某个小工具导出的本地库可能是爬虫落盘的缓存也可能是别人发给你的 SQLite 数据文件。它本质上就是一个单文件数据库不需要装 MySQL、不需要起服务Python 标准库里的sqlite3就能直接读。这篇就围绕「Python 读取 db 文件」这件事把sqlite3的连接配置、建表、查询骨架一次性讲清楚。目标很明确让你拿到任意一个.db文件后能快速知道它有几张表、表头长什么样、数据怎么取出来并且能验证自己读到的结果是对的。适合刚接触本地数据库读取的开发者也适合需要临时排查数据文件结构的人。顺带说一句如果你平时还在用各种 AI 编码工具、模型对话工具配置散落在不同地方会很烦。后面我会讲怎么用 TaoToken 把 Key 和 API 通道统一管起来让这些工具的接入配置不再东一块西一块。2. 先搞清楚 sqlite3 能做什么再动手sqlite3是 Python 自带的标准库不用pip install直接import就能用。它操作的是 SQLite 格式的数据库文件这种文件的特点是把整个数据库存成单个文件所以你会看到.db、.sqlite、.sqlite3这类后缀。Thumbs.db这种名字虽然看着像系统缓存但如果它内部是 SQLite 结构一样能读。读取流程其实就四步连接文件、拿游标、执行 SQL、取结果。听起来简单但新手最容易卡在两个地方一是路径写错导致连上了一个空库SQLite 有个坑连接不存在的文件会直接创建一个新的空库不报错二是不知道表名就去查数据SQL 直接报no such table。所以正确的顺序是先确认文件存在再连上去先查有哪些表再查表结构最后才查数据。这个顺序能帮你避开大部分「读不出来」的问题。2.1 连接配置路径、只读模式与防坑连接本身一行代码但配置有讲究。最基础的写法import sqlite3 conn sqlite3.connect(账号密码.db) cursor conn.cursor()这里有个必须提醒的点如果账号密码.db不在当前工作目录connect不会报错而是默默创建一个同名空文件。等你查表发现结果是空列表还以为文件坏了。所以稳妥做法是先判断文件是否存在import os import sqlite3 db_path 账号密码.db if not os.path.exists(db_path): raise FileNotFoundError(f找不到数据库文件{db_path}) conn sqlite3.connect(db_path)如果你只是想读不想有任何写入风险可以用只读模式打开。SQLite 支持 URI 方式连接conn sqlite3.connect(ffile:{db_path}?modero, uriTrue)modero表示 read-only这样即使代码里有误写的INSERT或UPDATE也会直接报错保护原文件。排查别人给的库时我建议默认用只读模式。2.2 查表名sqlite_master 是入口SQLite 把所有表的元信息存在sqlite_master这张系统表里。想知道库里有几张表、叫什么名字查它就行import sqlite3 conn sqlite3.connect(账号密码.db) cursor conn.cursor() sql SELECT name FROM sqlite_master WHERE typetable ORDER BY name cursor.execute(sql) tables cursor.fetchall() print(tables) print(type(tables)) conn.close()fetchall()返回的是一个列表里面每个元素是元组比如[(students,), (users,)]。如果你只想要名字列表可以顺手拍平table_names [row[0] for row in tables] print(table_names)这一步做完你就知道该查哪张表了。如果返回空列表[]说明这个文件里没有表要么它根本不是 SQLite 文件要么是个空库。2.3 查表头结构pragma table_info知道表名后下一步是看这张表有哪些字段、类型是什么。SQLite 提供了PRAGMA table_info(表名)import sqlite3 conn sqlite3.connect(账号密码.db) cursor conn.cursor() cursor.execute(PRAGMA table_info(students)) columns cursor.fetchall() for col in columns: print(col) conn.close()返回的每一行结构是(cid, name, type, notnull, dflt_value, pk)分别对应字段序号、字段名、类型、是否非空、默认值、是否主键。比如(0, id, INTEGER, 1, None, 1)就表示 id 是主键、整型、非空。这里注意PRAGMA table_info里的表名不能直接用参数占位符传得用字符串拼接但表名来自你自己查出来的结果风险可控。如果表名可能带特殊字符用双引号包一下cursor.execute(fPRAGMA table_info({table_name}))2.4 查询数据fetchone 与 fetchall 的区别结构清楚了就可以查数据。execute执行查询后游标本身是个可迭代对象你可以直接循环也可以用fetchone()逐条取或fetchall()一次取完import sqlite3 conn sqlite3.connect(账号密码.db) cursor conn.cursor() cursor.execute(SELECT * FROM students LIMIT 5) # 方式一循环读取 for row in cursor: print(row) conn.close()conn sqlite3.connect(账号密码.db) cursor conn.cursor() cursor.execute(SELECT * FROM students LIMIT 5) # 方式二fetchone 逐条 row cursor.fetchone() while row is not None: print(row) row cursor.fetchone() conn.close()conn sqlite3.connect(账号密码.db) cursor conn.cursor() cursor.execute(SELECT * FROM students LIMIT 5) # 方式三fetchall 一次取完 rows cursor.fetchall() print(rows) conn.close()数据量小的时候fetchall最省事数据量大时用循环或fetchone避免一次性把几万行塞进内存。另外如果你想让结果带字段名可以用row_factoryconn sqlite3.connect(账号密码.db) conn.row_factory sqlite3.Row cursor conn.cursor() cursor.execute(SELECT * FROM students LIMIT 5) for row in cursor: print(dict(row)) conn.close()这样每行都能按字段名取值比如row[name]比记元组下标舒服多了。3. 可复制的完整骨架从建表到查询光读别人的库还不够很多时候你需要自己建一个库来验证流程或者把读到的数据落到新库里。下面这份骨架可以直接复制运行覆盖建表、插入、查询、关闭全流程。import sqlite3 DB_PATH demo.db def init_db(): conn sqlite3.connect(DB_PATH) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, score REAL DEFAULT 0 ) ) conn.commit() conn.close() def insert_data(): conn sqlite3.connect(DB_PATH) cursor conn.cursor() cursor.executemany( INSERT INTO students (name, score) VALUES (?, ?), [(小明, 92.5), (小红, 88.0), (小刚, 76.5)] ) conn.commit() conn.close() def query_data(): conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row cursor conn.cursor() cursor.execute(SELECT id, name, score FROM students ORDER BY score DESC) rows cursor.fetchall() for row in rows: print(dict(row)) conn.close() if __name__ __main__: init_db() insert_data() query_data()运行后你会看到按分数降序排列的三条记录。这里几个细节值得说executemany适合批量插入?是参数占位符能防注入conn.commit()必须调用否则插入不生效CREATE TABLE IF NOT EXISTS保证重复运行不报错。如果你要读的是已有的账号密码.db把DB_PATH换成它跳过init_db和insert_data直接跑query_data里的查询逻辑即可。3.1 参数化查询别用字符串拼接新手常犯的错是这么写name 小明 cursor.execute(fSELECT * FROM students WHERE name {name})如果name里带个单引号SQL 就崩了还有注入风险。正确写法永远是占位符cursor.execute(SELECT * FROM students WHERE name ?, (name,))多个条件就多个?参数用元组按顺序传。这是sqlite3使用里最该养成的习惯。3.2 上下文管理器让连接自动关闭每次手动conn.close()容易忘。用with语句更稳import sqlite3 with sqlite3.connect(账号密码.db) as conn: conn.row_factory sqlite3.Row cursor conn.cursor() cursor.execute(SELECT name FROM sqlite_master WHERE typetable) print([r[name] for r in cursor.fetchall()])注意with对连接对象的作用是自动提交或回滚事务并不会自动关闭连接。要连关闭也自动可以配合contextlib.closingfrom contextlib import closing import sqlite3 with closing(sqlite3.connect(账号密码.db)) as conn: with conn: cursor conn.cursor() cursor.execute(SELECT name FROM sqlite_master WHERE typetable) print(cursor.fetchall())4. 验证读取结果是否正确代码跑通不代表结果对。怎么确认你读到的就是文件里真实的数据给你几个具体动作。第一步用表数量交叉验证。查sqlite_master得到的表名列表和你用数据库工具打开看到的表数量应该一致。如果工具里显示 5 张表你只查到 3 张可能是权限或连接到了别的文件。第二步用行数验证。对某张表执行SELECT COUNT(*)和你预期或工具里显示的行数对比cursor.execute(SELECT COUNT(*) FROM students) print(cursor.fetchone()[0])第三步抽样比对。取前几行数据和你在工具里看到的逐字段核对。特别是中文内容注意编码是否正常如果出现乱码说明文件编码或读取方式有问题。第四步检查字段类型。PRAGMA table_info返回的类型和你实际取到的值类型应该匹配。SQLite 是动态类型字段声明为INTEGER也可能存字符串所以别完全信声明要看实际值。把这四步走完基本能确认读取链路是通的、数据是对的。5. 本篇常见错排查报错no such table: xxx表名拼错或者你连的库不对。先跑一遍查表名的 SQL确认表真实存在。注意 SQLite 表名大小写不敏感但拼写要一致。查询结果为空但表里有数据检查是不是连到了新建的空库。用os.path.exists确认路径或者打印conn.execute(PRAGMA database_list).fetchall()看实际打开的文件路径。中文显示乱码SQLite 默认 UTF-8一般不会乱码。如果乱码检查源文件是不是被别的编码写过或者终端编码不是 UTF-8。database is locked有另一个进程正在写这个库。只读场景用modero打开或者等写入进程结束。fetchall返回空列表SQL 执行成功但没匹配到数据。先去掉WHERE条件查全表确认表里有数据再逐步加条件。插入后查不到忘了conn.commit()。SQLite 默认开启事务不提交的话数据只在当前连接可见关闭就丢。路径带中文或空格报错用原始字符串或双反斜杠Windows 下推荐rC:\Users\xxx\账号密码.db。6. 用 TaoToken 统一管理 AI 工具的 Key 与通道读数据库这类活儿很多时候你会顺手让 AI 工具帮你写 SQL、解释表结构、生成查询代码。但如果每个工具都单独配 Key、单独记 endpoint配置会越来越乱。TaoToken 做的就是把这层统一起来一个 Key、一个 API 通道对接多个模型和编码工具。接入方式很简单把 API 地址指向https://taotoken.net/api用你在控制台生成的 Key 即可。如果你用的是 Claude Code 这类编码工具可以在它的配置里把 Anthropic 的 base URL 换成 TaoToken 的通道地址这样就不用为每个工具单独维护一套凭证。具体入口我整理在下面按需取用想先试试模型对话效果https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite长期写代码、跑 Agent 任务看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite管理 Key 和额度https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档和参数说明https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteClaude Code 接入配置https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite回到数据库这条线你可以让 AI 工具基于PRAGMA table_info的输出帮你生成建表语句或者把查询结果贴给它做字段含义推断。Key 统一之后切换工具不用重新配一遍省下来的时间够你多排查几个no such table了。最后留个实用习惯读任何陌生的.db文件先跑「查表名 → 查表结构 → 查行数 → 抽样看数据」这四步再写业务查询。顺序对了坑就少一半。