pymysql读url时需复用连接并用with管理,用sscursor分批读取,url须strip()清洗,sql加order by和游标分页,连接必须设charset='utf8mb4'。

PyMySQL读URL时连接不释放,爬虫跑一会儿就卡死
PyMySQL默认不自动关闭连接,批量读取任务时如果每次循环都新建Connection又不close(),很快会耗尽数据库连接池,表现就是程序卡在cursor.execute()不动,或者报错Too many connections。
正确做法是复用连接,用完再关;或者更稳妥地用上下文管理器。别在循环里反复connect()。
- 用
with pymysql.connect(...) as conn:包裹整个读取逻辑,退出时自动关闭 - 如果任务量大、需分批读取,用
conn.cursor(pymysql.cursors.SSCursor)(服务端游标),避免一次性把全部URL加载进内存 - 别在
for url in urls:循环内部调用pymysql.connect()——这是最常见崩点
读出来的URL带空格或换行,requests.get()直接400
数据库字段类型是VARCHAR或TEXT时,人工录入或迁移过程常混入不可见字符,比如首尾空格、\r\n,而requests.get()对URL格式敏感,遇到非法字符会抛InvalidURL或返回400。
必须在发送请求前清洗:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 统一用
url.strip()去掉两端空白 - 检查是否为空:
if not url.strip(): continue - 简单验证协议头:
if not url.startswith(('http://', 'https://')):可补https://或跳过 - 避免用
url.replace('\r', '').replace('\n', '')这种手动清理——strip()已覆盖
批量读取时ORDER BY和LIMIT没配好,任务重复或漏抓
爬虫任务表通常有status字段标记“待抓”“已抓”“失败”,靠SELECT ... WHERE status = 'pending' ORDER BY id LIMIT 100取一批。但如果不加ORDER BY,MySQL返回顺序不保证,多次执行可能取到同一组URL;如果LIMIT太大(比如5000条),单次查询内存占用高,还容易锁表。
- 必须显式写
ORDER BY id ASC(或时间戳字段),确保每次从上次位置往后取 - 用
WHERE id > ?代替LIMIT做游标分页,比LIMIT offset, size更稳定(尤其数据有删改时) - 单次读取建议
LIMIT 100~500,太小IO频繁,太大易超时 - 读完立刻
UPDATE task SET status='processing' WHERE id IN (...),防止其他进程重复取
PyMySQL查出的URL是byte类型,requests报错“expected string or bytes-like object”
当MySQL列是TEXT或VARCHAR,但连接时没设字符集,或服务端配置了utf8mb4而Python端没声明,PyMySQL可能返回bytes而非str。此时requests.get(url)会直接报类型错误。
根本原因是连接参数缺charset='utf8mb4':
- 创建连接时必须加
charset='utf8mb4',例如:pymysql.connect(..., charset='utf8mb4') - 确认MySQL服务端
character_set_server是utf8mb4(不是utf8) - 如果仍出现
bytes,临时补救:url.decode('utf-8') if isinstance(url, bytes) else url,但这是掩盖问题,优先修连接配置
字符集不一致这事,线上跑几天才暴露,本地测试常发现不了——因为开发环境MySQL配置往往和生产不一致。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










