MongoDB数据库

Posted Afrafre

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MongoDB数据库相关的知识,希望对你有一定的参考价值。

爬虫流程:
1. 发送请求 -- 接收响应数据 -- 解析数据 -- 存储数据

1. requests: 专门用于处理HTTP请求
-- 对应HTTP的方法: GET,POST
-- GET 参数拼在地址栏  get(url,headers,params,cookies,timeout,proxies)
-- POST 参数放在请求体中,cookie and token  post(url,headers,data)
-- user-agent 两种方式都需要

2. selenium模块: 可以模拟手动操作,点击,滑动等等(动态页面)
-- 下载对应的浏览器驱动
-- webDriver.Chrome("驱动路径")
-- 请求页面 driver.get("url")
-- 解析数据: css_seletor, xpath
-- 等待元素加载: 显式等待(有条件,元素是满足某种状态,例如标签可以被点击)
                隐式等待(只设置时间)
-- 动作链: 点击,拖拽等

# 如果系统不带界面,需要配置无 ui模式 , 调入options
# 关键配置项: 无UI, 取消提示,不加载图片

3. 解析数据: re, bs4, (定位元素)xpath,css_selector 
-- find_all
-- 点语法 封装了find_all
-- find(name="aaa",attr={"id":"xx"},id="xxx",text="",_class="aa")(name标签名)
-- 过滤器:字符串,正则,数据,函数(自定义过滤规则),布尔

4. 定位元素
-- css_selector 类名 标签名 id 子类 后代
-- xpath: //全文范围查找   / 跟标签开始查找  //* 查所有
         谓语(限制条件) /a/p[@*] 有属性   /a/p/text()查文本 /a/p[@id='a' and @class]
         多路径:/a/p/ | /a/m/
         轴: 以某一个标签为起始,以相对方式查找标签 子代,兄弟

MongoDB注意事项

1. 文档中的键值对是有序的
2. 文档中的值不仅可以是字符串,还可以是其他几种数据类型, 键可以使用任意UTF-8字符
3. mongoDB区分类型和大小写
4. MongoDB文档不能有重复的键

文档键的命名规范:
1. 键不能含有 (空字符)。这个字符用来表示键的结尾。
2. . 和$有特别的以,只有在特定环境下才能使用
3. 以下划线"_" 开头的键是保留的

#下载地址: https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-4.0.8-signed.msi

MongoDB初识

爬虫的数据存储
mysql: 关系型数据库,基于硬盘存储
       当并发量太大时,mysql无法支撑,mysql 也支持分布式,但不是是强项
MongoDB: 高并发,分布式 -- 被应用于大数据领域
         是一款基于c++ 编写的分布式非关系型数据库
         noSQL: not only SQL
         最像关系型的非关系型数据库
Redis: 数据量小的话,效率高 (轻量级) --- 数据小,并发量高
    
Why MongoDB?? 数据量非常大
1. 安装mongodb
2. 初识MongoDB
--MongoDB: 集合(文档,类似字典)- 键值对
-- Mysql: 表(记录) - 字段
-- 默认端口: 27017

使用MongoDB

# 进入mongodb
-- 将mongo.exe 添加到环境变量
-- mongodb
# 创建账号
-- use admin
--  mongoDB 区分大小写(创建用户): db.createUser({"user":"root","pwd":"123",roles:[{"role":"root","db":"admin"}]}) # user和role也固定, role的value有几个值可以选择: root/readwrite/read
#  mongod.cfg 配置文件修改
-- # 开启验证,必须先建账户,注意缩进
-- #security: 
--   authorization: enabled
# 退出,重启服务,登录
-- 登录方式一: mongo -u "用户名" -p "密码"
-- 登录方式二: mongo (进入游客模式)
              use admin (切换到用户所在的数据库)
              db.auth("用户名","密码")  1 成功, 0 失败
# 小结:
mongodb 中用不同的数据库来区分权限,创建管理员在admin下创建
db 是一个全局变量,表示当前的数据库
db.createUser()是调用一个内部函数用于创建用户
每个账号可以具备多个角色
# 测试
1. 进入mongo,查看数据库
show dbs
2. 登录 mongo -u "root" -p "123"
   方式二: use admin
            db.auth("root","123")
3. 删除账号
db.dropUser('用户名')
4. 修改密码
db.changeUserPassword(用户名,新密码)

修改配置文档mongod.cfg

