Python爬虫第四课 appium和第MongoDB数据库
appium环境安装
学习目标
- 了解 appium环境的搭建
2.1 环境安装
以win10为例
2.1.1 安装node.js
- 点击进入 https://nodejs.org/zh-cn/
- 点击下载安装包
- 双击安装包,然后同样一路狂点下一步
- 安装完成后在cmd终端中输入
node -v
,显示版本号则表示安装成功
2.1.2 安装java JDK
- 点击进入官网下载页面 https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
- 注意下载位置,点击同意协议,下载相应的版本
- 双击安装包,然后同样一路狂点下一步
!!!注意安装位置,默认是C:\Program Files\Java,尽量不要修改。
添加环境变量
CLASSPATH
.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
注意上边最开始有个点
JAVA_HOME
C:\Program Files\Java\jdk1.8.0_191
Path
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
在cmd终端中输入java和javac不报异常,说明安装成功
2.1.3 安装android SDK
- 进入网站 https://www.androiddevtools.cn/
- 依次点击AndroidSDK工具>>SDK Tools>>会跳转到以下界面,Windows建议选择.exe后缀
- 下载后安装
注意此处,点击第一项,为所有用户安装
如果不小心关闭了,或者没有自动打开上边的界面,点击安装目录下的SDK Mansger.exe文件,同样可以打开,已经自动打开的忽略此步。
下载完成后,你的sdk文件夹下,应该是这样的
安装环境变量
ANDROID_HOME
C:\...\Android\android-sdk
Path
%ANDROID_HOME%\tools
%ANDROID_HOME%\platform-tools
2.1.4 安装Appium
- 需要科学上网 http://appium.io/
- 点击下载并安装
2.1.5 安装夜神模拟器
- 下载并安装夜神模拟器 https://www.yeshen.com/
- 首先,下载完成后不要忙着打开夜神模拟器,先确保你的电脑Hyper-V是关闭的,否则启动模拟器时部分电脑会蓝屏重启。
- 把安卓sdk中的adb.exe复制两份,其中一个改名为nox_adb.exe后,放到夜神模拟器安装目录的bin目录下覆盖原文件
2.2 联调测试环境
2.2.1 开启并设置夜神安卓模拟器
2.2.2 adb命令建立连接
在夜神模拟器安装路径下的bin目录下执行cmd进入终端
输入
adb devices
后,出现提示List of devices attached * daemon not running; starting now at tcp:5037 * daemon started successfully
再输入
nox_adb.exe connect 127.0.0.1:62001
后,出现提示connected to 127.0.0.1:62001
最后再次输入
adb devices
后,出现提示List of devices attached 127.0.0.1:62001 device
2.2.3 开启Appium并配置运行
获取app包名和进程名
打开夜神模拟器中的浏览器
在adb连接正确的情况下,在夜神模拟器安装目录的bin目录下的cmd中输入
adb shell
进入adb shell后输入
dumpsys activity | grep mFocusedActivity
com.android.browser
就是app包名.BrowserActivity
就是进程名
- 启动Appium,点击start server
- 点击放大镜进入并输入测试配置
配置参数的来源
platformName 系统名
Android
platformVersion 系统版本
4.4.2
deviceName 手机型号
SM-G955F
appPackage app的包名
com.android.browser
appActivity app的进程名
.BrowserActivity
- 关闭夜神模拟器中的浏览器,点击右下角的start session查看运行结果
2.2.4 干的漂亮!环境搭建成功
小结
- 了解 appium环境的搭建
Copyright © 知乎 @程序员小景 授权 @望京14k 用于 @黑马 python爬虫课程5.0
Copyright © @望京14k 授权 用于 @黑马 python爬虫课程5.0
利用appium自动控制移动设备并提取数据
学习目标
- 了解 appium-python-client模块定位元素以及提取其文本内容的方法
- 了解 appium-python-client模块控制滑动动作的方法
以控制抖音app滑动并获取抖音短视频发布者昵称和点赞数等信息为例
2.1 安装appium-python-client模块并启动已安装好的环境
2.1.1 安装appium-python-client模块
在window的虚拟环境下执行pip install appium-python-client
2.1.2 启动夜神模拟器,进入夜神模拟器所在的安装路径的bin目录下,进入cmd终端,使用adb命令建立adb server和模拟器的连接
adb devices
C:\Program Files (x86)\Nox\bin>adb devices
List of devices attached
* daemon not running; starting now at tcp:5037
* daemon started successfully
nox_adb.exe connect 127.0.0.1:62001
C:\Program Files (x86)\Nox\bin>nox_adb.exe connect 127.0.0.1:62001
already connected to 127.0.0.1:62001
adb devices
C:\Program Files (x86)\Nox\bin>adb devices
List of devices attached
127.0.0.1:62001 device
2.1.3 启动appium-desktop,点击start server启动appium服务
[Appium] Welcome to Appium v1.10.0
[Appium] Appium REST http interface listener started on 0.0.0.0:4723
2.1.4 利用上一小节所学习的内容获取Desired Capabilities参数
- 获取模拟设备的型号
- 打开设置——关于平板电脑
- 查看型号,获取模拟设备的型号
- 获取app包名称 以及 app进程名
- 打开模拟器中的抖音短视频app
- 在adb连接正确的情况下,在夜神模拟器安装目录的bin目录下的cmd中输入
adb shell
- 进入adb shell后输入
dumpsys activity | grep mFocusedActivity
- ``com.ss.android.ugc.aweme`就是app包名
.main.MainActivity
就是进程名 注意前边有个点.
2.2 初始化以及获取移动设备分辨率
完成代码如下,并运行代码查看效果:如果模拟器中抖音app被启动,并打印出模拟设备的分辨率则成功
from appium import webdriver# 初始化配置,设置Desired Capabilities参数
desired_caps = {'platformName': 'Android','deviceName': 'SM-G955F','appPackage': 'com.ss.android.ugc.aweme','appActivity': '.main.MainActivity'
}
# 指定Appium Server
server = 'http://localhost:4723/wd/hub'
# 新建一个driver
driver = webdriver.Remote(server, desired_caps)
# 获取模拟器/手机的分辨率(px)
width = driver.get_window_size()['width']
height = driver.get_window_size()['height']
print(width, height)
移动设备分辨率
driver.get_window_size()[‘width’]
driver.get_window_size()[‘height’]
2.3 定位元素以及提取文本的方法
2.3.1 点击appium desktop右上角的放大镜图标
如图填写配置,并点击start session
2.3.2 定位界面的使用方法如下图所示
2.3.3 点击短视频的作者名字,查看并获取该元素的id
2.3.4 在python使用代码通过元素id获取该元素的文本内容
实例化appium driver对象后添加如下代码,运行并查看效果
# 获取视频的各种信息:使用appium desktop定位元素
print(driver.find_element_by_id('bc').text) # 发布者名字
print(driver.find_element_by_id('al9').text) # 点赞数
print(driver.find_element_by_id('al_').text) # 留言数
print(driver.find_element_by_id('a23').text) # 视频名字,可能不存在,报错
定位元素及获取其文本内容的方法
- driver.find_element_by_id(元素的id).text
- driver.find_element_by_xpath(定位元素的xpath规则).text
2.4 控制抖音app滑动
2.4.1 appium滑动的函数
从(start_x, start_y)滑动到(end_x, end_y)
- driver.swipe(start_x, start_y, end_x, end_y)
2.4.2 控制抖音app滑动的代码实现
start_x = width // 2 # 滑动的起始点的x坐标,屏幕宽度中心点
start_y = height // 3 * 2 # 滑动的起始点的y坐标,屏幕高度从上开始到下三分之二处
distance = height // 2 # y轴滑动距离:屏幕高度一半的距离
end_x = start_x # 滑动的终点的x坐标
end_y = start_y-distance # 滑动的终点的y坐标
# 滑动
driver.swipe(start_x, start_y, end_x, end_y)
2.5 整理并完成自动滑动的代码
import time
from appium import webdriverclass DouyinAction():"""自动滑动,并获取抖音短视频发布者的id"""def __init__(self, nums:int=None):# 初始化配置,设置Desired Capabilities参数self.desired_caps = {'platformName': 'Android','deviceName': 'SM-G955F','appPackage': 'com.ss.android.ugc.aweme','appActivity': '.main.MainActivity'}# 指定Appium Serverself.server = 'http://localhost:4723/wd/hub'# 新建一个driverself.driver = webdriver.Remote(self.server, self.desired_caps)# 获取模拟器/手机的分辨率(px)width = self.driver.get_window_size()['width']height = self.driver.get_window_size()['height']print(width, height)# 设置滑动初始坐标和滑动距离self.start_x = width//2 # 屏幕宽度中心点self.start_y = height//3*2 # 屏幕高度从上开始到下三分之二处self.distance = height//2 # 滑动距离:屏幕高度一半的距离# 设置滑动次数self.nums = numsdef comments(self):# app开启之后点击一次屏幕,确保页面的展示time.sleep(2)self.driver.tap([(500, 1200)], 500)def scroll(self):# 无限滑动i = 0while True:# 模拟滑动print('滑动ing...')self.driver.swipe(self.start_x, self.start_y,self.start_x, self.start_y-self.distance)time.sleep(1)self.get_infos() # 获取视频发布者的名字# 设置延时等待time.sleep(4)# 判断是否退出if self.nums is not None and self.nums == i:breaki += 1def get_infos(self):# 获取视频的各种信息:使用appium desktop定位元素print(self.driver.find_element_by_id('bc').text) # 发布者名字print(self.driver.find_element_by_id('al9').text) # 点赞数print(self.driver.find_element_by_id('al_').text) # 留言数print(self.driver.find_element_by_id('a23').text) # 视频名字,可能不存在,报错# # 点击【分享】坐标位置 671,1058# self.driver.tap([(671, 1058)])# time.sleep(2)# # 向左滑动露出 【复制链接】 580,1100 --> 200, 1100# self.driver.swipe(580,1100, 20, 200, 1100)# # self.driver.get_screenshot_as_file('./a.png') # 截图# # 点击【复制链接】 距离右边60 距离底边170 720-60,1280-170# self.driver.tap([(660, 1110)])# # self.driver.get_screenshot_as_file('./b.png') # 截图def main(self):self.comments() # 点击一次屏幕,确保页面的展示time.sleep(2)self.scroll() # 滑动if __name__ == '__main__':action = DouyinAction(nums=5)action.main()
至此,可以参考爬虫5.0课程项目库,使用fiddler等抓包工具,利用appium+mitmproxy+wget等python模块自动获取抖音视频文件
2.6 关于模拟式移动端爬虫的参考阅读
https://zhuanlan.zhihu.com/appium # 你们猜这是谁写的,不服你们也写,以后课件就用咱们班同学的!
https://github.com/butomo1989/docker-android
https://blog.csdn.net/weixin_42620645/article/details/83828863
https://blog.csdn.net/weixin_39211232/article/details/83410130#Android_16
https://www.jianshu.com/p/bf1ca3d4ac76
http://www.testclass.net/appium/
小结
- 了解 appium-python-client模块定位元素以及提取其文本内容的方法
- 了解 appium-python-client模块控制滑动动作的方法
Mongodb的介绍和安装
学习目标
- 了解 非关系型数据库的优势
- 了解 mongodb的安装
1. mongodb的介绍
1.1 什么是mongodb
- mongodb 是一个功能最丰富的NoSQL非关系数据库。由 C++ 语言编写。
- mongodb 本身提供S端存储数据,即server;也提供C端操作处理(如查询等)数据,即client。
1.2 SQL和NoSQL的主要区别
- 在SQL中层级关系: 数据库>表>数据
- 而在NoSQL中则是: 数据库>集合>文档
1.2.1 数据之间无关联性
- SQL中如何需要增加外部关联数据的话,规范化做法是在原表中增加一个外键,关联外部数据表。
- NoSQL则可以把外部数据直接放到原数据集中,以提高查询效率。缺点也比较明显,对关联数据做更新时会比较麻烦。
- SQL中在一个表中的每条数据的字段是固定的。而NoSQL中的一个集合(表)中的每条文档(数据)的key(字段)可以是互不相同的。
1.2.2 拓展阅读
https://www.cnblogs.com/jeakeven/p/5402095.html
1.3 mongodb作为非关系型数据库相较于关系型数据库的优势
易扩展: NoSQL数据库种类繁多, 但是一个共同的特点都是去掉关系数据库的关系型特性。 数据之间无关系, 这样就非常容易扩展
大数据量,高性能: NoSQL数据库都具有非常高的读写性能, 尤其在大数据量下表现优秀。 这得益于它的非关系性,数据库的结构简单
灵活的数据模型: NoSQL无需事先为要存储的数据建立字段, 随时可以存储自定义的数据格式。 而在关系数据库中, 增删字段是一件非常麻烦的事情。 如果是非常大数据量的表, 增加字段简直就是一个噩梦
2. mongodb的安装
以ubuntu18.04为例
mongodb具有两种安装方式:命令安装 或 源码安装
2.1 命令安装
在ubuntu中使用apt-get工具安装
sudo apt-get install -y mongodb-org
或参考官方文档 https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
2.2 源码安装
2.2.1 选择相应版本和操作系统并下载
https://www.mongodb.com/download-center/community?jmp=docs
2.2.2 解压
tar -zxvf mongodb-linux-x86_64-ubuntu1804-4.0.3.tgz
2.2.3 移动到/usr/local/目录下
sudo mv -r mongodb-linux-x86_64-ubuntu1804-4.0.3/ /usr/local/mongodb
2.2.4 在shell的初始化脚本.bashrc中添加mongodb可执行文件到环境变量PATH中
a. 进入.bashrc文件中
cd ~
sudo vi .bashrc
b. 在.bashrc文件的最后添加:
export PATH=/usr/local/mongodb/bin:$PATH
3. mongodb的官方文档
https://docs.mongodb.com/manual/introduction/
小结
- 了解 非关系型数据库的优势
- 易扩展
- 高性能
- 灵活的数据字段
- 了解 mongodb的安装
- sudo apt-get install -y mongodb-org
mongodb的简单使用
学习目标
- 掌握 服务端的启动
- 掌握 客户端的使用
- 掌握 mongodb的数据库和集合命令
- 了解 文档中的_id字段
1. mongodb服务端的启动
- 默认端口:27017
- 默认配置文件的位置:/etc/mongod.conf
- 默认日志的位置:/var/log/mongodb/mongod.log
mongodb服务端启动分别两种方式:
- 本地测试方式的启动(只具有本地数据增删改查的功能)
- 生产环境启动(具有完整的全部功能)
1.1 测试方式启动
- 启动: sudo service mongod start (sudo service mongod start)
- 停止: sudo service mongod stop
- 重启: sudo service mongod restart
1.2 生产环境正式的启动方式
启动: sudo mongod [–auth --dbpath=dbpath --logpath=logpath --append --fork] [-–f logfile ]
- 只以 sudo mongod 命令启动时,默认将数据存放在了 /data/db 目录下,需要手动创建
- –dbpath: 指定数据库的存放路径
- –logpath: 指定日志的存放路径
- –append: 或–logappend 设置日志的写入形式为追加模式
- –fork: 或-fork 开启新的进程运行mongodb服务
- –f: 或-f 配置文件路径(可以将上述配置信息写入文件然后通过该文件中的参数进行加载启动)
- –auth: 以权限认证的方式启动,我们会在后边的课程中学习该内容
1.3 查看是否启动成功
ps aux | grep mongod
2. 启动mongodb的客户端:进入mongo shell
- 启动本地客户端: mongo
- 查看帮助:mongo –help
- 退出:exit或者ctrl+c
3. mongodb的简单使用
开启mongodb server的情况下,在进入mongo shell后,就可以做简单的使用了
3.1 mongodb数据库的命令
- 查看当前的数据库:db(没有切换数据库的情况下默认使用test数据库)
- 查看所有的数据库:show dbs /show databases
- 切换数据库:use db_name
- db_name为show dbs后返回的数据库名
- 删除当前的数据库:db.dropDatabase()
3.2 mongodb集合的命令
- 无需手动创建集合:
向不存在的集合中第一次添加数据时,集合会自动被创建出来 - 手动创建集合:
- db.createCollection(name,options)
- db.createCollection(“stu”)
- db.createCollection(“sub”, { capped : true, size : 10 } )
- 参数capped:默认值为false表示不设置上限,值为true表示设置上限
- 参数size:集合所占用的字节数。 当capped值为true时,需要指定此参数,表示上限大小,当文档达到上限时, 会将之前的数据覆盖,单位为字节
- 查看集合:show collections
- 删除集合:db.集合名称.drop()
- 检查集合是否设定上限: db.集合名.isCapped()
3.3 简单练习
在mongo shell中输入下列命令,查看结果
show dbs
use test
show collections
db
db.stu.insert({'name':'郭靖', 'age':22})
show dbs
show collections
db.stu.find()
db.stu.drop()
show collections
db.dropDatabase()
show dbs
exit
3.3 mongodb中常见的数据类型(了解)
3.3.1 常见类型
- Object ID: 文档ID/数据的ID,数据的主键
- String: 字符串,最常用,必须是有效的UTF-8
- Boolean: 存储一个布尔值,true或false
- Integer: 整数可以是32位或64位,这取决于服务器
- Double: 浮点数
- Arrays: 数组/列表
- Object: mongodb中的一条数据/文档,即文档嵌套文档
- Null: 存储null值
- Timestamp: 时间戳,表示从1970-1-1到现在的总秒数
- Date: 存储当前日期或时间的UNIX时间格式
3.3.2 注意点
每个文档都有一个属性,为_id,保证每个文档的唯一性,mongodb默认使用_id作为主键
- 可以手动设置_id的值,如果没有提供,那么MongoDB为每个文档提供了一个独特的_id, 类型为objectID
objectID是一个12字节的十六进制数,每个字节两位,一共是24位的字符串:
- 前4个字节为当前时间戳
- 接下来3个字节的机器ID
- 接下来的2个字节中MongoDB的服务进程id
- 最后3个字节是简单的增量值
小结
- 服务端的启动
- sudo mongod --dbpath=数据库路径
- 进入mongo shell客户端
- mongo
- mongodb的数据库和集合命令
- show dbs
- use db_name
- show collections
- db
- db.集合名.drop()
- db.dropDatabase()
- exit
- 了解文档中的_id字段
Mongodb的的增删改查
学习目标
- 掌握 mongodb插入数据的方法
- 掌握 mongodb保存数据的方法
- 掌握 mongodb查询数据的方法
- 掌握 mongodb查询结果的处理方法
- 掌握 mongodb更新数据的方法
- 掌握 mongodb删除数据的方法
1. mongodb插入数据
命令:db.集合名称.insert(document)
db.stu.insert({name:'gj', gender:1})
db.stu.insert({_id:"20170101", name:'gj', gender:1})
插文档时,如果不指定_id参数,MongoDB会为文档自动分配一个唯一的ObjectId
2. mongodb的保存
命令:db.集合名称.save(document)
db.stu.save({_id:'20170101', name:'gj', gender:2})
db.stu.save({name:'gj', gender:2})
db.stu.find()
如果文档的_id已经存在则修改,如果_id不存在则添加
3 mongodb的查询
命令:db.集合名称.find()
可以使用以下数据进行练习
db.stu.insert([{"name" : "郭靖", "hometown" : "蒙古", "age" : 20, "gender" : true },
{"name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false },
{"name" : "华筝", "hometown" : "蒙古", "age" : 18, "gender" : false },
{"name" : "黄药师", "hometown" : "桃花岛", "age" : 40, "gender" : true },
{"name" : "段誉", "hometown" : "大理", "age" : 16, "gender" : true },
{"name" : "段王爷", "hometown" : "大理", "age" : 45, "gender" : true },
{"name" : "洪七公", "hometown" : "华筝", "age" : 18, "gender" : true }])
3.1 简单查询
方法find(): 查询
db.集合名称.find({条件文档})
方法findOne():查询,只返回第一个
db.集合名称.findOne({条件文档})
方法pretty(): 将结果格式化;不能和findOne()一起使用!
db.集合名称.find({条件文档}).pretty()
3.2 比较运算符
- 等于: 默认是等于判断, 没有运算符
- 小于:
$lt (less than)
- 小于等于:
$lte (less than equal)
- 大于:
$gt (greater than)
- 大于等于:
$gte
- 不等于:
$ne
查询年龄大于18的所有学生
db.stu.find({age:{$gte:18}})
3.3 逻辑运算符
逻辑运算符主要指与、或逻辑
- and:在json中写多个条件即可
查询年龄大于或等于18, 并且性别为true的学生
db.stu.find({age:{$gte:18},gender:true})
- or:使用$or, 值为数组, 数组中每个元素为json
查询年龄大于18, 或性别为false的学生
db.stu.find({$or:[{age:{$gt:18}},{gender:false}]})查询年龄大于18或性别为男生, 并且姓名是郭靖
db.stu.find({$or:[{age:{$gte:18}},{gender:true}],name:'gj'})
3.4 范围运算符
使用$in
, $nin
判断数据是否在某个数组内
查询年龄为18、 28的学生
db.stu.find({age:{$in:[18,28,38]}})
3.5 支持正则表达式
使用$regex编写正则表达式
查询name以'黄'开头的数据
db.stu.find({name:{$regex:'^黄'}})
3.6 自定义查询
mongo shell 是一个js的执行环境
使用$where 写一个函数, 返回满足条件的数据
查询年龄大于30的学生
db.stu.find({$where:function() {return this.age>30;}
})
3.7 skip和limit
- 方法limit(): 用于读取指定数量的文档
db.集合名称.find().limit(NUMBER)
查询2条学生信息
db.stu.find().limit(2)
- 方法skip(): 用于跳过指定数量的⽂档
db.集合名称.find().skip(NUMBER)
db.stu.find().skip(2)
- 同时使用
db.stu.find().limit(4).skip(5)
db.stu.find().skip(5).limit(4)
注意:先使用skip在使用limit的效率要高于前者
3.8 投影
在查询到的返回结果中, 只选择必要的字段
命令:db.集合名称.find({},{字段名称:1,...})
参数为字段与值, 值为1表示显示, 值为0不显
特别注意:
- 对于_id列默认是显示的, 如果不显示需要明确设置为0
- 对于其他不显示的字段不能设置为0
db.stu.find({},{_id:0,name:1,gender:1})
3.9 排序
方法sort(), 用于对查询结果按照指定的字段进行排序
命令:db.集合名称.find().sort({字段:1,...})
参数1为升序排列
参数-1为降序排列
根据性别降序, 再根据年龄升序
db.stu.find().sort({gender:-1,age:1})
3.10 统计个数
方法count()用于统计结果集中文档条数
命令:db.集合名称.find({条件}).count()
命令:db.集合名称.count({条件})
db.stu.find({gender:true}).count()
db.stu.count({age:{$gt:20},gender:true})
4 mongodb的更新
db.集合名称.update({query}, {update}, {multi: boolean})
- 参数query:查询条件
- 参数update:更新操作符
- 参数multi:可选,默认是false,表示只更新找到的第一条数据,值为true表示把满足条件的数据全部更新
db.stu.update({name:'hr'},{name:'mnc'}) # 全文档进行覆盖更新
db.stu.update({name:'hr'},{$set:{name:'hys'}}) # 指定键值更新操作
db.stu.update({},{$set:{gender:0}},{multi:true}) # 更新全部
注意:“multi update only works with $ operators”
- multi参数必须和$set一起使用!
5 mongodb的删除
db.集合名称.remove({query}, {justOne: boolean})
- 参数query:可选,删除的⽂档的条件
- 参数justOne:可选, 如果设为true或1,则只删除一条,默认false,表示删除全部
小结
- mongo shell中的增
db.集合名.insert({数据})
db.集合名.save({包含_id的完整数据}) # 根据指定的_id进行保存,存在则更新,不存在则插入 - mongo shell中的删
db.集合名.remove({条件}, {justOne: true/false}) - mongo shell中的改
db.集合名.update({条件}, {$set:{完整数据/部分字段}}, {multi: true/false}) - mongo shell中的查
db.集合名.find({条件}, {字段投影})
mongodb的聚合操作
学习目标
- 了解 mongodb的聚合原理
- 掌握 mongdb的管道命令
- 掌握 mongdb的表达式
1 mongodb的聚合是什么
聚合(aggregate)是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。
语法:db.集合名称.aggregate({管道:{表达式}})
2 mongodb的常用管道和表达式
知识点:
- 掌握mongodb中管道的语法
- 掌握mongodb中管道命令
2.1 常用管道命令
在mongodb中,⽂档处理完毕后, 通过管道进⾏下⼀次处理
常用管道命令如下:
$group
: 将集合中的⽂档分组, 可⽤于统计结果$match
: 过滤数据, 只输出符合条件的⽂档$project
: 修改输⼊⽂档的结构, 如重命名、 增加、 删除字段、 创建计算结果$sort
: 将输⼊⽂档排序后输出$limit
: 限制聚合管道返回的⽂档数$skip
: 跳过指定数量的⽂档, 并返回余下的⽂档
2.2 常用表达式
表达式:处理输⼊⽂档并输出
语法:表达式:'$列名'
常⽤表达式:
$sum
: 计算总和, $sum:1 表示以⼀倍计数$avg
: 计算平均值$min
: 获取最⼩值$max
: 获取最⼤值$push
: 在结果⽂档中插⼊值到⼀个数组中
3 管道命令之$group
3.1 按照某个字段进行分组
$group
是所有聚合命令中用的最多的一个命令,用来将集合中的文档分组,可用于统计结果
使用示例如下
db.stu.aggregate({$group:{_id:"$gender",counter:{$sum:1}}}
)
其中注意点:
db.db_name.aggregate
是语法,所有的管道命令都需要写在其中_id
表示分组的依据,按照哪个字段进行分组,需要使用$gender
表示选择这个字段进行分组$sum:1
表示把每条数据作为1进行统计,统计的是该分组下面数据的条数
3.2 group by null
当我们需要统计整个文档的时候,$group
的另一种用途就是把整个文档分为一组进行统计
使用实例如下:
db.stu.aggregate({$group:{_id:null,counter:{$sum:1}}}
)
其中注意点:
_id:null
表示不指定分组的字段,即统计整个文档,此时获取的counter
表示整个文档的个数
3.3 数据透视
正常情况在统计的不同性别的数据的时候,需要知道所有的name,需要逐条观察,如果通过某种方式把所有的name放到一起,那么此时就可以理解为数据透视
使用示例如下:
统计不同性别的学生
db.stu.aggregate({$group:{_id:null,name:{$push:"$name"}}} )
使用
$$ROOT
可以将整个文档放入数组中db.stu.aggregate({$group:{_id:null,name:{$push:"$$ROOT"}}} )
3.4 动手
对于如下数据,需要统计出每个country/province下的userid的数量(同一个userid只统计一次)
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "b" }
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "c" }
{ "country" : "china", "province" : "bj", "userid" : "da" }
{ "country" : "china", "province" : "bj", "userid" : "fa" }
参考答案
db.tv3.aggregate({$group:{_id:{country:'$country',province:'$province',userid:'$userid'}}},{$group:{_id:{country:'$_id.country',province:'$_id.province'},count:{$sum:1}}}
4 管道命令之$match
$match
用于进行数据的过滤,是在能够在聚合操作中使用的命令,和find
区别在于$match
操作可以把结果交给下一个管道处理,而find
不行
使用示例如下:
查询年龄大于20的学生
db.stu.aggregate({$match:{age:{$gt:20}})
查询年龄大于20的男女学生的人数
db.stu.aggregate({$match:{age:{$gt:20}}{$group:{_id:"$gender",counter:{$sum:1}}})
5 管道命令之$project
$project
用于修改文档的输入输出结构,例如重命名,增加,删除字段
使用示例如下:
查询学生的年龄、姓名,仅输出年龄姓名
db.stu.aggregate({$project:{_id:0,name:1,age:1}})
查询男女生人生,输出人数
db.stu.aggregate({$group:{_id:"$gender",counter:{$sum:1}}}{$project:{_id:0,counter:1}})
5.1 动手练习
对于如下数据:统计出每个country/province下的userid的数量(同一个userid只统计一次),结果中的字段为{country:““,province:””,counter:“*”}
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "b" }
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "c" }
{ "country" : "china", "province" : "bj", "userid" : "da" }
{ "country" : "china", "province" : "bj", "userid" : "fa" }
参考答案
db.tv3.aggregate({$group:{_id:{country:'$country',province:'$province',userid:'$userid'}}},{$group:{_id:{country:'$_id.country',province:'$_id.province'},count:{$sum:1}}},{$project:{_id:0,country:'$_id.country',province:'$_id.province',counter:'$count'}})
6 管道命令之$sort
$sort
用于将输入的文档排序后输出
使用示例如下:
查询学生信息,按照年龄升序
db.stu.aggregate({$sort:{age:1}})
查询男女人数,按照人数降序
db.stu.aggregate({$group:{_id:"$gender",counter:{$sum:1}}},{$sort:{counter:-1}} )
7 管道命令之$skip
和 $limit
$limit
限制返回数据的条数$skip
跳过指定的文档数,并返回剩下的文档数- 同时使用时先使用skip在使用limit
使用示例如下:
查询2条学生信息
db.stu.aggregate({$limit:2} )
查询从第三条开始的学生信息
db.stu.aggregate({$skip:3} )
统计男女生人数,按照人数升序,返回第二条数据
db.stu.aggregate({$group:{_id:"$gender",counter:{$sum:1}}},{$sort:{counter:-1}},{$skip:1},{$limit:1} )
8 小结
- 理解聚合操作的是在干什么
- 掌握
$group
,$match
,$project
的使用 - 熟悉
$sort
,$limit
,$skip
的使用 - 实现常用的表达式
Mongodb的索引操作
学习目标
- 掌握 mongodb索引的创建,删除操作
- 掌握 mongodb查看索引的方法
- 掌握 mongodb创建唯一索引的方法
1. 为什么mongdb需要创建索引
- 加快查询速度
- 进行数据的去重
2. mongodb创建简单的索引方法
- 语法:
db.集合名.ensureIndex({属性:1})
,1表示升序, -1表示降序
3. 创建索引前后查询速度对比
测试:插入10万条数据到数据库中
插入数据:
for(i=0;i<100000;i++){db.t1.insert({name:'test'+i,age:i})}
创建索引前:
db.t1.find({name:'test10000'})
db.t1.find({name:'test10000'}).explain('executionStats') # 显示查询操作的详细信息
创建索引:
db.t1.ensureIndex({name:1})
创建索引后:
db.t1.find({name:'test10000'}).explain('executionStats')
前后速度对比
4. 索引的查看
默认情况下_id是集合的索引
查看方式:db.集合名.getIndexes()
5. 删除索引
语法:db.集合名.dropIndex({'索引名称':1})
db.t1.dropIndex({name:1})
db.t1.getIndexes()
6. mongodb创建唯一索引
在默认情况下mongdb的索引域的值是可以相同的,创建唯一索引之后,数据库会在插入数据的时候检查创建索引域的值是否存在,如果存在则不会插入该条数据,但是创建索引仅仅能够提高查询速度,同时降低数据库的插入速度。
6.1 添加唯一索引的语法:
db.集合名.ensureIndex({"字段名":1}, {"unique":true})
6.2 利用唯一索引进行数据去重
根据唯一索引指定的字段的值,如果相同,则无法插入数据
db.t1.ensureIndex({"name":1}, {"unique":true})
db.t1.insert({name: 'test10000'})
7. 建立复合索引
在进行数据去重的时候,可能用一个域来保证数据的唯一性,这个时候可以考虑建立复合索引来实现。
例如:抓全贴吧信息,如果把帖子的名字作为唯一索引对数据进行去重是不可取的,因为可能有很多帖子名字相同
建立复合索引的语法:db.collection_name.ensureIndex({字段1:1,字段2:1})
8. 建立索引注意点
根据需要选择是否需要建立唯一索引
索引字段是升序还是降序在单个索引的情况下不影响查询效率,但是带复合索引的条件下会有影响
数据量巨大并且数据库的读出操作非常频繁的时候才需要创建索引,如果写入操作非常频繁,创建索引会影响写入速度
例如:在进行查询的时候如果字段1需要升序的方式排序输出,字段2需要降序的方式排序输出,那么此时复合索引的建立需要把字段1设置为1,字段2设置为-1
课后思考
数据库为什么要做读写分离(读写分离的意义)?
小结
1. 掌握mongodb索引的创建,删除操作
2. 掌握mongodb查看索引的方法
3. 掌握mongodb创建唯一索引的方法
Mongodb的权限管理
学习目标
1.了解 mongodb的权限管理
1. 为什么要进行权限管理的设置
刚安装完毕的mongodb默认不使用权限认证方式启动,与MySQL不同,mongodb在安装的时候并没有设置权限,然而公网运行系统需要设置权限以保证数据安全,所以我们要学习mongodb的权限管理
2. mongodb的权限管理方案
- MongoDB是没有默认管理员账号,所以要先添加管理员账号,并且mongodb服务器需要在运行的时候开启验证模式
- 用户只能在用户所在数据库登录(创建用户的数据库),包括管理员账号。
- 管理员可以管理所有数据库,但是不能直接管理其他数据库,要先认证后才可以。
3. mongodb超级管理员账号的创建
3.1 创建超级用户
进入mongo shell
sudo mongod
使用admin数据库(超级管理员账号必须创建在该数据库上)
use admin
创建超级用户
db.createUser({"user":"python","pwd":"python","roles":["root"]})
创建成功会显示如下信息
Successfully added user: { "user" : "python", "roles" : [ "root" ] }
退出mongo shell
exit
3.2 以权限认证的方式启动mongodb数据库
sudo mongod --auth
启动之后在启动信息中会有如下信息,说明mongodb以权限认证的方式启动成功
[initandlisten] options: { security: { authorization: "enabled" } }
3.3 登录验证
此时再使用数据库各命令的时候会报权限错误,需要认证才能执行相应操作、
use admin
db.auth('python','python')
- python用户是创建在admin数据库上的所以必须来到admin数据库上进行认证
- 认证成功会返回1,失败返回0
4. 创建普通用户
4.1 在使用的数据库上创建普通用户
1.选择需要创建用户的数据库
use test1
- 创建用户
db.createUser("user":"user1", "pwd":"pwd1", roles:["read"])
创建普通用户user1,该用户在test1上的权限是只读
db.createUser("user":"user1", "pwd":"pwd1", roles:["readWrite"])
创建普通用户user1,该用户在test1上的权限是读写
4.2 在admin用户数据库上创建普通用户
use admin
db.createUser({"user":"python1", "pwd":"python1", roles:[{"role":"read","db":"dbname1"},{"role":"readWrite","db":"dbname2"}
]})
在admin上创建python1用户,python1用户的权限有两个,一个再dbname1上的只读,另一个是在dbname2上的读写
5. 查看创建的用户
show users
{"_id" : "admin.python","user" : "python","db" : "admin","roles" : [{"role" : "root","db" : "admin"}]
}
6. 删除用户
6.1 进入账号数据所在的数据库
use db_name
6.2 删除用户
db.dropUser('python')
小结
- 了解mongodb的权限管理
- 熟悉创建用户的相应流程
mongodb和python交互
学习目标
- 掌握 mongdb和python交互的增删改查的方法
- 掌握 权限认证的方式使用pymongo模块
1. mongdb和python交互的模块
pymongo
提供了mongdb和python交互的所有方法
安装方式: pip install pymongo
2. 使用pymongo
2.1 导入pymongo并选择要操作的集合
数据库和集合能够自动创建
2.1.1 无需权限认证的方式创建连接对象以及集合操作对象
from pymongo import MongoClientclient = MongoClient(host,port) # 如果是本地连接host,port参数可以省略collection = client[db名][集合名]
# collection = client.db名.集合名 # 与上边用法相同
2.1.2 需要权限认证的方式创建连接对象以及集合操作对象
from pymongo import MongoClient
from urllib.parse import quote_plususer = 'python' # 账号
password = 'python' # 密码
host = '127.0.0.1' # host
port = 27017 # port
uri = "mongodb://%s:%s@%s" % (quote_plus(user),quote_plus(password),host)
# quote_plus函数:对url进行编码
# uri = mongodb://python:python@127.0.0.1
client = MongoClient(uri, port=port)
collection = client.db名.集合名
2.2 insert()添加数据
insert可以批量的插入数据列表,也可以插入一条数据
collection.insert({一条数据})
collection.insert([{数据一},{数据二}])
2.2.1 添加一条数据
返回插入数据的_id
ret = collection.insert({"name":"test10010","age":33})
print(ret)
2.2.2 添加多条数据
返回ObjectId对象构成的列表
item_list = [{"name":"test1000{}".format(i)} for i in range(10)]
rets = collection.insert(item_list)
print(rets)
for ret in rets:print(ret)
2.3 find_one()查找一条数据
接收一个字典形式的条件,返回字典形式的整条数据
如果条件为空,则返回第一条
ret = client.test.test.find_one({'name': 'test10001'})
print(ret) # 包含mongodb的ObjectId对象的字典
_ = ret.pop('_id') # 清除mongodb的ObjectId对象的k,v
print(ret)
2.4 find()查找全部数据
返回所有满足条件的结果,如果条件为空,则返回全部
结果是一个Cursor游标对象,是一个可迭代对象,可以类似读文件的指针,但是只能够进行一次读取
rets = collection.find({"name":"test10005"}),
for ret in rets:print(ret)
for ret in rets: #此时rets中没有内容print(ret)
2.5 update()更新数据(全文档覆盖或指定键值,更新一条或多条)
- 语法:collection.update({条件}, {‘$set’:{指定的kv或完整的一条数据}}, multi=False/True, upsert=False/True)
- multi参数:默认为False,表示更新一条; multi=True则更新多条; multi参数必须和$set一起使用
- upsert参数:默认为False; upsert=True则先查询是否存在,存在则更新;不存在就插入
- $set表示指定字段进行更新
2.5.1 更新一条数据;全文档覆盖;存在就更新,不存在就插入
data = {'msg':'这是一条完整的数据1','name':'哈哈'}
client.test.test.update({'haha': 'heihei'}, {'$set':data}, upsert=True)
2.5.2 更新多条数据;全文档覆盖;存在就更新,不存在就插入
data = {'msg':'这是一条完整的数据2','name':'哈哈'} # 该完整数据是先查询后获取的
client.test.test.update({}, {'$set':data}, multi=True, upsert=True)
2.5.3 更新一条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___1'}
client.test.test.update({}, {'$set':data}, upsert=True)
2.5.4 更新多条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___2'}
client.test.test.update({}, {'$set':data}, multi=True, upsert=True)
2.6 delete_one()删除一条数据
collection.delete_one({"name":"test10010"})
2.7 delete_many()删除全部数据
collection.delete_many({"name":"test10010"})
3. pymongo模块其他api
查看pymongo官方文档或源代码 http://api.mongodb.com/python/current/
小结
- 掌握pymongo的增删改查的使用
- 掌握权限认证的方式使用pymongo模块
‘name’: ‘test10001’})
print(ret) # 包含mongodb的ObjectId对象的字典
_ = ret.pop(‘_id’) # 清除mongodb的ObjectId对象的k,v
print(ret)
#### 2.4 find()查找全部数据> 返回所有满足条件的结果,如果条件为空,则返回全部
> 结果是一个Cursor游标对象,是一个可迭代对象,可以类似读文件的指针,但是只能够进行一次读取```python
rets = collection.find({"name":"test10005"}),
for ret in rets:print(ret)
for ret in rets: #此时rets中没有内容print(ret)
2.5 update()更新数据(全文档覆盖或指定键值,更新一条或多条)
- 语法:collection.update({条件}, {‘$set’:{指定的kv或完整的一条数据}}, multi=False/True, upsert=False/True)
- multi参数:默认为False,表示更新一条; multi=True则更新多条; multi参数必须和$set一起使用
- upsert参数:默认为False; upsert=True则先查询是否存在,存在则更新;不存在就插入
- $set表示指定字段进行更新
2.5.1 更新一条数据;全文档覆盖;存在就更新,不存在就插入
data = {'msg':'这是一条完整的数据1','name':'哈哈'}
client.test.test.update({'haha': 'heihei'}, {'$set':data}, upsert=True)
2.5.2 更新多条数据;全文档覆盖;存在就更新,不存在就插入
data = {'msg':'这是一条完整的数据2','name':'哈哈'} # 该完整数据是先查询后获取的
client.test.test.update({}, {'$set':data}, multi=True, upsert=True)
2.5.3 更新一条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___1'}
client.test.test.update({}, {'$set':data}, upsert=True)
2.5.4 更新多条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___2'}
client.test.test.update({}, {'$set':data}, multi=True, upsert=True)
2.6 delete_one()删除一条数据
collection.delete_one({"name":"test10010"})
2.7 delete_many()删除全部数据
collection.delete_many({"name":"test10010"})
3. pymongo模块其他api
查看pymongo官方文档或源代码 http://api.mongodb.com/python/current/
小结
- 掌握pymongo的增删改查的使用
- 掌握权限认证的方式使用pymongo模块
Python爬虫第四课 appium和第MongoDB数据库相关推荐
- Python爬虫第四课:Network、XHR、json
在爬虫实践当中,如果我们爬取的页面的编写没有做好板块的区分,或者我们选取的标签不合适,最终我们获得的结果会多提取到出一些奇怪的东西. 当使用用request获取的网页源代码里没有我们想要的数据时,需要 ...
- Python爬虫入门四之Urllib库的高级用法
1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...
- python爬虫(四)数据存储
python爬虫(四)数据存储 JSON文件存储 JSON是一种轻量级的数据交换格式,它是基于ECMAScript的一个子集 JSON采用完全独立于语言的文本格式 JSON在Python中分别由lis ...
- Python爬虫利器四之PhantomJS的用法
随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) 原文这篇文章讲解的纯PhantomJS的用法,讲得比较细致,具体可以跟Python也可以跟node等结合使用. ...
- Python爬虫【四】爬取PC网页版“微博辟谣”账号内容(selenium多线程异步处理多页面)
专题系列导引 爬虫课题描述可见: Python爬虫[零]课题介绍 – 对"微博辟谣"账号的历史微博进行数据采集 课题解决方法: 微博移动版爬虫 Python爬虫[一]爬取移 ...
- Python爬虫(四)
Python爬虫(四) 一.案例:爬取站长素材图片 # 需求 下载前十页的图片 # 第一页:https://sc.chinaz.com/tupian/qinglvtupian.html # 第二页:h ...
- Python 爬虫入门四——代理服务器
Python 爬虫入门<四> 标签:代理服务器 在我们做爬虫的时候,对弈同一个网页,我们爬取次数多了之后,服务器会屏蔽我们的IP,所以,我们要解决这个问题,不然每次访问,就把自己Ip屏蔽了 ...
- 爬虫第四课 数据处理
页面解析和数据提取 一般来讲对我们而言,爬虫需要抓取的是某个网站或者某个应用的内容,提取有用的数据.响应内容一般分为两种,非结构化的数据 和 结构化的数据. 结构化数据:先有结构.再有数据 非结构化数 ...
- Python爬虫:详解Appium如何爬取手机App数据以及模拟用户操作手势
目录 Appium 模拟操作 屏幕滑动 屏幕点击 屏幕拖动 屏幕拖拽 文本输入 动作链 实战:爬取微博首页信息 Appium 在前文的讲解中,我们学会了如何安装Appium,以及一些基础获取App元素 ...
- python爬虫入门四:BeautifulSoup库(转)
正则表达式可以从html代码中提取我们想要的数据信息,它比较繁琐复杂,编写的时候效率不高,但我们又最好是能够学会使用正则表达式. 我在网络上发现了一篇关于写得很好的教程,如果需要使用正则表达式的话,参 ...
最新文章
- annie安装教程与Access is denied解决方法
- Dubbo Cloud Native 实践与思考
- 百度API_获取当前详细地址
- java 多线程合并结果集_多线程计算数据,然后合并数据
- matlab 平滑曲线连接_【仪光学习】技能分享 | 前方高能:如何用Matlab轻松实现数学建模...
- 给键盘上的enter设置事件_Selenium3 + Python3自动化测试系列——鼠标事件和键盘事件...
- python数据框拼接_pandas数据拼接的实现示例
- Illustrator 教程,了解 Illustrator 中的效果
- python2与python3区别底层的区别_Python2 与 Python3 的区别(二)?
- Selenium 模拟键盘操作
- python中scrapy框架爬取携程景点数据
- java tiff 压缩_java – 多页Tiff压缩
- HTML元素的宽度计算
- 基于目标检测实现遥感场景下的车辆检测计数
- 【评测】无血清细胞冻存液
- MarkDown语句显示红色(重点、关键字)
- yolov7正负样本分配详解
- You will surely observe a Tiffany jewelry
- pandas生意参谋数据处理
- 实验七 MapReduce实验:计数器
热门文章
- 【QNX Hypervisor 2.2 用户手册】1.3 QNX hypervisor架构
- 帝国CMS7.2管理员密码重置
- flea-jersey使用之Flea RESTful接口服务端接入
- Python实现自动录制虎牙直播
- Divide by Zero 2021 and Codeforces Round #714 (Div. 2)B. AND Sequences(位运算 + 思维 + 排列)
- SQL Server 数据库之分离和附加数据库
- Java中级内容——异常处理(exception handing)
- javascript根据浏览器userAgent判断浏览器类型
- jcp jsr_Oracle提交了第三次JCP改革JSR,以提高透明度
- flac格式歌曲如何转换成mp3格式,flac转mp3详细图文教程