万物科技 学,以致用

6. MongoDB与Python

2017-03-31
Geng

我们已经熟悉了MongoDB的基本知识,下一步就是如何让它与Python关联起来了。

首先,为了方便入门,选择一个合适的IDE还是很重要的,因为IntelliJ全家桶包括范围很广,快捷键统一,查看源代码方便,所以我还是选择Pycharm了。

然后,为了更好地与数据库交互,可以安装一个“Mongo Plugin”。

最后,使用PyMongo就可以让Python和MongoDB交互了。

PyCharm设置

我使用PyCharm来做爬虫IDE。首先,安装“Mongo Plugin”到PyCharm。重启IDE后即可开始设置并使用了。(如果不会安装插件,请看这里,其实都是我临时搜索出来的链接😂)

重启后,可以发现右边侧边栏看到Mongo Explorer菜单。打开它,点击设置按钮。

输入你的mongo路径。如果不知道,在Mac系统下,可以执行下面命令:

$ which mongo
/usr/local/bin/mongo

然后把输出的路径,这里我的是”/usr/local/bin/mongo”,输入到Path to Mongo Shell中。点击Test检查是否正确。没有问题的话,点击左下角➕,弹出框中Label位置输入这个配置的名称,这里我直接输入localhost

然后我们准备在PyCharm连接MongoDB。首先在终端中输入mongod启动数据库,然后点击下图插销按钮连接。

连接成功后,我们会看到类似下图的界面:

这里的显示了三个数据库,分别是“admin”, “local”和“test”。点击一个想要操作的数据库,然后点击上面的Mongo Sehll按钮,可以打开一个Mongo Sehll

更多使用方法,可以参考官方文档

使用PyMongo

使用PyMongo让Python和MongoDB交互,也就是Python代码操作MongoDB。下面大多数内容来自官方文档

首先在终端中输入mongod启动数据库,再进行下面操作

导入PyMongo:

import pymongo

连接数据库

回想在终端,我们是不是先用下面命令连接数据库:

$ mongo
>

这里,我们也需要连接数据库才能使用:

import pymongo
client = pymongo.MongoClient()

上面代码连接数据库,而且使用默认的主机和端口。它等同于

client = pymongo.MongoClient('localhost', 27017)

或者:

client = pymongo.MongoClient('mongodb://localhost:27017/')

连接到一个集合

为了操作数据,我们首先要连接到一个集合才可以。

一个MongoDB实例不只有一个数据库,所以为了连接到一个集合,首先要说明连接到哪个数据库。

连接或者新建并连接一个数据库:

db = client.test_database

或者:

db = client['test-database']

连接上数据库后,我们就可以开始操作集合了。 连接或者新建并连接一个结合:

collection = db.test_collection

或者:

collection = db['test-collection']

上面新建并连接的操作都是懒的。只有在第一个文档写入的时候,才会真正的新建。

文档操作

前面介绍过,文档是以BSON数据格式存储的,它类似于JSON,命名其实就是“Binary JSON”。PyMongo使用字典数据类型来描述文档。例如下面的字典可以用来表示一个博客文章。

>>> import datetime
>>> post = {"author": "Mike",
...         "text": "My first blog post!",
...         "tags": ["mongodb", "python", "pymongo"],
...         "date": datetime.datetime.utcnow()}

如果要添加一个文档,可以使用insert_one()

>>> posts = db.posts
>>> post_id = posts.insert_one(post).inserted_id
>>> post_id
ObjectId('...')

文档添加的同时,如果没有设置_id,自动会有_id创建。_id必须是唯一存在于一个集合的。

insert_one()会返回一个InsertOneResult实例。然后我又使用inserted_id返回了其_id

确认已经有我们创建的集合,可以列出数据库中所有集合:

>>> db.collection_names(include_system_collections=False)

如果要添加多个文档,可以使用insert_many()

>>> new_posts = [{"author": "Mike",
...               "text": "Another post!",
...               "tags": ["bulk", "insert"],
...               "date": datetime.datetime(2009, 11, 12, 11, 14)},
...              {"author": "Eliot",
...               "title": "MongoDB is fun",
...               "text": "and pretty easy too!",
...               "date": datetime.datetime(2009, 11, 10, 10, 45)}]
>>> result = posts.insert_many(new_posts)
>>> result.inserted_ids
[ObjectId('...'), ObjectId('...')]

最基本的就是查询一个符合条件的文档,使用find_one()。如果有匹配的,那么只会返回第一个匹配值。如果没有,返回None

如果仅仅想要返回第一个文档:

>>> import pprint
>>> pprint.pprint(posts.find_one())
{u'_id': ObjectId('...'),
 u'author': u'Mike',
 u'date': datetime.datetime(...),
 u'tags': [u'mongodb', u'python', u'pymongo'],
 u'text': u'My first blog post!'}