首先,将bind_ip改为0.0.0.0 (其他电脑可以访问,用于远程连接,如果bind_ip 是127.0.0.1,就只能本地访问)

技术图片

基本数据类型

# 概念上,MongoDB的文档和javascript 队形相近,类似JSON,JSON(http://www.json.org)是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚(其官网证明了这点),且仅包含六种数据类型。
# JSON缺点: 
-- 没有日期类型
-- 只有一种数字类型,无法区分浮点数和整数
-- 无法区分32 位和64位
--无法表示其他一些通用类型,如正则表达式或函数。
# 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':'jerry','addr':{'country':'China','city':'YT'}}
user.addr.country

#9、对象id:是一个12字节的ID,是文档的唯一标识,不可变
d={'x':ObjectId()}
#案例:
db.tb.insert({"a":null,"b":1.1,"c":true,"d":100,"e":"aaaaaa","f":new Date(),"g":/^jerry.*nice$/i,"h":[1,2],"j":{"name":"smallJerry"}})

库的操作

# 创建数据库,有则切换,无则创建
use 数据库名
# 删除数据库
db.dropDatebase()
# 查看数据库,如果没有数据,不显示数据库
show dbs

集合的操作

集合是一个存储元素的容器,类比mysql 中的表
# 首先进入库
use mydb
# 创建集合
db.user
# 查看集合,没有数据不显示
show tables 查看所有集合
show collections 所有集合
db.user.info.address  

# 删除集合
db.user.drop() 删除

db.dropUser("username")   只能登录admin 才能删除

文档操作

# 没有指定_id 则默认objectID, 不能重复,插入后不可变
# use ttt -新建库   db.user --新建表  然后给表添加数据
user1 = {
    "name":"iris",
    "age":13,
    "hobbies":["music","read","dance"],
    "addr":{
        "country":"China",
        "city":"biejing"
    }
}
# 插入单条
db.user.insert(user0)  
# 插入多条
db.user.insertMany([user1,user2,user3])
# save 覆盖或插入
db.user.save(user0)  -- 无则插入,有责覆盖
db.user.find()

查询数据

=============================比较运算=============================
find 查找所有匹配的数据, findone 查第一个
# = , != , > , < , >= , <=
#      $ne,  $gt,$lt, $gte,$lte
# {key:value}  代表相等
db.user.find({"name":"egon"})
# 查找除 egon以外
db.user.find({"name":{"$ne":"egon"}})
# id 大于2 的
db.user.find({"_id":{"$gt":2}})
# id 小于3
db.user.find("_id":{"$lt":3})

=============================逻辑运算=============================
# not and or
# MongoDB中: 多个条件是and 关系, "$or" 的条件放在[]内,"$not"
# and
db.user.find({"_id":{"$gte":2,"$lte":4}})
# or  id>=5 or name="cxx"  (外层$or 套列表)
db.user.find({
    "$or":[
        {"_id":{"$gte":5}},
        {"name":"cxx"}
    ]
})
# 基数,偶数
db.user.find({"_id":{"$mod":[2,1]}})  -- 基数
db.user.find("_id":{"$mod":[2,0]})  -- 偶数

# not , 取反
db.user.find("_id":{"$not":{"$mod":[2,1]}})

=============================成员运算=============================
# in , not in 
# $in   $nin
# age in [20,29,30]
db.user.find({"age":{"$in":[20,29,30]}})
# not in 
db.user.find({"age":{"nin":[20,29,30]}})
=============================正则匹配=============================
# 语法 /正则表达式/i
db.user.find({"name":/^e.*/i})

=============================指定字段显示========================
db.user.find({"_id":3},{"_id":0,"name":1,"age":1})
0 表示不显示, 1 为显示, 默认为0

=============================查询数组============================
# 多个兴趣爱好
# 有dancing爱好的人
db.user.find({'hobbies':'dancing '})
# 有dancing and tea 爱好的人
db.user.find({
    'hobbies':{
        "$all":['dancing','tea']
    }
})
# 第四个爱好是tea的人
db.user.find({"hobbies.3":"tea"})
# 查看所有人最后两个爱好
db.user.find({},{"hobbies":{"$slice":-2},"age":0,"_id":0...})
# 查看所有人第二个到第三个爱好
db.user.find({},{"hobbies":"slice":[1,2],"age":0....})
# db.user.find().pretty()   -- 格式化查询结果

