Python3 爬虫数据存储:MongoDB 操作指南

在爬虫项目中,我们经常会遇到非结构化或半结构化的网页数据——嵌套的评论列表、动态变化的字段、不同页面字段差异巨大……此时,传统的relational-database就显得有些“束缚手脚”。而 MongoDB 作为文档型数据库的代表,凭借灵活的模式设计和类似 JSON 的数据结构,几乎是为爬虫数据存储而生。

本文将从environment-setup开始,带你逐步掌握 Python3 操作 MongoDB 的核心技能,包括基础 CRUD、高级查询、索引去重与聚合统计,并结合爬虫场景给出最佳实践,让你在实战中游刃有余。


1. NoSQL 与 MongoDB 简介

NoSQL 数据库的优势

NoSQL(Not Only SQL)数据库是为大规模、高并发、灵活数据模型设计的数据库。对于爬虫场景,它的几个特性非常友好:

  • 无严格模式:抓取到新字段可以直接写入,无需预先修改表结构;
  • 支持嵌套数据:可以像 JSON 一样直接存储列表、对象等复杂结构,完美对应网页中的嵌套内容;
  • 高性能读写:写入速度极快,适合爬虫高频入库的场景。

NoSQL 常见分类

类型核心特点代表产品
键值存储简单高效,常用于缓存Redis
文档型类 JSON 格式,灵活度最高MongoDB
列存储适合海量数据分析HBase
图形数据库擅长处理复杂关联关系Neo4J

MongoDB 是什么

MongoDB 是一款用 C++ 编写的开源文档数据库,它:

  • 使用 BSON(二进制 JSON)存储数据,天然支持嵌套文档和数组;
  • 具备分布式架构,易于水平扩展;
  • 查询语法非常贴近 JavaScript 对象,对 Python 开发者也很友好,通过 PyMongo 即可轻松操作。

2. 环境准备

安装 MongoDB(推荐 Docker 一键启动)

如果本地没有 MongoDB 服务,使用 Docker 是最快的方式,特别适合开发测试:

docker run -d -p 27017:27017 --name mongodb mongo:latest

安装 PyMongo 驱动

pip install pymongo

3. 基本连接操作

连接 MongoDB

from pymongo import MongoClient

# 方式一:分别指定主机和端口
client = MongoClient('localhost', 27017)

# 方式二:使用连接字符串(推荐,方便后续添加认证信息)
client = MongoClient('mongodb://localhost:27017/')

选择数据库和集合

MongoDB 的组织结构为:客户端 → 数据库 → 集合 → 文档,相当于relational-database中的 连接 → 库 → 表 → 行

# 选择数据库(不存在会自动创建)
db = client['spider_data']   # 等同于 db = client.spider_data

# 选择集合(不存在也会自动创建)
collection = db['articles']  # 等同于 collection = db.articles

# 建议使用字典方式选择,避免字段名与 Python 关键字冲突

4. 核心 CRUD 操作

插入数据:单条批量任意选

MongoDB 会自动为每条文档生成一个 _id 字段作为主键,你也可以自行指定。

# 插入单条文档
article = {
    'title': 'MongoDB 爬虫入门',
    'url': 'https://example.com/mongodb-spider',
    'tags': ['Python', 'MongoDB', '爬虫'],
    'views': 100
}
result = collection.insert_one(article)
print(result.inserted_id)   # 输出自动生成的 _id

# 批量插入(性能更高,爬虫优先推荐使用)
articles = [
    {'title': 'PyMongo 基础', 'url': 'https://example.com/pymongo', 'tags': ['Python'], 'views': 50},
    {'title': '爬虫去重技巧', 'url': 'https://example.com/spider-dedup', 'tags': ['爬虫'], 'views': 80}
]
result = collection.insert_many(articles)
print(result.inserted_ids)

查询数据:灵活筛选

# 查询单条(返回第一个匹配的文档)
result = collection.find_one({'title': 'PyMongo 基础'})
print(result)

# 查询多条(返回游标,需要遍历)
results = collection.find({'views': {'$gt': 60}})  # $gt 表示大于
for res in results:
    print(res)

# 使用正则查询(例如查询标题中含“爬虫”的文档)
results = collection.find({'title': {'$regex': '.*爬虫.*'}})

更新数据:局部更新更高效

推荐使用 $set 进行局部更新,避免覆盖整个文档。

# 更新单条文档
result = collection.update_one(
    {'title': 'PyMongo 基础'},
    {'$set': {'views': 60}}   # 只更新 views 字段
)
print(f"匹配数:{result.matched_count},修改数:{result.modified_count}")

# 更新多条文档,使用 $inc 递增(例如所有文档的 views 增加 10)
result = collection.update_many(
    {},
    {'$inc': {'views': 10}}
)