如果是返回匹配某个条件的文档:

>>> pprint.pprint(posts.find_one({"author": "Mike"}))
{u'_id': ObjectId('...'),
 u'author': u'Mike',
 u'date': datetime.datetime(...),
 u'tags': [u'mongodb', u'python', u'pymongo'],
 u'text': u'My first blog post!'}

类似语法,就可以如下根据_id来查询了

>>> post_id
ObjectId(...)
>>> pprint.pprint(posts.find_one({"_id": post_id}))
{u'_id': ObjectId('...'),
 u'author': u'Mike',
 u'date': datetime.datetime(...),
 u'tags': [u'mongodb', u'python', u'pymongo'],
 u'text': u'My first blog post!'}

但是需要注意,post_id类型是ObjectId,而不是字符串。所以如果得到一个字符串类型的_id,需要进行类型转换:

from bson.objectid import ObjectId

# The web framework gets post_id from the URL and passes it as a string
def get(post_id):
    # Convert from string to ObjectId:
    document = client.db.collection.find_one({'_id': ObjectId(post_id)})

Unicode字符串参考:A Note On Unicode StringsUnicode HOWTO。比如解释了u’Mike’而不是‘Mike’的原因

怎么查询多个文档呢?可以使用find(),它返回一个Cursor实例,这个实例可以遍历所有匹配文档.

遍历所有文档:

>>> for post in posts.find():
...   pprint.pprint(post)
...
{u'_id': ObjectId('...'),
 u'author': u'Mike',
 u'date': datetime.datetime(...),
 u'tags': [u'mongodb', u'python', u'pymongo'],
 u'text': u'My first blog post!'}
{u'_id': ObjectId('...'),
 u'author': u'Mike',
 u'date': datetime.datetime(...),
 u'tags': [u'bulk', u'insert'],
 u'text': u'Another post!'}
{u'_id': ObjectId('...'),
 u'author': u'Eliot',
 u'date': datetime.datetime(...),
 u'text': u'and pretty easy too!',
 u'title': u'MongoDB is fun'}

遍历匹配文档:

>>> for post in posts.find({"author": "Mike"}):
...   pprint.pprint(post)
...
{u'_id': ObjectId('...'),
 u'author': u'Mike',
 u'date': datetime.datetime(...),
 u'tags': [u'mongodb', u'python', u'pymongo'],
 u'text': u'My first blog post!'}
{u'_id': ObjectId('...'),
 u'author': u'Mike',
 u'date': datetime.datetime(...),
 u'tags': [u'bulk', u'insert'],
 u'text': u'Another post!'}

计数

使用count()即可

>>> posts.count()
3
>>> posts.find({"author": "Mike"}).count()
2

索引

添加索引可以提高特定查询速度还能提供更多其他功能。数据库自动创建的_id本身就是索引。

首先,我们可以建立一个索引:

>>> result = db.profiles.create_index([('user_id', pymongo.ASCENDING)],
...                                   unique=True)
>>> sorted(list(db.profiles.index_information()))
[u'_id_', u'user_id_1']

我们可以这样添加一些文档:

>>> user_profiles = [
...     {'user_id': 211, 'name': 'Luke'},
...     {'user_id': 212, 'name': 'Ziltoid'}]
>>> result = db.profiles.insert_many(user_profiles)

因为索引具有唯一性,所以是不能重复的,例如下面报错:

>>> new_profile = {'user_id': 213, 'name': 'Drew'}
>>> duplicate_profile = {'user_id': 212, 'name': 'Tommy'}
>>> result = db.profiles.insert_one(new_profile)  # This is fine.
>>> result = db.profiles.insert_one(duplicate_profile)
Traceback (most recent call last):
DuplicateKeyError: E11000 duplicate key error index: test_database.profiles.$user_id_1 dup key: { : 212 }

改和删

可以发现,PyMongo的语法与Mongo shell没什么太大区别。

改可以写为:

db.collection.update_one(
        {查询条件},
        {更新}
    )

例如:

db.Employees.update_one(
        {"id": criteria},
        {
        "$set": {
            "name":name,
            "age":age,
            "country":country
        }
        }
    )

删除的话:

db.collection.delete_many({条件})

实操

import pymongo

client = pymongo.MongoClient()
db = client.store
products = db.products


path = './products.txt'

with open(path, 'r') as file:
    for line in file:
        line = line.rstrip()
        data = {
            "content": line,
            "length": len(line)
        }
        products.insert_one(data)

for product in products.find():
    print(product)

上面代码中,使用了前面学的知识和基本Python读取文件的方法,最后输出打印了数据库中的内容。


Comments

你可以请我喝喝茶,聊聊天,鼓励我

Wechat Pay
wechat

Thanks!