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

在爬虫项目中,我们经常会遇到非结构化或半结构化的网页数据——比如嵌套的评论列表、动态变化的字段、不同页面的字段差异等。这时传统的关系型数据库就显得有些束手束脚,而 MongoDB 作为文档型数据库的代表,其灵活的模式设计和类似 JSON 的数据结构,简直是为爬虫数据存储量身定做的。

本文将带你快速入门使用 Python3 操作 MongoDB,从环境搭建到核心 CRUD,再到实用的索引和聚合技巧,最后结合最佳实践,让你在爬虫项目中得心应手。


1. NoSQL 与 MongoDB 简介

NoSQL 数据库的优势

NoSQL(Not Only SQL)数据库专为大规模数据存储设计,对爬虫特别友好的特性有:

  • 无严格模式:抓到新字段直接存,不用提前改表结构
  • 支持嵌套数据:可以直接存网页里的列表、评论等嵌套内容
  • 高性能读写:适合爬虫高频写入数据

NoSQL 常见分类

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

MongoDB 是什么

MongoDB 是基于 C++ 编写的开源文档数据库:

  • 数据以 BSON(二进制 JSON)存储,支持嵌套文档和数组
  • 分布式架构,易扩展
  • 语法非常接近 JavaScript 对象,Python 操作起来也很顺手

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

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

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

选择数据库和集合

MongoDB 的结构是 客户端 -> 数据库 -> 集合 -> 文档,对应关系型数据库的 连接 -> 库 -> 表 -> 行

# 选择数据库(不存在会自动创建)
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 递增(比如给所有浏览量加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小于等于/大于等于-
$ne不等于-
$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. 索引管理:爬虫去重与提速的利器

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

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. 简单聚合:数据统计

聚合管道(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 实例:它自带连接池,不要每次操作都新建连接,爬虫中全局创建一个即可。
  2. 批量操作优先:用 insert_manyupdate_many 或更灵活的 bulk_write,减少网络开销。
  3. 合理用索引:为常用查询、排序、分组字段加索引,但不要滥用(索引会占用存储空间,降低写入速度)。
  4. 错误处理:捕获 PyMongo 异常,避免爬虫因数据库错误中断。
  5. 生产环境安全:修改默认端口,开启身份验证,不要暴露在公网。
from pymongo.errors import PyMongoError

try:
    collection.insert_one(article)
except PyMongoError as e:
    print(f"数据库操作失败:{e}")

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 的核心操作,快去结合自己的爬虫项目试试吧!