删除数据

# 删除单条
result = collection.delete_one({'title': 'PyMongo 基础'})
print(result.deleted_count)

# 删除多条(清空集合请谨慎操作!)
# result = collection.delete_many({})

5. 高级查询与实用技巧

常用比较运算符

运算符含义示例
$lt小于{'views': {'$lt': 50}}
$gt大于{'views': {'$gt': 60}}
$lte / $gte小于等于/大于等于{'views': {'$lte': 100}}
$ne不等于{'title': {'$ne': 'test'}}
$in / $nin在/不在范围内{'tags': {'$in': ['Python', '爬虫']}}

计数、排序与分页

import pymongo

# 统计符合条件的文档数
count = collection.count_documents({'views': {'$gt': 60}})
print(f"符合条件的文档数:{count}")

# 排序(pymongo.ASCENDING 升序,DESCENDING 降序)
results = collection.find().sort('views', pymongo.DESCENDING)

# 分页(注意:数据量大时 skip 性能较差,建议使用 _id 范围分页)
results = collection.find().skip(2).limit(2)

6. 索引管理:爬虫去重与提速的利器

索引能极大提升查询速度,尤其是唯一索引,是爬虫实现 URL 去重的常用手段。

import pymongo

# 创建唯一索引(例如根据 url 字段去重,避免重复爬取)
collection.create_index([('url', pymongo.ASCENDING)], unique=True)

# 查看所有索引
indexes = collection.list_indexes()
for idx in indexes:
    print(idx)

# 删除指定索引
collection.drop_index('url_1')

7. 简单聚合:数据统计

聚合管道(Aggregation Pipeline)可以对数据进行多步骤处理。下面以统计标签分布为例:

pipeline = [
    {'$unwind': '$tags'},                   # 将 tags 数组拆分成多条文档
    {'$group': {'_id': '$tags', 'count': {'$sum': 1}}},  # 按标签分组计数
    {'$sort': {'count': -1}}                # 按数量降序排列
]
results = collection.aggregate(pipeline)
for res in results:
    print(res)

8. 最佳实践

  1. 重用 MongoClient 实例
    PyMongo 的 MongoClient 自带连接池,不要在每次操作时新建连接,爬虫中全局创建一个实例即可。

  2. 批量操作优先
    尽量使用 insert_manyupdate_many 或更灵活的 bulk_write,减少网络往返开销。

  3. 合理使用索引
    为常用查询、排序、分组字段创建索引,但不要滥用——索引会占用存储空间并降低写入速度。

  4. 错误处理
    捕获 PyMongo 异常,防止爬虫因数据库错误而中断。

    from pymongo.errors import PyMongoError
    
    try:
        collection.insert_one(article)
    except PyMongoError as e:
        print(f"数据库操作失败:{e}")
  5. 生产环境安全
    修改默认端口,开启身份验证,不要将数据库直接暴露在公网。


9. 完整示例

下面是一个模拟爬虫存储文章数据的完整流程:

import pymongo
from pymongo import MongoClient
from pymongo.errors import DuplicateKeyError

# 1. 连接数据库
client = MongoClient('mongodb://localhost:27017/')
db = client['spider_demo']
articles = db['articles']

# 2. 创建唯一索引,防止重复爬取
articles.create_index([('url', pymongo.ASCENDING)], unique=True)

# 3. 模拟插入爬取到的数据
sample_articles = [
    {'title': 'MongoDB 与爬虫', 'url': 'https://demo.com/1', 'tags': ['MongoDB', '爬虫'], 'views': 120},
    {'title': 'Python 基础', 'url': 'https://demo.com/2', 'tags': ['Python'], 'views': 80},
    {'title': 'MongoDB 与爬虫', 'url': 'https://demo.com/1', 'tags': ['重复数据'], 'views': 0}  # 重复 url
]

for art in sample_articles:
    try:
        articles.insert_one(art)
        print(f"插入成功:{art['title']}")
    except DuplicateKeyError:
        print(f"重复数据跳过:{art['url']}")

# 4. 统计标签分布
print("\n标签分布:")
pipeline = [
    {'$unwind': '$tags'},
    {'$group': {'_id': '$tags', 'count': {'$sum': 1}}},
    {'$sort': {'count': -1}}
]
for res in articles.aggregate(pipeline):
    print(f"{res['_id']}: {res['count']}")

# 5. 清理演示数据
articles.delete_many({})
client.close()

10. 资源推荐


通过本文的学习,你已经掌握了 PyMongo 的核心操作。赶快把这些技巧应用到你的爬虫项目中,享受 MongoDB 带来的高效与灵活吧!