爬虫之mongodb数据库
Posted 方杰0410
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爬虫之mongodb数据库相关的知识,希望对你有一定的参考价值。
一 mongodb的介绍
1、易用性:mongodb是一款强大。灵活并且易扩展的数据库。他面向于文档的数据库,而不是关系性数据库。不采用关系型主要是为了获得更好的扩展性。还有一个好处就是面向文档的数据库不在需要行(row)的概念。通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录来表现复杂的层级关系,这与现代的面向对象语言的开发者对数据的看法一致。 另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小。由于没有固定的模式,根据需要添加或删除字段变得更容易了。通常由于开发者能够进行快速迭代,所以开发进程得以加快。而且,实验更容易进行。开发者能尝试大量的数据模型,从中选一个最好的。
2、易扩展性:应用程序数据集的大小正在以不可思议的速度增长。随着可用带宽的增长和存储器价格的下降,即使是一个小规模的应用程序,需要存储的数据量也可能大的惊人,甚至超出 了很多数据库的处理能力。过去非常罕见的T级数据,现在已经是司空见惯了。 由于需要存储的数据量不断增长,开发者面临一个问题:应该如何扩展数据库,分为纵向扩展和横向扩展,纵向扩展是最省力的做法,但缺点是大型机一般都非常贵,而且 当数据量达到机器的物理极限时,花再多的钱也买不到更强的机器了,此时选择横向扩展更为合适,但横向扩展带来的另外一个问题就是需要管理的机器太多。 MongoDB的设计采用横向扩展。面向文档的数据模型使它能很容易地在多台服务器之间进行数据分割。MongoDB能够自动处理跨集群的数据和负载,自动重新分配文档,以及将 用户的请求路由到正确的机器上。这样,开发者能够集中精力编写应用程序,而不需要考虑如何扩展的问题。如果一个集群需要更大的容量,只需要向集群添加新服务器,MongoDB就会自动将现有的数据向新服务器传送
3,丰富的功能
MongoDB作为一款通用型数据库,除了能够创建、读取、更新和删除数据之外,还提供了一系列不断扩展的独特功能 #1、索引 支持通用二级索引,允许多种快速查询,且提供唯一索引、复合索引、地理空间索引、全文索引 #2、聚合 支持聚合管道,用户能通过简单的片段创建复杂的集合,并通过数据库自动优化 #3、特殊的集合类型 支持存在时间有限的集合,适用于那些将在某个时刻过期的数据,如会话session。类似地,MongoDB也支持固定大小的集合,用于保存近期数据,如日志 #4、文件存储 支持一种非常易用的协议,用于存储大文件和文件元数据。MongoDB并不具备一些在关系型数据库中很普遍的功能,如链接join和复杂的多行事务。省略 这些的功能是处于架构上的考虑,或者说为了得到更好的扩展性,因为在分布式系统中这两个功能难以高效地实现
4、卓越的性能:MongoDB的一个主要目标是提供卓越的性能,这很大程度上决定了MongoDB的设计。MongoDB把尽可能多的内存用作缓存cache,视图为每次查询自动选择正确的索引。 总之各方面的设计都旨在保持它的高性能 虽然MongoDB非常强大并试图保留关系型数据库的很多特性,但它并不追求具备关系型数据库的所有功能。只要有可能,数据库服务器就会将处理逻辑交给客户端。这种精简方式的设计是MongoDB能够实现如此高性能的原因之一
二 MongoDB的基础知识
文档是MongoDB的核心概念,文档就是键值对的一个续集。就相当于python中的有序字典,也就相当于mysql数据库里面的记录。
注意的是:
#1、文档中的键/值对是有序的。 #2、文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。 #3、MongoDB区分类型和大小写。 #4、MongoDB的文档不能有重复的键。 #5、文档中的值可以是多种不同的数据类型,也可以是一个完整的内嵌文档。文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
文档的命名规范:
#1、键不能含有\\0 (空字符)。这个字符用来表示键的结尾。 #2、.和$有特别的意义,只有在特定环境下才能使用。 #3、以下划线"_"开头的键是保留的(不是严格要求的)。
2 集合就是一组文档。如果将MongoDB中的一个文档比喻为关系型数据库的一行,那么集合就相当于一张表
#1、集合存在于数据库中,通常情况下为了方便管理,不同格式和类型的数据应该插入到不同的集合,但其实集合没有固定的结构,这意味着我们完全可以把不同格式和类型的数据统统插入一个集合中。 #2、组织子集合的方式就是使用“.”,分隔不同命名空间的子集合。 比如一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors,这是为了使组织结构更清晰,这里的blog集合(这个集合甚至不需要存在)跟它的两个子集合没有任何关系。 在MongoDB中,使用子集合来组织数据非常高效,值得推荐 #3、当第一个文档插入时,集合就会被创建。合法的集合名: 集合名不能是空字符串""。 集合名不能含有\\0字符(空字符),这个字符表示集合名的结尾。 集合名不能以"system."开头,这是为系统集合保留的前缀。 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
3 数据库:在MongoDB中,多个文档组成集合,多个集合可以组成数据库。
数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串: #1、不能是空字符串("")。 #2、不得含有\' \'(空格)、.、$、/、\\和\\0 (空字符)。 #3、应全部小写。 #4、最多64字节。 有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。 #1、admin: 从身份认证的角度讲,这是“root”数据库,如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。再者,一些特定的服务器端命令也只能从admin数据库运行,如列出所有数据库或关闭服务器 #2、local: 这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中 #3、config: MongoDB用于分片设置时,分片信息会存储在config数据库中
4 强调:把数据库名添加到集合名前,得到集合的完全限定名,即命名空间。如下:
如果要使用cms数据库中的blog.posts集合,这个集合的命名空间就是
cmd.blog.posts。命名空间的长度不得超过121个字节,且在实际使用中应该小于100个字节
三 安装
下载网址:https://www.mongodb.com/download-center#community
注意:在 MongoDB 2.2 版本后已经不再支持 Windows XP 系统。最新版本也已经没有了 32 位系统的安装文件。
MongoDB for Windows 64-bit 适合 64 位的 Windows Server 2008 R2, Windows 7 , 及最新版本的 Window 系统。 MongoDB for Windows 32-bit 适合 32 位的 Window 系统及最新的 Windows Vista。 32 位系统上 MongoDB 的数据库最大为 2GB。 MongoDB for Windows 64-bit Legacy 适合 64 位的 Windows Vista, Windows Server 2003, 及 Windows Server 2008 。
1 安装:根据你的系统下载 32 位或 64 位的 .msi 文件,下载后双击该文件,按操作提示安装即可。安装过程中,你可以通过点击 "Custom(自定义)" 按钮来设置你的安装目录。
创建数据目录 MongoDB将数据目录存储在 db 目录下。但是这个数据目录不会主动创建,我们在安装完成后需要创建它。请注意,数据目录应该放在根目录下((如: C:\\ 或者 D:\\ 等 )。 在本教程中,我们已经在 C 盘安装了 mongodb,现在让我们创建一个 data 的目录然后在 data 目录里创建 db 目录。
解压,讲bin目录加入环境变量
新建文件和目录如下:
......\\data\\db ......\\data\\log ......\\data\\log\\mongod.log
新建配置文件mongod.cfg,参考:
网址:https://docs.mongodb.com/manual/reference/configuration-options/
systemLog: destination: file path: "c:\\data\\log\\mongod.log" logAppend: true storage: journal: enabled: true dbPath: "c:\\data\\db" net: bindIp: 0.0.0.0 port: 27017 setParameter: enableLocalhostAuthBypass: false
制作系统服务:
mongod --config "C:\\mongodb\\mongod.cfg" --bind_ip 0.0.0.0 --install
启动关闭:
net start MongoDB net stop MongoDB
登陆:
mongo
借鉴网址:http://www.runoob.com/mongodb/mongodb-window-install.html
账户管理:
#账号管理:https://docs.mongodb.com/master/tutorial/enable-authentication/ #1、创建账号 use admin db.createUser( { user: "root", pwd: "123", roles: [ { role: "root", db: "admin" } ] } ) use test db.createUser( { user: "egon", pwd: "123", roles: [ { role: "readWrite", db: "test" }, { role: "read", db: "db1" } ] } ) #2、重启数据库,auth设置管理员权限 mongod --remove mongod --config "C:\\mongodb\\mongod.cfg" --bind_ip 0.0.0.0 --install --auth #3、登录:注意使用双引号而非单引号 mongo --port 27017 -u "root" -p "123" --authenticationDatabase "admin" 也可以在登录之后用db.auth("账号","密码")登录 mongo use admin db.auth("root","123") #推荐博客:https://www.cnblogs.com/zhoujinyi/p/4610050.html
启动服务端还有在这种方法:
E:\\MongoDB\\Server\\3.6\\bin\\mongod --dbpath E:\\MongoDB\\Server\\3.6\\data\\db
注意:在不同的库之间需要创建不同的用户名进行操作,用户名不可以越库操作。
命令行shell
#1、mongo 127.0.0.1:27017/config #连接到任何数据库config #2、mongo --nodb #不连接到任何数据库 #3、启动之后,在需要时运行new Mongo(hostname)命令就可以连接到想要的mongod了: > conn=new Mongo(\'127.0.0.1:27017\') connection to 127.0.0.1:27017 > db=conn.getDB(\'admin\') admin #4、help查看帮助 #5、mongo时一个简化的javascript shell,是可以执行JavaScript脚本的
四 基本的数据类型
1、在概念上,MongoDB的文档与Javascript的对象相近,因而可以认为它类似于JSON。JSON(http://www.json.org)是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚(其官网证明了这点),且仅包含六种数据类型。
2、这样有很多好处:易于理解、易于解析、易于记忆。然而从另一方面说,因为只有null、布尔、数字、字符串、数字和对象这几种数据类型,所以JSON的表达能力有一定的局限。
3、虽然JSON具备的这些类型已经具有很强的表现力,但绝大数应用(尤其是在于数据库打交道时)都还需要其他一些重要的类型。例如,JSON没有日期类型,这使得原本容易日期处理变得烦人。另外,JSON只有一种数字类型,无法区分浮点数和整数,更别区分32位和64位了。再者JSON无法表示其他一些通用类型,如正则表达式或函数。
4、MongoDB在保留了JSON基本键/值对特性的基础上,添加了其他一些数据类型。在不同的编程语言下,这些类型的确切表示有些许差异。下面说明了MongoDB支持的其他通用类型,以及如何正在文档中使用它们
#1、null:用于表示空或不存在的字段 d={\'x\':null} #2、布尔型:true和false d={\'x\':true,\'y\':false} #3、数值 d={\'x\':3,\'y\':3.1415926} #4、字符串 d={\'x\':\'egon\'} #5、日期 d={\'x\':new Date()} d.x.getHours() #6、正则表达式 d={\'pattern\':/^egon.*?nb$/i} 正则写在//内,后面的i代表: i 忽略大小写 m 多行匹配模式 x 忽略非转义的空白字符 s 单行匹配模式 #7、数组 d={\'x\':[1,\'a\',\'v\']} #8、内嵌文档 user={\'name\':\'egon\',\'addr\':{\'country\':\'China\',\'city\':\'YT\'}} user.addr.country #9、对象id:是一个12字节的ID,是文档的唯一标识,不可变 d={\'x\':ObjectId()}
5、_id和ObjectId
五CRUD操作
基础命令:
show dbs:查看所有的库
use 库名:切换库
db:设置库的变量
...help:查看使用方法
help: 库的使用方法 db.help: 集合的使用方法 db.表名.help: 文档的操作方法
库的操作:
#1、增 use config #如果数据库不存在,则创建数据库,否则切换到指定数据库。 #2、查 show dbs #查看所有 可以看到,我们刚创建的数据库config并不在数据库的列表中, 要显示它,我们需要向config数据库插入一些数据。 db.table1.insert({\'a\':1}) #3、删 use config #先切换到要删的库下 db.dropDatabase() #删除当前库
集合的操作:
#1、增 当第一个文档插入时,集合就会被创建 > use database1 switched to db database1 > db.table1.insert({\'a\':1}) WriteResult({ "nInserted" : 1 }) > db.table2.insert({\'b\':2}) WriteResult({ "nInserted" : 1 }) #2、查 > show tables table1 table2 #3、删 > db.table1.drop() true > show tables table2
文档的操作:
增:
#创建一个user的局部变量,这是一个JavaScript对象 user={ "name":"egon", \'is_nb\':true, \'hobbies\':[\'music\',\'read\',\'dancing\'] } db.userinfo.insert(user) db.userinfo.find() user1={ "name":"asb", \'is_sb\':true, \'hobbies\':[\'music\',\'read\',\'dancing\'] } user2={ "name":"egon", \'is_sb\':\'wxx\', \'hobbies\':[\'music\',\'read\',\'dancing\'] } user3={ "name":"egon3", \'is_sb\':false, \'hobbies\':[\'music\',\'read\',\'dancing\'] } db.userinfo.insertMany([user1,user2,user3])
改:
upate方法:
update() 方法用于更新已存在的文档。语法格式如下: db.collection.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document> } ) 参数说明:对比update db1.t1 set name=\'EGON\',sex=\'Male\' where name=\'egon\' and age=18; query : 相当于where条件。 update : update的对象和一些更新的操作符(如$,$inc...等,相当于set后面的 upsert : 可选,默认为false,代表如果不存在update的记录不更新也不插入,设置为true代表插入。 multi : 可选,默认为false,代表只更新找到的第一条记录,设为true,代表更新找到的全部记录。 writeConcern :可选,抛出异常的级别。 更新操作是不可分割的:若两个更新同时发送,先到达服务器的先执行,然后执行另外一个,不会破坏文档。
简单更新:
#1、最简单的更新就是用一个新的文档完全替换匹配的文档。这适用于大规模式迁移的情况。例如对下面的用户文档做 一个比较大的调整 > db.user.find().pretty() { "_id" : ObjectId("5a5b3a74c126b4b2cbb57e3f"), "name" : "egon", "girl_friends" : 33, "wives" : 23 } # 先找到要修改的对象 > var egon=db.user.findOne({\'name\':\'egon\'}) > egon { "_id" : ObjectId("5a5b3a74c126b4b2cbb57e3f"), "name" : "egon", "girl_friends" : 33, "wives" : 23 } # 修改 > egon.ralationship={\'girl_friends\':33,\'wives\':23} { "girl_friends" : 33, "wives" : 23 } > egon.username=egon.name egon # 删掉没有用的 > delete egon.girl_friends true > delete egon.wives true > delete egon.name true # 完成更新 > db.user.update({\'name\':\'egon\'},egon) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b3a74c126b4b2cbb57e3f"), "ralationship" : { "girl_friends" : 33, "wives" : 23 }, "username" : "egon" }
一个常见的在错误:
2、一个常见的错误 db.user.insert({\'name\':\'egon\',\'age\':10}) db.user.insert({\'name\':\'egon\',\'age\':20}) db.user.insert({\'name\':\'egon\',\'age\':30}) > db.user.find() { "_id" : ObjectId("5a5b3ecac126b4b2cbb57e43"), "name" : "egon", "age" : 10 } { "_id" : ObjectId("5a5b3ecbc126b4b2cbb57e44"), "name" : "egon", "age" : 20 } { "_id" : ObjectId("5a5b3ecdc126b4b2cbb57e45"), "name" : "egon", "age" : 30 } # 找到的是第二个egon,他要过生日,于是长了一岁 > var obj=db.user.findOne({\'name\':\'egon\',\'age\':20}) > obj.age++ # 我们的条件只定位到第二条记录这一条记录,所以更新是没问题的, > db.user.update({\'name\':\'egon\',\'age\':20},obj) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) # 我们的条件定位到了多条记录,而obj对象的_id是一定的,文档中的_id是唯一的,我们无法将三条改成同一个_id,所以报错 > db.user.update({\'name\':\'egon\'},obj) #报错 # 删掉后,更新成功,但默认改的是第一条 > delete obj._id true > obj { "name" : "egon", "age" : 21 } > db.user.update({\'name\':\'egon\'},obj) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b40f6c126b4b2cbb57e46"), "name" : "egon", "age" : 21 } { "_id" : ObjectId("5a5b40f6c126b4b2cbb57e47"), "name" : "egon", "age" : 20 } { "_id" : ObjectId("5a5b40f6c126b4b2cbb57e48"), "name" : "egon", "age" : 30 } > # 所以针对这种情况,我们还以_id作为条件保证只定位到我们想要修改的对象,并且比起随机字符串,_id的查找速度更快 > db.user.update({\'_id\':ObjectId("5a5b40f6c126b4b2cbb57e47")},obj) # ps: 改多条 > db.user.update({\'name\':\'egon\'},obj,{\'multi\':true}) WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0, "writeError" : { "code" : 9, "errmsg" : "multi update only works with $ operators" #只能用修改器 } }) > db.user.update({\'name\':\'egon\'},{$set:obj},{\'multi\':true}) WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 1 })
使用修改器:
通常文档只会有一部分需要更新。可以使用原子性的更新修改器,指定对文档中的某些字段进行更新。 更新修改器是种特殊的键,用来指定复杂的更新操作,比如修改、增加后者删除 #=======>1、"$set"修改器 > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "name" : "egon", "age" : 10 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 20 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30 } #完全覆盖原文档 > db.user.update({\'_id\':ObjectId("5a5b51a257147a04b81f2bb4")},{\'age\':111}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "age" : 111 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 20 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30 } > #$set只更新原文档的一部分 > db.user.update({\'_id\':ObjectId("5a5b51a257147a04b81f2bb5")},{\'$set\':{\'age\':222}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "age" : 111 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 222 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30 } # 有则更新,无则新增 > db.user.update({\'age\':30},{\'$set\':{\'addr\':{\'country\':\'China\',\'city\':\'BJ\'}}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.find() { "_id" : ObjectId("5a5b51a257147a04b81f2bb4"), "age" : 111 } { "_id" : ObjectId("5a5b51a257147a04b81f2bb5"), "name" : "egon", "age" : 222 } { "_id" : ObjectId("5a5b51a357147a04b81f2bb6"), "name" : "egon", "age" : 30, "addr" : { "country" : "China", "city" : "BJ" } } # 修改内嵌文档 db.user.update({\'age\':30},{\'$set\':{\'addr.country\':\'CHINA\'}}) # $unset db.user.update({\'age\':30},{\'$unset\':{\'addr\':\'什么值都行\'}}) #=======>2、增加和减少 "$inc"修改器用来增加已经有的键的值(负数则为减少),或者该键不存在那就创建一个。只能用于整型、长整型或双浮点 的值。 对于更新分析数据、因果关系、投票或者其他有变化数值的地方,使用这个都会非法方便 例如: > db.analytics.find() { "_id" : ObjectId("5a5b4cb2126d610970c553ab"), "url" : "http://www.baidu.com", "pv" : 2 } > db.analytics.update({\'url\':\'http://www.baidu.com\'},{\'$inc\':{\'pv\':1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.analytics.find() { "_id" : ObjectId("5a5b4cb2126d610970c553ab"), "url" : "http://www.baidu.com", "pv" : 3 } 使用修改器时,_id的值不能改变。其他的键值,包括其他的唯一索引的键,都是可以更改的。 注意:整个文档替换时可以改变_id #=======>3、往数组内添加元素 如果数组已经存在,"$push"会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组。 例如提交博客的评论 db.blog.insert({\'_id\':1,\'name\':\'alex意外死亡的真相\'}) db.blog.update({\'_id\':1},{\'$push\':{\'comments\':{"name":"egon","content":\'alex是谁???\'}}}) db.blog.update({\'_id\':1},{\'$push\':{\'comments\':{"name":"wxx","content":\'我去,真的假的\'}}}) db.blog.update({\'_id\':1},{\'$push\':{\'comments\':{"name":"yxx","content":\'吃喝嫖赌抽,欠下两个亿\'}}}) > db.blog.find().pretty() { "_id" : 1, "name" : "alex意外死亡的真相", "comments" : [ { "name" : "egon", "content" : "alex是谁???" }, { "name" : "wxx", "content" : "我去,真的假的" }, { "name" : "yxx", "content" : "吃喝嫖赌抽,欠下两个亿" } ] } # 一次加多个"$push" db.blog.update({\'_id\':1},{\'$push\':{\'comments\':{\'$each\':[{\'name\':\'oldboy\',\'content\':\'鼓掌\'},{\'name\':\'oldgirl\',\'content\':\'一鞠躬\'}]}}}) > db.blog.find().pretty() { "_id" : 1, "name" : "alex意外死亡的真相", "comments" : [ { "name" : "egon", "content" : "alex是谁???" }, { "name" : "wxx", "content" : "我去,真的假的" }, { "name" : "yxx", "content" : "吃喝嫖赌抽,欠下两个亿" }, { "name" : "oldboy", "content" : "鼓掌" }, { "name" : "oldgirl", "content" : "一鞠躬" } ] } # 限制大小"$slice",只留最后n个 db.blog.update({\'_id\':运维学python之爬虫中级篇MongoDBpython爬虫11--文件存储之非关系型数据库存储MongoDB