如何读取含无效日期格式的MongoDB游标并忽略错误条目
解决pymongo读取MongoDB时因无效日期触发InvalidBSON异常的问题
问题背景
使用pymongo==3.12.0读取MongoDB数据时,部分文档存在超出Python datetime范围的日期字段(如created字段年份为201244),遍历游标时触发bson.errors.InvalidBSON异常,导致程序中断,无法继续读取后续有效文档。
解决方案
方法1:自定义日期解码器,捕获溢出错误
通过修改BSON编解码器的日期解析逻辑,捕获OverflowError并返回占位值或跳过该字段,避免整个文档解析失败。
import datetime from bson import codec_options, _millis_to_datetime from pymongo import MongoClient def safe_millis_to_datetime(millis): try: return _millis_to_datetime(millis) except OverflowError: # 返回占位日期或None,可根据需求调整 return datetime.datetime(9999, 12, 31) # 配置自定义编解码器 opts = codec_options.CodecOptions( tz_aware=False, datetime_converter=safe_millis_to_datetime ) # 应用编解码器获取集合 client = MongoClient() db = client["testdb"] collection = db.get_collection("test", codec_options=opts) # 执行查询 query = {'modified': {'$gt': datetime.datetime(2020, 7, 22, 6, 35, 51, 859000), '$lte': datetime.datetime(2022, 12, 1, 2, 44, 41, 424501)}} cursor = collection.find(query).sort("modified", 1).skip(1000).limit(1000) # 遍历并处理文档 for doc in cursor: # 检查是否为占位日期,做跳过或标记处理 if doc.get("created") == datetime.datetime(9999, 12, 31): print(f"跳过无效日期文档: {doc['_id']}") continue print(doc)
方法2:读取原始BSON字节,逐个解析文档
如果自定义解码器仍无法处理,可直接读取原始BSON响应,逐个解析文档,捕获单个文档的解析异常并跳过。
import bson from pymongo import MongoClient import datetime from bson.errors import InvalidBSON client = MongoClient() db = client["testdb"] collection = db["test"] query = {'modified': {'$gt': datetime.datetime(2020, 7, 22, 6, 35, 51, 859000), '$lte': datetime.datetime(2022, 12, 1, 2, 44, 41, 424501)}} cursor = collection.find(query).sort("modified", 1).skip(1000).limit(1000) # 手动处理查询响应 with client._get_socket(cursor.address) as sock_info: msg = cursor._query_message() sock_info.send_message(msg) reply = sock_info.receive_message() offset = 0 while offset < len(reply.data) - 1: try: doc = bson.BSON(reply.data[offset:]).decode() offset += len(doc) + 4 # 跳过BSON长度前缀+文档内容 print(doc) except InvalidBSON: offset += 4 # 跳过无效文档的长度字段,继续下一个 print("跳过无效BSON文档")
方法3:MongoDB端过滤无效日期(推荐)
如果能确定无效日期的规则(如年份超出合理范围),可在查询时直接过滤掉这些文档,从源头避免读取无效数据。
import datetime from pymongo import MongoClient client = MongoClient() db = client["testdb"] collection = db["test"] # 查询时同时过滤日期字段在合理范围内的文档 query = { 'modified': {'$gt': datetime.datetime(2020, 7, 22, 6, 35, 51, 859000), '$lte': datetime.datetime(2022, 12, 1, 2, 44, 41, 424501)}, 'created': { '$gte': datetime.datetime(1970, 1, 1), '$lte': datetime.datetime(9999, 12, 31) } } cursor = collection.find(query).sort("modified", 1).skip(1000).limit(1000) for doc in cursor: print(doc)
注意:方法3依赖MongoDB能正常识别无效日期字段,若日期字段的BSON结构已损坏,需使用方法1或方法2处理。
内容的提问来源于stack exchange,提问作者manas pythonic
相关产品推荐
相关产品推荐

