非关系型数据库存储 NoSQL全称Not Only SQL,意为不仅仅是SQL,泛指非关系型数据库.NoSQL基于键值对,不经过SQL层的解析,数据间没有耦合性,性能高. 非关系型数据库细分如下:键值存储数据库:代表有Redis,Voldemort和Oracle BDB等.
列存储数据库:代表有Cassandra,HBase和Riak等.
文档型数据库:代表有CouchDB和MongoDB等.
图形数据库:代表有:Neo4J,InfoGrid和Infinite Graph等. 爬取一条数据可能存在某些字段提取失败而缺失的情况,而且数据可能随时调整.另外,数据之间还存在嵌套关系.如果用关系型数据库存储,一是需要提前建表,二是如果存在数据嵌套关系的话,需要进行序列化操作才可以存储,这非常不方便.用非关系型数据库,可以避免一些麻烦,更简单高效.下面重点介绍MongoDB和Redis数据存储操作.
(一) MongoDB存储 MongoDB是由C++编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似JSON对象,它的字段值可以包含其他文档、数组及文档数组,非常灵活. 在使用之前需安装好MongoDB并启动其服务,并且要安装Python的PyMongo库. 安装PyMongo库:pip3 install pymongo PyMongo库下载:https://pypi.python.org/pypi/pymongo MongoDB源码安装方式参考:https://zhuanlan.zhihu.com/p/50240932
MongoDB配置文件db目录设置参考:https://www.jianshu.com/p/938539b2a9d8
1 连接MongoDB 使用pymongo库的MongoClient()方法连接MongoDB,第一个参数是IP地址Host,第二个参数是端口Port,端口默认是2710⑦例如下面所示: import pymongo client = pymongo.MongoClient(host='localhost', port=27107) 运行上面代码就创建MongoDB连接对象.其中host参数还可以传入连接字符串,以mongodb开头,例如: client = pymongo.MongoClient('mongodb://michael:michael123@localhost:27017/')
2 指定数据库 MongoDB可以创建多个数据库,在使用时要指定数据库,这里以test数据库为例: db = client.test 上面代码是调用client的test属性可返回数据库,还可以使用下面这种方式: db = client['test']
3 指定集合 每个数据库包含很多集合(collection),类似关系型数据库中的表. collection = db.students collection = db['students']
4 插入数据 在students这个集合中插入数据,数据以字典形式表示: student = { 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'mail', } 直接调用collection.insert()方法插入数据,如下所示: result = collection.insert(student) print(result) # 插入成功则输出ID值: 5c6fb47bf3721d3445bd09bf 在MongoDB中,每条数据都有一个_id属性来唯一标识.如果没有显式指明该属性,MongoDB会自动产生一个Objectid类型的_id 属性.insert()方法会在执行后返回_id值. 插入数据时可以同时插入多条数据,参数以列表形式传递,如下所示: from pymongo import MongoClient host = 'mongodb://michael:michael123@localhost:27017/' client = MongoClient(host) # 连接Mongodb db = client.test # 指定数据库 collection = db.students # 指定集合 student1 = { 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male', } student2 = { 'id': '20170102', 'name': 'Mike', 'age': 21, 'gender': 'male', } result = collection.insert([student1, student2]) # 插入多条数据,以列表形式指定参数 print(result) 输出信息如下,可以看出返回结果是对应的_id的集合: [ObjectId('5c734026f3721d0f4f0be288'), ObjectId('5c734026f3721d0f4f0be289')] 在PyMongo ③x版本中,不建议使用insert()方法,面是推荐使用insert_one()和insert_many()方法来分别插入单条和多条记录,如下所示: from pymongo import MongoClient host = 'mongodb://michael:michael123@localhost:27017/' client = MongoClient(host) db = client.test collection = db.students result = collection.insert_one({"name":"michael", "age":20}) print(result) # 输出结果是InsertOneResult对象 print(result.inserted_id) 输出如下所示:
5c7346baf3721d117c1a132f 这次的返回结果是InsertOneResult对象,与insert()方法返回的结果是不同的.可以调用inserted_id属性获取_id值. 对于insert_many()方法将数据以列表形式传递,获取_id值是调用属性inserted_ids,如下所示: from pymongo import MongoClient host = 'mongodb://michael:michael123@localhost:27017/' client = MongoClient(host) # 连接Mongodb db = client.test # 指定数据库 collection = db.students # 指定集合 student1 = { 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male', } student2 = { 'id': '20170102', 'name': 'Mike', 'age': 21, 'gender': 'male', } result = collection.insert_many([student1, student2]) # 用insert_many方法插入多条数据 print(result) # 输出结果是:InsertManyResult print(result.inserted_ids) # 输出是列表 输出如下所示: [ObjectId('5c7347a3f3721d11d768a561'), ObjectId('5c7347a3f3721d11d768a562')] 5 查询 利用find_one()或find()方法进行查询,在进行查询前也需要先连接MongoDB,指定数据库,指定集合.find_one()方法查询的是单个结果,find()则返回一个生成器对象.如下所示: from pymongo import MongoClient host = 'mongodb://michael:michael123@localhost:27017/' client = MongoClient(host) # 连接 db = client.test # 指定数据库 collection = db.students # 指定数据表(集合) result = collection.find_one({'name':'Mike'}) # 利用find_one()方法查询 print(type(result)) # 字典类型 print(result) # 在结果中有_id属性 查询到的数据是字典类型,输出如下所示:
{'_id': ObjectId('5c734026f3721d0f4f0be289'), 'id': '20170102', 'name': 'Mike', 'age': 21, 'gender': 'male'} 在输出结果中,多了_id属性,这是MongoDB在插入过程中自动添加的.也可以使用ObjectID进行查询,需要借助于bson库里面的objectid: from pymongo import MongoClient from bson.objectid import ObjectId host = 'mongodb://michael:michael123@localhost:27017/' client = MongoClient(host) # 连接 db = client.test # 指定数据库 collection = db.students # 指定数据表(集合) result = collection.find_one({'_id':ObjectId('5c734026f3721d0f4f0be289')}) # 利用find_one()方法查询 print(result) # 在结果中有_id属性 此时输出结果与前面的一样.如果结果不存在,则返回None. 要查询多条数据,可使用find()方法,例如查找age为20为数据,示例如下: from pymongo import MongoClient from bson.objectid import ObjectId host = 'mongodb://michael:michael123@localhost:27017/' client = MongoClient(host) # 连接 db = client.test # 指定数据库 collection = db.students # 指定数据表(集合) results = collection.find({'age': 20}) # 使用find()方法查找多条数据 print(results) # 类型是Cursor类型 for result in results: print(result) 输出如下所示: {'_id': ObjectId('5c733e96f3721d0ec2ec3357'), 'name': 'michael', 'age': 20} {'_id': ObjectId('5c734026f3721d0f4f0be288'), 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male'} 输出可以看出,结果是Cursor类型,相当于一个生成器,需要遍历获取每个结果,其中的每个结果都是字典类型.如果要查询年龄大于20的数据,find()方法的写法如下所示: results = collection.find({'age': {'$gt':20}}) 查询条件的键值也不是单纯的数字,而是一个字典,其键名是比较符号$gt,其意是键值大于20. 比较符号归纳为下面的表5-3(表5-3 比较符号) 除了用比较符号查询外,还可以用正则匹配查询.例如,查询name字段以M开头的数据,find()方法可改为: results = collection.find({'name': {'$regex': '^M.*'}}) 这里使用$regex指定正则匹配,^M.*代表以M开头的正则表达式. 正则表达式是功能符号,功能符号归类为表5-4(表5-4 功能符号)
这些操作的详细用法,参考MongoDB官方文档: https://docs.mongodb.com/manual/reference/operator/query/
6 计数(count()) 统计查询结果的数据条数,调用count()方法.例如要统计所有数据条数: count = collection.find().count() print(count) 要统计符合某个条件的数据,在find()方法中添加指定的条件,例如: count = collection.find({'age': 20}).count() print(count) 上面2个count的值是整数值.
7 排序(sort()) 直接调用查询结果的sort()方法,并传入排序字段及升序标志即可,如下所示: results = collection.find().sort('name', pymongo.ASCENDING) print([result['name'] for result in results]) 输出如下所示: ['Jordan', 'Mike', 'michael'] 参数pymongo.ASCENDING指定升序排序.要指定以降序排序,可传入pymongo.DESCENDING.
8 偏移 对排序的结果还可以调用偏移方法skip(),该方法的参数指定要偏移几个位置.例如要偏移1个元素,得到第二个及后面的元素: results = collection.find().sort('name', pymongo.ASCENDING).skip(1) print([result['name'] for result in results]) # 输出如下所示: ['Mike', 'michael'] 另外,也可以用limit()方法指定要取的结果个数,例如下面这样: results = collection.find().sort('name', pymongo.ASCENDING).skip(1).limit(1) print([result['name'] for result in results]) # 输出如下所示: ['Mike'] 这里使用limit()方法会限制返回结果个数,原本返回的是2个,现只返回1个.要注意的是,在数据库数量非常大的时候,如千万、亿级别,就不要使用大的偏移量来查询数据,因为这样可能导致内存溢出.可用类似下面操作来查询: from bson.objectid import ObjectId collection.find({'_id': {'$gt': ObjectId('5c734026f3721d0f4f0be288')}}) 要这样查询,需要记录好上次查询的_id.
9 更新 要更新数据,使用update()方法,指定更新的条件和更新后的数据即可.例如: """使用update()方法查询数据""" from pymongo import MongoClient host = "mongodb://michael:michael123@localhost:27017" client = MongoClient(host) # 连接 db = client.test # 指定数据库 collection = db.students # 指定集合(数据表) condition = {'name': 'michael'} # 设定查询条件 student = collection.find_one(condition) # 根据查询条件查询1条数据 student['age'] = 25 # 修改age字段的值是25 result = collection.update(condition, student) # 要指定更新条件和更新值 print(result) # 输出信息:{'n': 1, 'nModified': 1, 'ok': 1.0, 'updatedExisting': True} 上面代码中更新name为michael的数据的age,根据设定的查询条件,将数据查询出来,修改age值后调用update()方法将原条件和修改后的数据传入.返回结果是字典形式,ok表示执行成功,nModified代表影响的数据条数.还可以使用$set操作符对数据进行更新,例如下面这样: result = collection.update(condition, {'$set': student}) 使用$set操作符只更新student字典内存在的字段.如果该字典内还有其他字段,则不会更新,也不会删除.不用$set操作符,则会把之前的数据全部用student字典替换;如果原来存在其它字段,则会被删除. 官方不推荐使用update()方法,而是使用更严格的update_one()方法和update_many()方法,它们的第二个参数需要使用$类型操作符作为字典的键名,如下所示: condition = {'name': 'Jordan'} student = collection.find_one(condition) student['age'] = 26 result = collection.update_one(condition, {'$set': student}) print(result) print(result.matched_count, result.modified_count) 输出结果如下所示:
1 1 这里调用update_one()方法,第二个参数不能再直接传入修改后的字典,需要使用{'$set': student}的形式,返回结果是UpdateResult类型.对其调用matched_count和modified_count属性,可获得匹配的数据条数和影响的数据条数. 下面来看下update_one()方法和update_many()方法的区别.update_one()方法只操作第一条符合条件的数据.update_many()方法会操作所有符合条件的数据.例如下面示例: # 使用update_one()方法 condition = {'age': {'$gt': 20}} result = collection.update_one(condition, {'$inc': {'age': 1}}) # $inc是满足条件对应后面的值加1 print(result) print(result.matched_count, result.modified_count) # 使用update_many()方法 condition = {'age': {'$gt': 20}} result = collection.update_many(condition, {'$inc': {'age': 1}}) # $inc是满足条件对应后面的值加1 print(result) print(result.matched_count, result.modified_count) 这里指定查询条件age为大于20,然后更新条件为{'$inc': {'age': 1}},即age的值加1,执行后将第一条符合条件的数据加1,update_one()方法运行结果如下所示: 1 1 update_many()方法运行结果如下所示: 4 4 从输出可知,只要匹配到的数据都会被更新. 10 删除 直接调用remove()方法指定删除的条件即可,此时符合条件的所有数据均会被删除.例如: result = collection.remove({'name': 'Kevin'}) print(result) # 输出:{'n': 1, 'ok': 1.0} remove方法不常用,推荐使用delete_one()和delete_many()方法,示例如下: result = collection.delete_one({'name': 'Kevin'}) print(result) # 此时的类型是:DeleteResult类型 print(result.deleted_count) result = collection.delete_many({'age': {'$lt': 25}}) print(result.deleted_count) 输出如下所示:
1 6 从输出可知,delete_one()删除第一条符合条件的数据,delete_many()删除所有符合条件的数据.它们的结果类型都是DeleteResult类型.调用deleted_count属性可获取删除的数据条数. 除了上面的基本操作外,PyMongo还提供一些组合方法,如find_one_and_delete()、fine_one_and_replace()和find_one_and_update(),依次是查找后删除、替换和更新操作,用法与前面的方法基本一致.此外,还有对索引进行操作的方法,有create_index()、create_indexes()和drop_index()等. PyMongo详细用法参考官方文档:http://api.mongodb.com/python/current/api/pymongo/collection.html. 对数据库和集合本身的操作,参考官方文件:http://api.mongodb.com/python/current/api/pymongo/ (二) Redis存储 Redis是基于内存的高效键值型非关系型数据库,存取效率极高,支持多种存储数据结构,使用简单.下面说下Python的Redis操作,主要以redis-py库的用法作说明. 首先要正确安装Redis及redis-py库.要做数据导入/导出操作,还要安装RedisDump. redis-py库有两个类Redis和StrictRedis来实现Redis的命令操作. StrictRedis有大部分官方命令, 参数也一 一对应,比如set()方法就对应Redis命令的set方法.Redis是StrictRedis的子类,它的主要功能是用于向后兼容旧版本库里的几个方法.为了做兼容,它将方法做了改写,比如lrem()方法就将value和num参数的位置互换,这和Redis命令行的命令参数不一致. 官方推荐使用StrictRedis,此时此刻呢也用StrictRedis类的相关方法作介绍.
1 连接Redis Redis正常运行在6379端口,无密码,下面进行Redis连接测试: from redis import StrictRedis redis = StrictRedis(host='localhost', port=6379, db=0) redis.set('name', 'Bob') print(redis.get('name')) # 输出:b'Bob' 这段连接测试代码中,StrictRedis的参数依次是:主机地址、端口、使用的数据库.如果有密码的话,后面还可以传密码参数password='foobared',这里假设密码是foobared.这4个参数默认不传时,分别默认为:localhost、6379、0和None.首先声明redis为StrictRedis对象,调用其set()方法设置键值对.运行程序未报错,则表示连接成功,同时可以执行set()和get()操作. 还可以使用ConnectionPool(连接池)来连接,如下面代码所示: from redis import StrictRedis, ConnectionPool pool = ConnectionPool(host='localhost', port=6379, db=0) # 有密码时也可以今天这一节传 redis = StrictRedis(connection_pool=pool) 在StrictRedis内其实就是用host和port等参数又构造一个ConnectionPool,所以直接将ConnectionPool当做参数传给StrictRedis也一样.ConnectionPool还可以通过URL构建.URL格式支持下面3种: redis://[:password]@host:port/db rediss://[:password]@host:port/db unix://[:password]@/path/to/socket.sock?db=db 这3种URL分别表示创建Redis TCP连接、Redis TCP◆SSL连接、Redis UNIX socket连接.当有password时可以写,没有时可以省略.使用URL连接实例如下: url = 'redis://@localhost:6379/0' pool = ConnectionPool.from_url(url) redis = StrictRedis(connection_pool=pool) 先声明一个Redis连接字符串,然后调用from_url()方法创建ConnectionPool,接着将其传给StrictRedis就可完成连接.
2 键操作 Redis的键操作如图5-1所示:
图5-1 Reids键的一些判断和操作方法
3 字符串操作 Redis支持基本的键值对形式存储,用法如图5-2所示:
图5-2 Redis用法总结
4 列表操作 Redis提供了列表存储,列表内元素可以重复,可以从两端存储,用法如图5-3所示.
图5-3 Redis列表操作
5 集合操作 Redis提供了集合存储,集合中的元素不能重复,用法如图5-4所示.
图5-4 Redis集合操作
6 有序集合 有序集合比集合多了一个分数字段,利用它可以对集合中的数据进行排序,用法总结如图5-5所示.
图5-5 Redis有序集合操作
7 散列操作 Redis还提供了散列表的数据结构.可用name指定散列表的名称,表内存储各个键值对,用法总结如图5-6所示.
图5-6 Redis散列操作
8 RedisDump,Redis数据的导入和导出功能 RedisDump有强大的Redis数据的导入和导出功能.要使用该功能需要先进行安装.RedisDump基于Ruby实现,在安装前要先安装Ruby. RedisDump的GitHub连接和官方连接如下: GitHub: https://github.com/delano/redis-dump Redis官方:http://delanotes.com/redis-dump Ruby的安装参考:http://www.ruby-lang.org/zh_cn/documentation/installation 安装完Ruby后可使用gem命令安装RedisDump,命令如下: gem install redis-dump RedisDump提供的两个命令:redis-dump是导出数据,redis-load是导入数据.
(1) redis-dump redis-dump -h ,输入这个命令可以看所有可选项.其中 –u 代表Redis连接字符串,-d 代表数据库代号,-s 代表导出之后的休眠时间,-c 代表分块大小,默认是10000,-f 代表导出时的过滤器,-O 代表禁用运行时优化,-V 用于显示版本,-D 表示开启调试. 导出测试,有密码和无密码的使用方式如下所示: redis-dump -u :foobared@localhost:6379 # 有密码,密码是:foobared redis-dump -u localhost:6379 # 无密码 运行上面命令,可将本地0到15号数据库的所有数据导出来,输出如下所示: {"db":0,"key":"name","ttl":-1,"type":"string","value":"michael","size":7} 每条数据包含6个字段,其中db即数据库代号,key即键名,ttl即该键值对的有效时间,type即键值类型,value即内容,size是占用空间. 要导出所有数据库的所有数据为JSON文件,可使用如下命令: redis-dump -u localhost:6379 > ./redis_data.json 可使用 -d 参数指定某个数据库的导出,例如要导出10号数据库内容,命令如下: redis-dump -u localhost:6379 -d 10 > ./py_data.json 要导出特定的内容,可使用 –f 参数过滤,并指出过滤条件,例如要导出以py开头的数据,命令如下: redis-dump -u localhost:6379 -f py:* > ./py_data.json
(2) redis-load redis-load -h ,使用该命令查看所有可选项.其中 –u 是Redis连接字符串,-d 是数据库代号,默认是全部,-s 是导入之后的休眠时间,-n 是不检测UTF-8编码,-V 是显示版本,-D 是开启调试. 将JSON行文件导入到Redis数据库中,命令如下: < redis_data.json redis-load -u @localhost:6379 在linux下使用下面的命令是同样的效果: cat redis_data.json | redis-load -u @localhost:6379 Redis可实现很多架构,如维护代理池、Cookies池、ADSL拨号代理池、Scrapy-Redis分布式架构等,对于Redis的操作需要熟练.
以上就是土嘎嘎小编为大家整理的第五部分_三) 数据存储_非关系型数据库存储:MongoDB存储、Redis存储)相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!