ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

pymysql 查询遇到特殊字符?单引号、反斜杠等问题完整解决方案与 TaoToken 配置骨架

pymysql 查询遇到特殊字符?单引号、反斜杠等问题完整解决方案与 TaoToken 配置骨架 1. pymysql 查询遇到特殊字符的真实场景你在写 Python 后端接口时大概率遇到过这种报错pymysql.err.ProgrammingError: (1064, You have an error in your SQL syntax...)。日志里 SQL 看起来没毛病但数据库就是不认。排查半天发现问题出在业务数据里带了一个英文单引号或者一个反斜杠\。这类问题的核心检索词就是 pymysql、特殊字符、单引号、反斜杠、SQL 注入。它适合所有用 Python 直连 MySQL 的后端开发者尤其是做爬虫入库、内容管理、订单备注、用户昵称这类「用户输入不可控」场景的人。我先把根因说清楚pymysql 驱动不会自动处理你已经手动拼接进 SQL 字符串里的内容。只有通过参数化查询占位符 参数元组传进去的变量驱动才会在底层完成转义、加引号、防注入。很多人误以为「用了 pymysql 就安全了」其实只要你是 f-string 或str.format()拼的 SQL驱动完全不知情单引号会提前闭合字符串字面量反斜杠会吃掉后面的字符\0空字节甚至能截断语句。举个最小复现标题是SMS groups advanced technology upgrade你写成sql fSELECT id FROM information WHERE title {title}拼出来就是WHERE title SMS groups advanced...MySQL 解析到group后面的单引号就认为字符串结束了剩下的s advanced...变成非法语法直接 1064 报错。更危险的是如果这个 title 来自外部输入攻击者塞一个 OR 11你的查询条件就被改写这就是 SQL 注入。所以这篇不绕弯子直接给你可复制的参数化配置、转义兜底代码、调试打印 SQL 的方法再补上 TaoToken 统一 Key/API 通道的 settings.json 与 config.toml 骨架方便你在多模型/多工具环境里统一管理调用凭证。下面按「先解决问题再统一配置」的顺序走。2. TaoToken 前置统一 Key 与 API 通道准备在进入 pymysql 代码之前先把调用通道理清楚。TaoToken 的作用是给你一个统一的 Key 和 API 入口把模型对话、编码计划、控制台、API Keys 管理这些能力收敛到一处避免每个工具各配一套凭证、各记一个地址。你需要先拿到自己的 API Key入口在控制台的 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。拿到之后统一的基础地址是https://taotoken.net/api这个地址不加 UTM 参数直接用于代码里的 base_url。几个常用 deep link 按场景分流你按需取用想先验证模型是否正常返回用模型对话https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite长期做编码、跑 Agent 任务用 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite管理或轮换 Key回控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite查接入细节和字段说明看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你用 Claude Code / Anthropic 风格接入https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_code_anthropicutm_campaignrewrite注意API Key 属于敏感凭证不要写进前端代码、不要提交到公开仓库。建议放环境变量或本地配置文件并在.gitignore里排除。这一步的意义在于后面无论你是用脚本调模型生成 SQL 审查建议还是用编码工具辅助排查凭证和地址都是同一套不用来回切换。准备好 Key 和 base_url我们进入 pymysql 的正题。3. 可复制配置参数化查询与转义处理3.1 生产标准方案参数化查询pymysql 的占位符是%s不是?也不是{}。变量通过execute的第二个参数以元组传入驱动底层自动完成转义、加引号、防注入。import pymysql conn pymysql.connect( host127.0.0.1, userroot, passwordyour_password, databasetest, charsetutf8mb4, ) cursor conn.cursor() title SMS groups advanced technology upgrade href https://example.com/demo spider_name sms_press_releases_spider # SQL 里写 %s 占位符外面绝对不要手动加单引号 sql SELECT id, uuid, hash, version_no FROM information WHERE title %s AND title_href %s AND spider_name %s # 参数按顺序放进元组 cursor.execute(sql, (title, href, spider_name)) rows cursor.fetchall() print(rows) cursor.close() conn.close()几个必须记住的点%s外面不要写只有一个参数也要写成(title,)那个逗号不能省否则会被当成普通值而不是元组charsetutf8mb4要显式设置否则 emoji 存不进去转义行为也可能异常。参数化查询一次性解决两件事特殊字符语法报错 SQL 注入漏洞。业务代码请强制优先使用它。3.2 兜底方案escape_string 手动转义有些场景你确实需要拼 SQL比如日志打印、调试、动态表名表名不能用占位符。这时用连接对象的escape_string()。import pymysql conn pymysql.connect( host127.0.0.1, userroot, passwordyour_password, databasetest, charsetutf8mb4, ) cursor conn.cursor() title SMS groups advanced technology upgrade esc_title conn.escape_string(title) # escape_string 只转义内容不加外层引号拼接时自己补单引号 sql fSELECT id FROM information WHERE title {esc_title} cursor.execute(sql) print(sql)输出会是WHERE title SMS group\s advanced technology upgrade单引号被正确转义。注意escape_string()依赖真实数据库连接转义结果受连接 charset 影响不能脱离连接离线预处理。也不要自己写title.replace(, \\)手写替换会漏掉反斜杠、\0空字节等存在绕过注入的风险。3.3 调试打印完整 SQLmogrify想看最终渲染出来的 SQL 用于排查用cursor.mogrify()。它只生成字符串用于打印业务执行仍然走参数化execute不要把 mogrify 的输出再丢给 execute。sql SELECT id FROM information WHERE title %s params (title,) full_sql cursor.mogrify(sql, params) print(完整SQL, full_sql) cursor.execute(sql, params)3.4 TaoToken 配置骨架settings.json 与 config.toml如果你在项目里同时用多个工具/模型建议把 TaoToken 的凭证和地址统一到配置文件。下面给两个骨架字段按你实际工具调整。settings.json骨架{ taotoken: { base_url: https://taotoken.net/api, api_key: ${TAOTOKEN_API_KEY}, timeout: 60, default_model: your-model-name }, database: { host: 127.0.0.1, port: 3306, user: root, database: test, charset: utf8mb4 } }config.toml骨架[taotoken] base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} timeout 60 default_model your-model-name [database] host 127.0.0.1 port 3306 user root database test charset utf8mb4提示api_key用环境变量占位运行时读取TAOTOKEN_API_KEY避免明文入库。数据库密码同理。4. 验证请求确认查询正常返回配置写完必须做一次真实验证别只看代码「感觉对」。验证分两层数据库查询是否正常返回以及 TaoToken 通道是否可用。先验证 pymysql 参数化查询。准备一条带单引号和反斜杠的数据插入后查询import pymysql conn pymysql.connect( host127.0.0.1, userroot, passwordyour_password, databasetest, charsetutf8mb4, ) cursor conn.cursor() # 插入一条含特殊字符的数据 raw_title OBriens path\\to\\test cursor.execute( INSERT INTO information (title, title_href, spider_name) VALUES (%s, %s, %s), (raw_title, https://example.com/x, test_spider), ) conn.commit() # 用参数化查询取回 cursor.execute( SELECT id, title FROM information WHERE title %s, (raw_title,), ) row cursor.fetchone() print(查询结果, row) assert row is not None, 参数化查询未命中检查数据或 SQL assert row[1] raw_title, 取回内容与写入不一致检查 charset cursor.close() conn.close() print(pymysql 特殊字符验证通过)预期输出里row[1]应该和你写入的raw_title完全一致单引号和反斜杠都原样保留。如果断言失败先查charset是否为utf8mb4。再验证 TaoToken 通道。用统一 base_url 发一个最小请求确认 Key 有效、地址可达import os import json import urllib.request base_url https://taotoken.net/api api_key os.environ[TAOTOKEN_API_KEY] payload json.dumps({ model: your-model-name, messages: [{role: user, content: ping}], }).encode(utf-8) req urllib.request.Request( f{base_url}/v1/chat/completions, datapayload, headers{ Content-Type: application/json, Authorization: fBearer {api_key}, }, methodPOST, ) with urllib.request.urlopen(req, timeout60) as resp: body json.loads(resp.read().decode(utf-8)) print(TaoToken 返回, body)能拿到正常 JSON 响应说明 Key 和 base_url 配置正确。如果返回鉴权错误回 API Keys 页面核对https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。想先确认模型本身可用用模型对话页试一下https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。5. 本篇常见错排查坑 1给占位符加了单引号。写成WHERE title%s%s被当成字面量字符串参数替换不生效查询永远不命中。正确写法是WHERE title %s。坑 2混用拼接和参数化。一部分参数 f-string 拼进去一部分用%s拼接那部分漏转义照样有注入风险。要么全参数化要么全走escape_string不要混。坑 3离线预处理字符串。没有数据库连接就自己replace转义遇到反斜杠、特殊编码会出错。转义必须依赖真实连接。坑 4忘记charsetutf8mb4。用默认 utf8 时 emoji 存不进转义行为也可能异常。连接参数里显式写上。坑 5mogrify输出拿去执行。mogrify只用于打印日志执行必须用参数化execute否则等于绕过了驱动的安全处理。坑 6单参数忘了逗号。cursor.execute(sql, (title))里(title)不是元组是普通表达式会报参数数量错误。写成(title,)。坑 7动态表名/字段名用占位符。占位符只能用于值不能用于表名、字段名。这类动态标识符需要白名单校验后再拼接不能直接塞用户输入。排查顺序建议先看报错是不是 1064语法再看 SQL 里有没有手动拼的变量然后确认占位符写法最后查 charset。按这个顺序走基本能定位到根因。6. 统一通道与后续接入pymysql 的特殊字符问题本质是「业务变量不要直接拼进 SQL 本体」。参数化查询是唯一的生产标准答案escape_string只在调试和动态标识符场景兜底mogrify只用于日志。把这三条守住单引号、反斜杠、\0空字节都不会再让你半夜爬起来改 bug。如果你在项目里还要接模型能力做 SQL 审查、日志分析或编码辅助建议把凭证统一到 TaoToken一套 Key 走通模型对话、编码计划和 API 调用。长期跑编码和 Agent 任务的话Coding Plan 更适合持续使用https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。接入细节和字段说明看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。需要轮换或新增 Key 时回控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。最后留一个实用习惯每次写完带用户输入的查询先本地跑一遍含、\、、换行的测试数据断言取回内容和写入一致。这个动作花不了两分钟能挡掉绝大多数线上注入和语法报错。
返回列表