===============================其他===============================
# 排序 sort 1 代表升序 -1代表降序
db.user.find().sort("name":1)
# 分页:limit代表取多少个文档,skip 代表跳过多少个文档
db.user.find().sort({"age":1}).limit(1).skip(2)
# 获取数量
db.user.find({"age":{"$gt":30}}).count()
db.user.count({"age":{"$gt":30}})

# 匹配key 的值为null 或者没有这个key
db.t2.insert({'a':10,'b':111})
db.t2.insert({'a':20})
db.t2.insert({'b':null})

db.t2.find({"b":null})  -- 查出后两个结果

修改数据

update() 用于更新已存在的文档,语法如下:
db.collection.update(
    <query>,    # 相当where条件
    <update>,   # update的对象和一些更新的操作符(相当set后面的内容)
    {
        upsert:<boolean>,  #默认false,不存在时不更新也不插入
        multi:<boolean>,   # 默认false,只更新找到的第一条记录,true为更新所有
        writeConcern: <document>  # 抛出异常的级别
    }
)
=========================================覆盖式=========================================
#注意:除非是删除,否则_id是始终不会变的
#1、覆盖式:
db.user.update({'age':20},{"name":"Wxx","hobbies_count":3})
是用{"_id":2,"name":"Wxx","hobbies_count":3}覆盖原来的记录
#2、用一个新的文档完全替换匹配的文档,适用于大规模式迁移的情况
var obj=db.user.findOne({"_id":2})
obj.username = obj.name + 'SB'
obj.hobbies_count++
delete obj.age
# 结果: {"_id":2,"name":"Wxx","hobbies_count":4,"username":"WxxSB"}
db.user.update({"_id":2},obj)

=========================================设置:$set=========================================
# 1. update db1.user set name='egon' where id=2
db.user.update({"_id":2},{"$set":{"name":"egon"}})
# 没匹配成功就新增一条,有就修改set 指定的字段
db.user.update({"_id":2},{"$set":{"name":"iris","age":38}},{"upsert":true})
# 匹配多条  {"multi":true}
db.user.update({"_id":{"$gt":4}},{"$set":{"age":38}},{"multi":true})
 # 修改嵌入文档
db.user.update({"name":"alex"},{"$set":{"addr.country":"Japan"}})
# 修改名为alex 的人的第二个爱好成pingpang
db.user.update({"name":"alex"},{"$set":{"hobbies.1":'pingpang'}})
# 删除alex 的爱好, $unset
db.user.update({"name":"alex"},{"$unset":{"hobbies":""}})

=========================================增加和减$inc=====================================
 # 1. 所有人年龄增加一岁
 db.user.update({},
                {
                    "$inc":{"age":1}
                },{
                    "multi":true
                })
    
 #2. 所有人年龄减少2岁
db.user.update({},
               {
                   "$inc":{"age":-2}
               },
               {
                   "multi":true
               })
