网络爬虫之MongoDB数据库的使用
Posted 不为乌合不从众
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络爬虫之MongoDB数据库的使用相关的知识,希望对你有一定的参考价值。
一.简洁
MongoDB是一款强大、灵活、且易于扩展的通用型数据库
1、易用性
MongoDB是一个面向文档(document-oriented)的数据库,而不是关系型数据库。
不采用关系型主要是为了获得更好得扩展性。当然还有一些其他好处,与关系数据库相比,面向文档的数据库不再有“行“(row)的概念取而代之的是更为灵活的“文档”(document)模型。
通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录来表现复杂的层级关系,这与现代的面向对象语言的开发者对数据的看法一致。
另外,不再有预定义模式(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基础知识
1、文档是MongoDB的核心概念。文档就是键值对的一个有序集{\'msg\':\'hello\',\'foo\':3}。类似于python中的有序字典。
需要注意的是:
#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个字节
三.MongoDB的安装与使用
【01】windows下安装首先去官网下载:
https://www.mongodb.com/dr/fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-3.4.4-signed.msi/download
【02】下载后安装,安装完毕后,将bin目录配置到系统的环境变量中去
【03】进入到安装目录下新建文件夹:
首先新建一个data目录:
D:\\ProfessionalSoftwares\\mongodb\\data
然后在data目录下新建两个文件夹:db和logs
最后在logs目录下新建一个文件:mongo.log
【04】现在我们将mongoDB做成一个服务然后在后台运行,这样我们每次开机就可以使用MongoDB服务端了:
首先以管理员身份运行cmd命令行,然后在命令行中执行如下的命令:(因为我配置了环境变量,所以可以直接使用
mongod命令)
mongod --bind_ip 0.0.0.0 --logpath D:\\ProfessionalSoftwares\\mongodb\\data\\logs\\mongo.log
--logappend --dbpath D:\\ProfessionalSoftwares\\mongodb\\data\\db --port 27017
--serviceName "MongoDB" --serviceDisplayName "MongoDB" --install --auth
上述--install参数表示将mongodb作为一个服务进行安装,--auth表示我客户端链接mongodb服务端时,需要输入指定的用户名和密码
安装完毕后,然后在刚才开启的管理员cmd命令行中启动MongoDB服务:
net start MongoDB 这里的MongoDB就是上面命令中的服务名:serviceName
另外如果需要关闭MongoDB这个服务,只需要使用net stop MongoDB即可
【05】最终操作完毕后,我们在浏览器中输入如下的网址:http://localhost:27017/
显示这句话就表示我们安装成功:
It looks like you are trying to access MongoDB over HTTP on the native driver port.
2.账号管理
数据库一般都是有相应的账号和密码的,MongoDB也有:
#账号管理,参考官网地址:https://docs.mongodb.com/master/tutorial/enable-authentication/
#1、创建账号,这里我们相当于创建了一个管理员账号root,然后在使用Robomongo登入数据库时,需要输入验证,
要不然会报错:Failed to execute "listdatabases" command. 笔者在这里坑了好久
默认情况下,MongoDB存在两个数据库:admin和test,我们创建管理员账户时,一般都在admin数据库下
注意下面的步骤,都是需要在管理员权限的cmd命令行下执行:
接着上面的说,在初始环境下登入到MongoDB数据库后,我们直接使用admin数据库,然后创建账号
use admin
db.createUser(
{
user: "root",
pwd: "cisco123",
roles: [ { role: "root", db: "admin" } ]
}
)
#2、重启数据库
创建完管理员账号和密码后,我们首先退出MongoDb数据库,然后将刚才安装的MongoDB数据库服务移除:
先关闭服务:net stop MongoDB
然后删除:
mongod --remove
移除后,我们再来重新安装服务,这次安装我们加入了--auth参数,
该参数表示,下次客户端连接MongoDB数据库时候,需要输入用户名和密码,如下所示:
mongod --bind_ip 0.0.0.0 --logpath D:\\ProfessionalSoftwares\\mongodb\\data\\logs\\mongo.log --logappend --dbpath D:\\ProfessionalSoftwares\\mongodb\\data\\db --port 27017 --serviceName "MongoDB" --serviceDisplayName "MongoDB" --install --auth
然后再来重启服务:
net start MongoDB
现在我们来使用账号和密码登入MongoDB数据库,注意后面我们需要指定相应的数据库名称:admin
因为我们的管理员账号和密码是在admin数据库下新建的:
#3、登录:注意使用双引号而非单引号
mongo --port 27017 -u "root" -p "cisco123" --authenticationDatabase "admin"
也可以在登录之后用db.auth("账号","密码")登录
mongo
use admin
db.auth("root","cisco123")
关于MongoDB的详细信息可以关注这篇博客:
#推荐博客:https://www.cnblogs.com/zhoujinyi/p/4610050.html
四 MongoDB的基本数据类型
1、在概念上,MongoDB的文档与javascript的对象相近,因而可以认为它类似于JSON。
JSON(http://www.json.org)是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚(其官网证明了这点),且仅包含六种数据类型。
2、这样有很多好处:易于理解、易于解析、易于记忆。然而从另一方面说,因为只有null、布尔、数字、字符串、
数字和对象这几种数据类型,所以JSON的表达能力有一定的局限。
3、虽然JSON具备的这些类型已经具有很强的表现力,但绝大数应用(尤其是在于数据库打交道时)都还需要其他一些重要的类型。
例如,JSON没有日期类型,这使得原本容易日期处理变得烦人。另外,JSON只有一种数字类型,无法区分浮点数和整数,更别区分32位和64位了。再者JSON无法表示其他一些通用类型,如正则表达式或函数。
4、MongoDB在保留了JSON基本键/值对特性的基础上,添加了其他一些数据类型。在不同的编程语言下,这些类型的确切表示有些许差异。下面说明了MongoDB支持的其他通用类型,以及如何使用它们
MongoDB支持的通用数据类型:
#1、null:用于表示空或不存在的字段
d={\'x\':null}
#2、布尔型:true和false,注意这里的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、内嵌文档,表示value也可以是一个文档,
如下面所示,我想访问country对应的value:user.addr.country即可:
user={\'name\':\'egon\',\'addr\':{\'country\':\'China\',\'city\':\'YT\'}}
user.addr.country
#9、对象id:是一个12字节的ID,是文档的唯一标识,不可变
d={\'x\':ObjectId()}
例如我使用MongoDB中的数据类型来定义一个用户记录:
{"_id":ObjectId(),"name":\'carson\',\'is_admin\':true, \'age\':18,\'salary\':18000, \'hobbies\':[1,\'a\'], \'comments\':null}
5、_id和ObjectId
五 CRUD操作
1、数据库操作(增删改查)
#1、新增数据库,需要说明的是,这里不需要像mysql数据库那样判断下数据库是否存在
#如果数据库config不存在,则创建数据库config;如果存在,那就切换到该数据库
这里使用use来新增数据库
use config
#2、查
show dbs #查看所有的数据库
可以看到,我们刚创建的数据库config并不在数据库的列表中,
MongoDB数据库和其他的不一样,如果一个新的数据库中没有任何集合,那么就不会展示它,
为了显示该数据库,我们需要向config数据库插入一些数据。
db.table1.insert({\'a\':1})
然后再来查询:
细心的你可能发现了,我们并没有给config数据库建立表结构,那为什么这里可以使用table1
原因在于mongodb压根不需要建立表,因为它根本没有表结构,我们往表中插入数据时,这个表就自动建立起来了
> db.table1.insert({\'a\':1})
WriteResult({ "nInserted" : 1 })
> show dbs
admin 0.000GB
config 0.000GB
database1 0.000GB
local 0.000GB
test 0.000GB
3.查看当前使用的数据库:db
一旦你登入mongodb,那么就会给db赋予一个默认值,默认是test数据库,即你默认是在test数据库下面
这样的好处在于,不管我们怎么切换数据,如果需要对该数据库进行操作,那我们只需要使用db来代表当前的数据库即可:因此use config 相当于 db=test
> db
database1
> use test
switched to db test
> db
test
>
#4、删除数据库,
use config #先切换到要删的库下
db.dropDatabase() #删除当前库
如下所示:
> db
config
> db.dropDatabase()
{ "dropped" : "config", "ok" : 1 }
>
5.另外如果我们需要查看数据库有哪些操作方法,直接使用db.help()即可:
2、集合操作(类似于Mysql中的表操作)
当我们讨论集合操作,其实就相当于讨论的是关系型数据库中的表操作,因为两者是对应的:
#1、增
当第一个文档插入时,集合就会被创建
> use database1
switched to db database1
> db.table1.insert({\'a\':1})
WriteResult({ "nInserted" : 1 })
> db.table2.insert({\'b\':1})
WriteResult({ "nInserted" : 1 })
>
# 查询数据库database1下面的表有哪几张,其实质就是查询数据库下的集合有哪些
> show tables;
table1
table2
# 删除表中的集合,使用drop()
> db.table1.drop()
true
> show tables;
table2
>
我们在为MongoDB中的表命名时候,需要按照一定的规则联系起来,如下图所示:
例如我们之间所做的博客系统,有用户表和评论表,我们使用外键将它们俩关联起来,但是在
MongoDB中,我们直接使用.的方式即可关联起来:
db.blog.user.inser()
db.blog.comments.insert()
在前面我们说过,MongoDB中默认会为我们的每条记录新增一个"_id"字段,该字段用来唯一标识某个集合中的一条文档,我们也可以自定义id属性,不过要注意,自定义时,这个字段要叫做 _id,如下所示:
> db.emp.insert({\'_id\':1, \'name\': \'carson\'}) # 自定义属性
WriteResult({ "nInserted" : 1 })
> db.emp.findOne()
{ "_id" : 1, "name" : "carson" }
> db.emp.insert({\'name\':\'alex\'})
WriteResult({ "nInserted" : 1 })
> db.emp.find()
{ "_id" : 1, "name" : "carson" }
{ "_id" : ObjectId("5a5c5d5d8c6f3bc41b4589b2"), "name" : "alex" }
> db.emp.insert({\'name\':\'eva\',\'salary\':18000})
WriteResult({ "nInserted" : 1 })
> db.emp.find()
{ "_id" : 1, "name" : "carson" }
{ "_id" : ObjectId("5a5c5d5d8c6f3bc41b4589b2"), "name" : "alex" }
{ "_id" : ObjectId("5a5c5dfb8c6f3bc41b4589b3"), "name" : "eva", "salary" : 18000 }
> db.emp.insert({\'_id\':1001, \'name\': \'alex666\'})
WriteResult({ "nInserted" : 1 })
> db.emp.find()
{ "_id" : 1, "name" : "carson" }
{ "_id" : ObjectId("5a5c5d5d8c6f3bc41b4589b2"), "name" : "alex" }
{ "_id" : ObjectId("5a5c5dfb8c6f3bc41b4589b3"), "name" : "eva", "salary" : 18000 }
{ "_id" : 1001, "name" : "alex666" }
3、文档操作
新增文档(新增记录)
#创建一个user的局部变量,这是一个JavaScript对象
user={
"name":"carson",
\'is_nb\':true,
\'hobbies\':[\'music\',\'read\',\'dancing\']
}
db.userinfo.insert(user)
db.userinfo.find()
user1={
"name":"tom",
\'is_sb\':true,
\'hobbies\':[\'music\',\'read\',\'dancing\']
}
user2={
"name":"eva",
\'is_sb\':\'wxx\',
\'hobbies\':[\'music\',\'read\',\'dancing\']
}
user3={
"name":"carson666",
\'is_sb\':false,
\'hobbies\':[\'music\',\'read\',\'dancing\']
}
# 一次性插入多条记录(文档)
db.userinfo.insertMany([user1,user2,user3])
当我们查询记录时,如果想让查询结果更好的展示,使用pretty()方法即可:
修改文档:
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 :可选,抛出异常的级别。
更新操作是不可分割的:若两个更新同时发送,先到达服务器的先执行,然后执行另外一个,不会破坏文档。
对文档的更新,我们使用.来进行操作:首先找到要更新的对象,然后使用.取出文档对应的属性,来看下面的例子:
# 先找到要修改的文档对象 carson 使用var关键字声明
> var carson=db.userinfo.findOne({\'name\':\'carson\'})
> carson
{
"_id" : ObjectId("5a5c5fb18c6f3bc41b4589b4"),
"name" : "carson",
"is_nb" : true,
"hobbies" : [
"music",
"read",
"dancing"
]
}
# 为carson文档对象新增一个属性relationship,其对应的值为:{\'girl_friends\':33,\'wives\':23}
可以看到value对应的值类型又可以是一个文档
> carson.relationship={\'girl_friends\':33,\'wives\':23}
{ "girl_friends" : 33, "wives" : 23 }
> carson
{
"_id" : ObjectId("5a5c5fb18c6f3bc41b4589b4"),
"name" : "carson",
"is_nb" : true,
"hobbies" : [
"music",
"read",
"dancing"
],
"relationship" : {
"girl_friends" : 33,
"wives" : 23
}
}
# 为carson文档对象新增一个属性username,其值为carson.name
> carson.username=carson.name
carson
> carson
{
"_id" : ObjectId("5a5c5fb18c6f3bc41b4589b4"),
"name" : "carson",
"is_nb" : true,
"hobbies" : [
"music",
"read",
"dancing"
],
"relationship" : {
"girl_friends" : 33,
"wives" : 23
},
"username" : "carson"
}
> carson.girl_friends=33
33
> carson
{
"_id" : ObjectId("5a5c5fb18c6f3bc41b4589b4"),
"name以上是关于网络爬虫之MongoDB数据库的使用的主要内容,如果未能解决你的问题,请参考以下文章
网络爬虫入门05分布式文件存储数据库MongoDB的基本操作与爬虫应用
python爬虫11--文件存储之非关系型数据库存储MongoDB