Is your feature request related to a problem? Please describe.
Python 环境 OpenSSL 自身异常导致 pymysql SSL 握手失败(非 MySQL 服务器问题),但 feapder 没有提供跳过 SSL 的配置入口,用户只能修改源码或降级环境。
场景复现
问题出在 Python 端,不是 MySQL 端。
Windows 11 上,conda-forge 的 OpenSSL 3.6.0+ 调用 ssl.create_default_context() 读取 Windows 系统证书商店时就会崩溃:
ssl.SSLError: [ASN1: NOT_ENOUGH_DATA] not enough data (_ssl.c:4192)
这个错误发生在 Python 进程内部(_load_windows_store_certs → load_verify_locations),此时还没有和 MySQL 建立任何网络连接,与 MySQL 的 tls_version、have_ssl 等配置无关。
pymysql 连接时默认启用 SSL,触发 create_default_context() 直接本地报错,导致 MysqlDB 初始化失败。此时只需要 ssl_disabled=True 就能跳过 SSL(连接本地 MySQL 本就不需要),但 feapder 没有提供任何配置入口。
更糟糕的是,feapder 存在一个格式化 Bug(6 个 {} 占位符 vs 7 个参数),导致真正的 SSL 异常被丢弃,用户看到的错误信息是误导性的 exception: utf8mb4,完全无法定位真实原因。
为什么会走到这一步
MysqlDB.__init__ 已经支持 **kwargs 透传给 PooledDB → pymysql:
# feapder/db/mysqldb.py, line 43-75
def __init__(self, ip=None, port=None, db=None, user_name=None,
user_pass=None, charset="utf8mb4", set_session=None, **kwargs):
...
self.connect_pool = PooledDB(creator=pymysql, ..., **kwargs)
但问题在于:
- 框架内部大量
MysqlDB() 无参调用(共 8 处),都无法传递 SSL 参数:
# 所有 8 处无参调用:
feapder/commands/create/create_item.py:29 self._db = MysqlDB()
feapder/commands/create/create_table.py:23 self._db = MysqlDB()
feapder/core/base_parser.py:128 self._mysqldb = mysqldb or MysqlDB()
feapder/core/base_parser.py:224 batch_info = MysqlDB().find(sql)
feapder/core/spiders/batch_spider.py:117 self._mysqldb = MysqlDB()
feapder/core/spiders/task_spider.py:114 self._mysqldb = MysqlDB()
feapder/network/user_pool/normal_user_pool.py:67 self._mysqldb = MysqlDB()
feapder/pipelines/mysql_pipeline.py:25 self._to_db = MysqlDB()
setting.py 没有 MySQL 额外参数配置项:
# feapder/setting.py — 当前只有 5 个 MySQL 配置
MYSQL_IP = os.getenv("MYSQL_IP")
MYSQL_PORT = int(os.getenv("MYSQL_PORT", 3306))
MYSQL_DB = os.getenv("MYSQL_DB")
MYSQL_USER_NAME = os.getenv("MYSQL_USER_NAME")
MYSQL_USER_PASS = os.getenv("MYSQL_USER_PASS")
# 没有 MYSQL_KWARGS 或 MYSQL_SSL_DISABLED
这意味着:即使 MysqlDB 底层支持透传,框架内 8 处无参调用和用户自定义爬虫都无法让用户配置这些参数。
补充说明:feapder/setting.py 底部有 from setting import *,会自动加载项目 setting.py 中的值覆盖默认配置。这也是为什么 MYSQL_KWARGS 方案可行——只需在 feapder 的 setting.py 中定义默认值,在 MysqlDB.__init__ 中读取,用户即可在项目的 setting.py 中覆盖。
Describe the solution you'd like
新增 MYSQL_KWARGS 配置项(与已有的 REDISDB_KWARGS 设计模式一致),用户可通过 setting.py 传递任意 pymysql 参数。
改动 1:feapder/setting.py — 新增配置项
# 在第 22 行 MYSQL_USER_PASS 下方新增
# 连接mysql时携带的其他参数,如 ssl_disabled=True
# 参考 https://pymysql.readthedocs.io/en/latest/modules/connections.html
MYSQL_KWARGS = dict()
改动 2:feapder/db/mysqldb.py — 读取并合并配置
def __init__(
self, ip=None, port=None, db=None, user_name=None, user_pass=None,
charset="utf8mb4", set_session=None, **kwargs
):
...
+ # 合并 setting 中的 MySQL 连接额外参数
+ extra_kwargs = getattr(setting, 'MYSQL_KWARGS', {})
+ extra_kwargs.update(kwargs)
+
try:
self.connect_pool = PooledDB(
creator=pymysql,
...
charset=charset,
setsession=set_session,
cursorclass=cursors.SSCursor,
- **kwargs
+ **extra_kwargs
)
用户使用方式
在 setting.py 中配置:
MYSQL_KWARGS = {
"ssl_disabled": True, # 禁用 SSL(适用于本地开发环境)
}
或传递其他 pymysql 参数:
MYSQL_KWARGS = {
"ssl_disabled": True,
"connect_timeout": 10,
"read_timeout": 30,
"autocommit": True,
}
为什么推荐这个方案而非新增专用配置项
| 方案 |
改动量 |
通用性 |
与现有设计一致性 |
MYSQL_KWARGS(推荐) |
2 个文件,~4 行 |
可传递任意 pymysql 参数 |
与 REDISDB_KWARGS 一致 |
MYSQL_SSL_DISABLED |
2 个文件,~6 行 |
只能控制 SSL |
新增模式 |
Describe alternatives you've considered
-
给 feapder create 命令行加 --ssl-disabled 参数:治标不治本,只修了 CLI,用户自己的爬虫代码仍然无法配置。
-
给 MysqlDB.__init__ 加 ssl_disabled=False 参数:比方案 A 更重,且只能解决 SSL 问题,下次用户要传 connect_timeout 还得再改一次。
-
修改 pymysql 的全局默认配置:会影响同一环境中所有使用 pymysql 的项目,副作用太大。
-
降级 OpenSSL:不解决框架问题,只是绕过了触发条件。其他用户遇到类似环境兼容性问题时依然无解。
Additional context
关联 Bug
此问题与 MysqlDB 的错误日志格式化 Bug 密切相关——那个 Bug 正是因为这个 SSL 错误才被发现的。格式化 Bug 导致真实的 SSLError 被丢弃,显示为误导性的 exception: utf8mb4。建议一起修复。
环境信息
| 项 |
值 |
说明 |
| feapder |
1.9.3(最新) |
|
| Python |
3.9.23 |
|
| OpenSSL |
3.6.3(conda-forge) |
← 问题根因:此版本在 Windows 上无法加载系统证书商店 |
| pymysql |
2.2.8 |
|
| DBUtils |
3.1.2 |
|
| OS |
Windows 11 Home China |
|
| MySQL |
8.x |
MySQL 本身正常,ssl_disabled=True 可正常连接 |
临时 Workaround
# 方案 1:降级 OpenSSL
conda install openssl=3.5.5 -c conda-forge
# 方案 2:手动修改 feapder 源码(不推荐)
# 在 mysqldb.py 第 59 行 PooledDB 调用中加入 ssl_disabled=True
Is your feature request related to a problem? Please describe.
场景复现
问题出在 Python 端,不是 MySQL 端。
Windows 11 上,conda-forge 的 OpenSSL 3.6.0+ 调用
ssl.create_default_context()读取 Windows 系统证书商店时就会崩溃:这个错误发生在 Python 进程内部(
_load_windows_store_certs→load_verify_locations),此时还没有和 MySQL 建立任何网络连接,与 MySQL 的tls_version、have_ssl等配置无关。pymysql 连接时默认启用 SSL,触发
create_default_context()直接本地报错,导致MysqlDB初始化失败。此时只需要ssl_disabled=True就能跳过 SSL(连接本地 MySQL 本就不需要),但 feapder 没有提供任何配置入口。更糟糕的是,feapder 存在一个格式化 Bug(6 个
{}占位符 vs 7 个参数),导致真正的 SSL 异常被丢弃,用户看到的错误信息是误导性的exception: utf8mb4,完全无法定位真实原因。为什么会走到这一步
MysqlDB.__init__已经支持**kwargs透传给PooledDB→pymysql:但问题在于:
MysqlDB()无参调用(共 8 处),都无法传递 SSL 参数:setting.py没有 MySQL 额外参数配置项:这意味着:即使
MysqlDB底层支持透传,框架内 8 处无参调用和用户自定义爬虫都无法让用户配置这些参数。Describe the solution you'd like
新增
MYSQL_KWARGS配置项(与已有的REDISDB_KWARGS设计模式一致),用户可通过setting.py传递任意 pymysql 参数。改动 1:
feapder/setting.py— 新增配置项改动 2:
feapder/db/mysqldb.py— 读取并合并配置def __init__( self, ip=None, port=None, db=None, user_name=None, user_pass=None, charset="utf8mb4", set_session=None, **kwargs ): ... + # 合并 setting 中的 MySQL 连接额外参数 + extra_kwargs = getattr(setting, 'MYSQL_KWARGS', {}) + extra_kwargs.update(kwargs) + try: self.connect_pool = PooledDB( creator=pymysql, ... charset=charset, setsession=set_session, cursorclass=cursors.SSCursor, - **kwargs + **extra_kwargs )用户使用方式
在
setting.py中配置:或传递其他 pymysql 参数:
为什么推荐这个方案而非新增专用配置项
MYSQL_KWARGS(推荐)REDISDB_KWARGS一致MYSQL_SSL_DISABLEDDescribe alternatives you've considered
给
feapder create命令行加--ssl-disabled参数:治标不治本,只修了 CLI,用户自己的爬虫代码仍然无法配置。给
MysqlDB.__init__加ssl_disabled=False参数:比方案 A 更重,且只能解决 SSL 问题,下次用户要传connect_timeout还得再改一次。修改 pymysql 的全局默认配置:会影响同一环境中所有使用 pymysql 的项目,副作用太大。
降级 OpenSSL:不解决框架问题,只是绕过了触发条件。其他用户遇到类似环境兼容性问题时依然无解。
Additional context
关联 Bug
此问题与
MysqlDB的错误日志格式化 Bug 密切相关——那个 Bug 正是因为这个 SSL 错误才被发现的。格式化 Bug 导致真实的SSLError被丢弃,显示为误导性的exception: utf8mb4。建议一起修复。环境信息
ssl_disabled=True可正常连接临时 Workaround