========================================= 添加,删除数组元素===============================   
# 数据添加元素: $push
# 为alex 添加爱好 read
db.user.update({"name":"alex"},{"$push":{"hobbies":"read"}})
#添加多个爱好 tea,dancing ($push, $each)
db.user.update({"name":"alex"},{"$push":{"hobbies":{"$each":['tea','dancing']}}}
# 开头或结尾删除元素: $pop
{"$pop":{"key":1}}  从数组末尾删除一个元素, -1 从头部删除
db.user.update({"name":"egon"},{"$pop":{"hobbies":1}})
#按条件删除元素: $pull 把符号条件的统统删除,$pop只能从两端删除
db.user.update({"addr.country":"China"},{"$pull":{"hobbies":"read"}},{"multi":true})
    
=====================================避免添加重复:$addToSet===========================
db.urls.insert({"_id":1,"urls":[]})
# 更新一条,自动去除重复
db.urls.update({"_id":1},{"addToSet":{"urls":'http://www.baidu.com'}})
db.urls.update({"_id":1},{"addToSet":{"urls":'http://www.baidu.com'}})
# 更新添加多条
               db.urls.update({"_id":1},{
                   "addToSet":{
                       "urls":{
                           "$each":[
                              'http://www.baidu.com',
                            'http://www.xxxx.com'    # 出现重复,自动去重
                           ]
                       }
                   }
               })
=====================================其他========================================
#1、了解:限制大小"$slice",只留最后n个

db.user.update({"_id":5},{
    "$push":{"hobbies":{
        "$each":["read",'music','dancing'],
        "$slice":-2
    }
    }
})

#2、了解:排序The $sort element value must be either 1 or -1"
db.user.update({"_id":5},{
    "$push":{"hobbies":{
        "$each":["read",'music','dancing'],
        "$slice":-1,
        "$sort":-1
    }
    }
})

#注意:不能只将"$slice"或者"$sort"与"$push"配合使用,且必须使用"$eah"
               

删除数据

# 删除多个中的一个
db.user.deleteOne({'age':19})
# 删除满足条件的多个
db.user.deleteMany({"addr.country":"China"})
# 删全部
db.user.deleteMany({})

Mongo DB 数据库


# 用户管理 --- 登录(先创建用户在修改配置文件)


--

MongoDB 命令使用:


# use admin: 切换数据库,如果不存在会自动创建
db: 当前数据库
db.user  相当建了一个表
db.user.insert({"_id":1,"name":"egon"})
show tables  -- show dbs
exit 退出
db.help 查看帮助信息
db.dropDatabase()  删除数据库





记录的操作
db.user.insertMany([
    {"_id":3,"name":"octivia"},
    {"_id":4,"name":"lily"}
])
db.user.find() -- 查询所有 == select * from user

db.user.find({"name":"octivia","_id":3})  -- 条件可单可多






    
save()
db.user.save({"_id":6,"name":"clerk"})  # 如果id存在就覆盖,id不同就新增

mongoDB的语法就是JS 语法,所以json支持的数据类型mongo都支持
字符串,整型,浮点,列表,字典,布尔(小写), new Date(), /^a.*c$/i

_id和ObjectId

"""
MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任意类型,默认是个ObjectId对象。
在一个集合里,每个文档都有唯一的“_id”,确保集合里每个文档都能被唯一标识。
不同集合"_id"的值可以重复,但同一集合内"_id"的值必须唯一

#1、ObjectId
ObjectId是"_id"的默认类型。因为设计MongoDb的初衷就是用作分布式数据库,所以能够在分片环境中生成
唯一的标识符非常重要,而常规的做法:在多个服务器上同步自动增加主键既费时又费力,这就是MongoDB采用
ObjectId的原因。
ObjectId采用12字节的存储空间,是一个由24个十六进制数字组成的字符串
    0|1|2|3|   4|5|6|     7|8    9|10|11    
    时间戳      机器      PID    计数器
如果快速创建多个ObjectId,会发现每次只有最后几位有变化。另外,中间的几位数字也会变化(要是在创建过程中停顿几秒)。
这是ObjectId的创建方式导致的,如上图

时间戳单位为秒,与随后5个字节组合起来,提供了秒级的唯一性。这个4个字节隐藏了文档的创建时间,绝大多数驱动程序都会提供
一个方法,用于从ObjectId中获取这些信息。

因为使用的是当前时间,很多用户担心要对服务器进行时钟同步。其实没必要,因为时间戳的实际值并不重要,只要它总是不停增加就好。
接下来3个字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样就可以保证不同主机生成不同的ObjectId,不产生冲突

接下来2个字节确保了在同一台机器上并发的多个进程产生的ObjectId是唯一的

前9个字节确保了同一秒钟不同机器不同进程产生的ObjectId是唯一的。最后3个字节是一个自动增加的 计数器。确保相同进程的同一秒产生的
ObjectId也是不一样的。

#2、自动生成_id
如果插入文档时没有"_id"键,系统会自帮你创建 一个。可以由MongoDb服务器来做这件事。
但通常会在客户端由驱动程序完成。这一做法非常好地体现了MongoDb的哲学:能交给客户端驱动程序来做的事情就不要交给服务器来做。
这种理念背后的原因是:即便是像MongoDB这样扩展性非常好的数据库,扩展应用层也要比扩展数据库层容易的多。将工作交给客户端做就
减轻了数据库扩展的负担。
"""
0|1|2|3|       4|5|6|             7|8        9|10|11    
 时间戳          机器              PID         计数器
秒级的唯一性    主机的唯一标识符    不同进程     自动增加的 计数器

以上是关于MongoDB数据库的主要内容,如果未能解决你的问题,请参考以下文章

mongodb关联查询

无法在 MongoDB(猫鼬)文档中追加数组

ios - Heroku 和 MongoDb 上的自定义解析服务器错误 3080:JSON 文本没有以数组或对象开头,并且允许未设置片段的选项

mongodb snappy 压缩数据大小与存储大小

在 Spring MongoDB 的 ReplaceRoot 管道阶段使用 $mergeObjects

文档数据库:冗余数据、参考资料等(特别是 MongoDB)