|
|
文件保存为hello.sh
,然后修改文件的权限:
|
|
最后,执行:
|
|
exit
不是必须的,但是每个命令都会返回一个退出状态给父进程,成功返回0,非0值通常被认为是错误码,良好脚本都会带上exit
,当一个脚本不带参数exit
来结束时,脚本的退出状态由脚本中最后执行命令来决定
echo $?
可以用来查看前一个命令的退出状态
使用=
进行赋值,并且=
左右两边不能有空格,获取变量值得时候在变量名前面加$
|
|
|
|
正如所见,变量替换会去除掉空白,全引用会禁止所有特殊符号,如果只是想输出变量的值,推荐使用"${}"
这种形式
|
|
所以说bash中的变量都是无类型的
|
|
1 2 3 4 5 6 7 8 9 10
是从命令行传入的10个参数,$0
表示脚本名称,$1
表示第一个参数,${10}
表示第10个参数,$#
位置参数的个数,$*
所有的位置参数,被作为一个单词
每一次执行shift
命令能够将所有位置参数向前移动一个位置,而原来第一个位置的参数则被丢弃
$BASH
- bash二进制执行文件的位置
$FUNCNAME
- 当前函数的名字
$GROUPS
- 当前用户属于的组
$HOME
- 用户home目录
$HOSTNAME
- 主机名
$IFS
- 内部域分隔符,该变量决定bash在解释字符串时如何识别域或单词的边界
$LINENO
- 记录它所在shell脚本中它所在行的行号
$OSTYPE
- 系统类型
$PPID
- 一个进程的$PPID
就是它的父进程的pid
$PWD
- 当前工作目录
$SECONDS
- 这个脚本已经运行的时间
$SHLVL
- shell层叠的层次
$UID
- 用户id号
$$
- 脚本自身进程pid
|
|
这两个命令都可以返回以prefix
开头的已有变量
here documents是一种重定向的形式
|
|
这里的command是一个可以接受标准输入的命令,token是一个用来指示嵌入文本结束的字符串。上述结构就是将text的内容当作标准输入传给了command
将<<
改为<<-
,shell就会忽略text开头的tab字符,这样text内容就可以缩进,从而提高代码的可读性。
|
|
常用上述方法代替echo
输出多行内容
使用read
来获取用户的输入
read a
将获取用户的输入到变量a,如果没有提供变量名,默认变量REPLY
会包含用户输入
read
支持以下选项
-a array
- 把输入赋值到数组array中,从索引号0开始
-n num
- 读取num个输入字符,而不是整行
-p prompt
- 为输入显示提示信息
-r
- raw modw,不会把反斜杠字符解释为转义字符
-s
- silent mode,不会再屏幕上显示输入的文字
-t seconds
- 超时,seconds秒之后,如果没有输入,则返回一个非零退出状态
|
|
若parameter
没有设置或者为空,展开结果为word
,若parameter
不为空,则展开结果是parameter
的值
|
|
若parameter
没有设置或者为空,展开结果为word
,并且word
的值会赋值给parameter
,若parameter
不为空,则展开结果是parameter
的值
|
|
若parameter
没有设置或者为空,这种展开导致脚本带有错误退出,并且word
的内容会发送到标准错误,若parameter
不为空,则展开结果是parameter
的值
函数定义有两种形式
|
|
或者
|
|
调用函数时,只用写函数名,不用加括号,并且函数的定义要在函数调用之前
|
|
在函数内部使用local
关键字来定义局部变量
|
|
|
|
涉及到判断的地方都是检测命令的退出状态码,如果是0,表示命令成功执行,也就表示当前判断的内容为真,非0则假。
-d file
- file存在并且是一个目录
-e file
- file存在
-f file
- file存在并且是一个普通文件
-s file
- file存在并且其长度大于0
-r file
- file存在并且可读
-w file
- file存在并且可写
-x file
- file存在并且可执行
|
|
-n string
- 字符串string的长度大于0
-z string
- 字符串string的长度为0
string1 == string2
- 字符串string1等于字符串string2
string1 > string2
- string1排列在string2之后
|
|
类似于test
|
|
如果string匹配正则表达式regex,则返回真
|
|
循环中可以使用continue
和break
|
|
|
|
read
每次读取文本行之后将会返回退出状态码0,知道文件末尾,返回状态码非零才结束while循环
当循环终止时,循环中创建的任意变量或赋值的变量都会消失
与while类似
|
|
|
|
a)
- 匹配单词a
a|A)
- 匹配单词a
或A
[[:alpha:]]
- 若单词是一个字母字符,则匹配
???)
- 若单词只有3个字符,则匹配
*.txt
- 若单词以.txt
字符结尾,则匹配
|
|
|
|
|
|
也可以使用c语言格式:
|
|
|
|
会展开为parameter
所包含的字符串的长度
|
|
|
|
|
|
|
|
功能与#
和##
类似,只是是从结尾开始匹配
|
|
原parameter变量值不变
|
|
原parameter变量值不变
|
|
|
|
|
|
下标*
和@
可以被用来访问数组中的每一个元素
|
|
bash允许数组下标包含空格,有时候确定哪个元素真正存在是很有用的
|
|
组命令
|
|
子shell
|
|
组命令和子shell都是用来管理重定向的
|
|
会将三个命令的结果合成在一起然后重定向到output.txt
中
组命令是在当前shell中执行它所有的命令,而子shell是在一个子shell中执行命令,在子shell中执行命令对环境变量等修改在子shell消失之后便会消失,大多数情况下,我们使用组命令。
|
|
该REPLY
变量的内容总是空,是应为在管道线中的命令总是在子shell中执行的,bash提供进程替换来解决这个问题
<(list)
- 一种适用于产生标准输出的进程
>(list)
- 一种适用于接受标准输入的进程
|
|
进程替换允许我们把一个子shell的输出结果当作一个用于重定向的普通文件,事实上,它就是一种展开形式
]]>显示磁盘容量
|
|
显示内存信息
|
|
确定文件类型
|
|
less
和more
都能浏览文件,但是前者可以前后分页浏览,后者只支持向前分页浏览
以管理员模式打开资源管理器
|
|
说明怎样解释一个命令名
|
|
获取命令简介
|
|
help
和man
都可以查看命令帮助文档,但是前者是shell内部命令的帮助文档
输入文件前多少行
|
|
输出文件后多少行
|
|
清空屏幕,与ctrl+l
功能一样
|
|
显示历史列表内容
|
|
显示所有服务的运行状态
|
|
显示单个服务的运行状态,例如ssh服务
|
|
;
命令分隔符,可以用来在一行中来写多个命令
""
部分引用,阻止了一部分特殊字符
''
全引用,阻止了全部特殊字符
`
反引号,命令替换
?
测试操作,在参数替换中,可以测试一个变量是够被set
$?
退出状态变量
$$
进程ID变量,保存运行脚本进程ID
cp
- 复制文件和目录
mv
- 移动/重命名文件和目录
mkdir
- 创建目录
rm
- 删除文件和目录
ln
- 创建硬链和符号链接
命令可以是下面四种形式之一:
/usr/bin
中的文件一样。属于这一类的程序,可以编译成二进制文件,诸如用 C 和 C++ 语言写成的程序, 也可以是由脚本语言写成的程序,比如说 shell, perl, python, ruby,等等。>
会删除文件中的内容,然后将内容定向到文件中,>>
则是在文件末尾中追加
标准输入和标准输出以及标准错误流是各自重定向的,shell内部参考它们文件描述符为0,1,2
|
|
上述命令就是将错误流输出到ls-error.txt
文件中
如果我们想实现将标准输出和标准错误重定向到同一个文件中,我们可以:
|
|
上述命令就是先将标准输出重定向到文件, 然后将标准错误重定向到标准输出
注意重定向的顺序很重要,标准错误的重定向必须总是出现在标准输出重定向之后,要不然它不起作用
现在的bash也支持使用以下更精简的方法来将标准输出和错误重定向到同一个文件中
|
|
有时候,我们不想要一个命令的输出结果,只想把它扔掉,我们就可以利用一个特殊的设备/dev/null
(相当于垃圾桶)
|
|
上述命令就是将标准错误流扔掉了
|
|
将文件内容清空,如果文件不存在,则创建文件,与下面命令功能一样
|
|
:
是空命令
管道命令|
是将一个命令的标准输出重定向到另一个命令的标准输入
例如,我们使用:
|
|
就能更方便的查看当前目录下的所有文件了
tee
命令从标准输入读取数据,并同时输出到标准输出和文件中。
|
|
命令替换允许我们把一个命令的输出作为一个展开模式来使用
|
|
也可以使用反引号来代替美元符号和括号
|
|
当应用到一个可执行文件时,它把有效用户ID从真正的用户(实际运行程序的用户)设置成程序所有者的ID
与setuid位相似,把有效用户组ID从真正的用户组ID更改为文件所有者的组的ID
linux会忽略文件的sticky位,但是如果一个目录设置了sticky位,那么它能阻止用户删除或重命名,除非用户是这个目录的所有者,或是文件的所有者,或是超级用户
ps
显示当前有TTY(进程的控制终端)的进程,ps x
显示所有进程,不管它们由什么终端控制,px aux
还可以显示进程的所有者,CPU和内存使用率等
R
- 运行S
- 正在睡眠D
- 不可中断睡眠,进程正在等待I/OT
- 已停止Z
- 僵尸进程<
- 高优先级进程N
- 低优先级进程 ps
只是进程快照,而top
命令可以动态的显示系统进程更新的信息(默认情况下,每3秒更新一次).pstree
可以输出一个树形结构的进程列表
可以在命令之后加上&
,让它立即在后台执行
|
|
jobs
可以显示当前终端后头运行的任务以及状态
一个在后台运行的进程对一切来自键盘的输入都免疫,也不能用ctrl+c
来中断它。
使用fg
将一个进程返回前台执行
|
|
有时候我们需要停止一个进程,而不是终止。这样会把一个前台进程移到后台等待,输入ctrl+z
,可以停止一个前台进程。处于停止的进程可以使用fg
命令恢复程序到前台运行或者用bg
命令把程序移到后台。
可以使用kill PID
或kill jobspec
来终止进程
常用命令:
yy
- 复制当前行5yy
- 复制当前行以及随后的四行文本y0
- 复制当前光标位置到当前行首的内容y$
- 复制当前光标位置到当前行的尾部p
- 粘贴d
- 删除/剪切文本cat -A 文件名
可以查看文件中的特殊符号
cat -n 文件名
输出文件内容并显示行号
sort
对标准输入的内容,或命令行中指定的一个或多个文件进行排序,然后把排序结果发送到标准输出。
cut
用来从文本行中抽取文本,并把它输入到标准输出
paste
功能与cut
相反,它会添加一个或多个文本列到文件中,而不是从文件中抽取文本列。它通过读取多个文件,然后把每个文件中的字段整合成单个单个文本流,输入到标准输出。
sed
命令对文本流就行编辑,一般用来做替换操作。
北京时间2016年12月23日晚上6点半左右,python官网放出了python3.6.0正式版,安装后,可以看到windows版具体编译时间是2016年12月23日早上8点6分。可以说python3.6从测试到正式发布已经有很长一段时间了,并且官方表示,2017年初开始对3.6版本进行各种bug修复等改进,也就是3.6.x的版本,关于python3.6相较于3.5有哪些变化,请看What’s New In Python 3.6
本文主要讲解如何将工作环境从python3.5转到python3.6,以及python3.6新功能的介绍。
由于python的每个版本,例如3.5和3.6安装时安装目录是分开的(windows环境),而如果我们将python第三方库安装在python安装目录下的话,那么现在我如果使用3.6,又得重新将3.6的安装目录添加到环境变量PATH
,并且将大量第三方库安装到3.6安装目录,但是这样就引发了一个问题,那就是多份第三方库都存在于电脑中,当然也可以删除3.5相关的所有文件,但是实际上重新安装常用的那些库又很麻烦,所以我将python虚拟环境当作我的工作环境,也就是在F:\pythonVE
目录创建一个python虚拟环境,将第三方库都安装在这个虚拟环境中,所以现在刚刚安装好python3.6,只用在cmd执行:
|
|
注意这里的python
是3.6中的python.exe
,--upgrade
参数的意思就是将虚拟环境中的python版本升级为此python版本(3.6版本)
所以PAHT
中只用添加虚拟环境的路径就可以了,然后就是慢慢更新第三方包了,毕竟第三方包适配3.6也需要时间,但是毫无疑问,会很快。jupyter的ipython-qtconsole.exe
现在就用不了,因为pyqt还没支持3.6(毕竟3.6今天才出23333),不过相信过几天就可以用了,python3已经是趋势,不要告诉我你的主要工作环境是python2(话说12月17号更新了python2.7.13)
注意有些包还是要手动更新的,例如windows上无法编译lxml,所以一般都是下载编译好的进行安装,之前下载的是支持python3.5的lxml,现在需要卸载当前库,并手动下载编译好的支持3.6的lxml进行安装,有些包使用pip安装的时候会提示编码问题,简单的方法就是从Unofficial Windows Binaries for Python Extension Packages下载,然后直接安装
以上只是本人环境,因为我目前只把python当作工具,所以不会像开发库一样考虑版本兼容等情况,不过一般还是建议将常用包放在python安装目录下,对于特定的项目构建虚拟环境,在虚拟环境中安装与python版本相适应的包进行开发。
主要改变:
|
|
在字符串前面加f
,表示该字符串将被格式化,类似于对字符串进行str.format()
操作,不得不说,确实很方便
提供变量声明语法,,包括类中的变量,实例中的变量和函数参数
|
|
|
|
当然,python始终是一门动态语言,所以这些类型声明实际上只是将这些类型信息存储在类或者模块的__annotations__
属性中,并不会在运行时检擦这些属性,只是起到提示的作用,当然,这个特性确实也很有用处,具体类型声明语法请看PEP 484
能够在数字间添加下划线以提高阅读性
|
|
同时字符串格式化也支持这种下划线的格式化方式:
|
|
当然也可以使用二进制b
,八进制o
异步生成器,python3.6中可以在同一函数体中使用await
和yield
|
|
以上代码现在可以简写为:
|
|
可以在列表,元组,字典,生成器表达式中使用async for
和await
|
|
可以简写为:
|
|
有关await
的例子:
|
|
现在可以不用使用元类来自定义子类的创建
当子类被创建时,基类中的__init_subclass__()
类方法将被调用
|
|
描述符中新增了__set_name__()
方法,当描述符被实例化时,便会调用__set_name__()
方法
|
|
在大多数眼中,路径就是字符串或者是字节对象,以至于python标准库pathlib
较少被使用。现在提供了一个os.PathLike
接口,只要实现了__fspath__()
方法,那么这个对象就表示是一个路径,并且可以使用os.fspath()
,os.fsdecode()
, 或者 os.fsencode()
方法或者这个路径对象的字符串或字节表示
|
|
现在的python3.6版本使得我们可以在windows平台是正确使用字节对象表示的路径,而不会造成数据丢失,事实上,该字节对象就是通过sys.getfilesystemencoding()
编码的,也就是UTF-8
The default console on Windows will now accept all Unicode characters and provide correctly read str objects to Python code. sys.stdin
, sys.stdout
andsys.stderr
now default to utf-8 encoding.
只想说,简直是福音,再也不用担心控制台输出乱码了。。。
类中定义的属性的顺序在__dict__
中将被保留
**kwargs
in a function signature is now guaranteed to be an insertion-order-preserving mapping.
新的dict实现,比原来的实现快20% 到25%不说,还保留了顺序,也就是说dict现在是有序的。。。所以要OrderedDict何用?不过,官方也说了,现在只是暂时这样,有可能之后的版本又变成无序的了
|
|
添加了secrets
模块
改进了re
模块,在正则表达式中添加了修饰符跨度的支持,Examples: '(i:p)ython'
matches 'python'
and 'Python'
, but not 'PYTHON'
; '(?i)g(?-i:v)r'
matches 'GvR'
and 'gvr'
, but not 'GVR'
更多细节改动参考官网What’s New In Python 3.6
cryptography模块主要分为两类,一类是高层次的加密配方,也就是我们只用关心如何使用它提供的api,并不用关心具体加密过程等细节,这也是我们经常使用的。另一类是低层次的加密原语,如果对密码学不是很了解的话,使用加密原语构造自己的加密算法是很危险的。本片文章介绍高层次的对称加密api和低层次非对称的公钥私钥以及证书
|
|
|
|
为了以后根据password
得到token
,需要保存好salt
数字证书是CA机构签名的含有服务器公钥以及其他网站相关信息的一种电子证书,用来说明该服务器(网站)确实是真的(官方的),而不是伪造的
这里主要使用的是非对称加密,也就是公钥和私钥(RSA),私钥用来签名,公钥用来验签
When obtaining a certificate from a certificate authority (CA), the usual flow is:
所以首先要生成密钥对:
|
|
关于生成certificate signing request,请看官方文档,然后就可以将生成的证书发送给CA机构,待CA机构处理完,就会返回给你经过他们签名的数字证书,该数字证书也是用户用来核实我们网站的证书。
|
|
这样就生成了一个RSAPrivateKey
对象。参数保持上面就可以了,具体参数解析看官方文档
私钥公钥是成对生成的,所以当我们使用generate_private_key
生成RSAPrivateKey
对象时,我们可以通过生成的对象获取到RSAPublicKey
对象
|
|
当然,肯定是不可以从RSAPublicKey
对象中获取到RSAPrivateKey
对象的。
也可以从一个pem格式的文件导入一个RSAPrivateKey
对象
pem格式文件就是类似:
|
|
A PEM block which starts with -----BEGIN CERTIFICATE-----
is not a public or private key, it’s anX.509 Certificate. You can load it using load_pem_x509_certificate()
and extract the public key with Certificate.public_key
当然这个文件也可以被加密,我们使用如下方法从pem文件中导入RSAPrivateKey
对象
|
|
同理也可以从cer文件和ssh格式文件中导入私钥或公钥。
RSAPrivateKey
对象和RSAPublicKey
对象都可以序列化为pem文件
|
|
强烈建议对私钥进行序列化的时候用自己的密钥进行加密,这样不会将私钥完全暴露
我们之所以说上述过程是序列化,而不是保存私钥,是因为该pem文件不止包含私钥,还包括一些有关私钥的重要信息,具体pem格式请查阅相关文档。而且实际上用的时候并不需要我们手动对pem文件进行解析,只用使用库提供的api就行
也可以不加密,改变如下
|
|
对于公钥的序列化,如下:
|
|
使用私钥可以对一段信息进行签名,然后别人就可以使用公钥进行验证。
|
|
padding
也就是填充,就是将不够长度的信息填充成指定长度(这里为256),具体为什么需要填充请参考SHA256算法实现
也可以使用更简单的方法进行签名:
|
|
|
|
如果验证不通过,将会触发异常,同样,也有以下简单的方式进行验证:
|
|
使用私钥对信息加密没有意义,因为全世界都有你的公钥,毕竟公钥是公开的,当然,如果你不公开你的公钥,那更失去了意义,所以加密指的是用公钥进行加密,然后我们使用私钥来解密
|
|
|
|
可以看到目前对公钥私钥的操作很多都是使用固定参数就完全够了,所以可以对此进一步封装,于是就出现了该项目
使用python标准库进行邮件的处理比较复杂,所以产生了yagmail,但是yagmail目前只能用SMTP协议进行邮件发送,并不能读取邮件,也不支持其他的邮件相关协议,但是对于一般使用完全够了。
首先是通过yagmail.SMTP()
生成一个客户端,但是为了不将我们的密码暴露下脚本文件中,yagmail使用keyring模块将密码存放在系统keyring服务中。
关于keyring是什么,请看:What does a Keyring do?
官方文档中,
|
|
实际上是对keyring.set_password('yagmail', 'mygmailusername', 'mygmailpassword')
的封装。
SMTP()
方法会去用户主文件夹读取.yagmail
文件,但是以上操作并不会生成这个文件,所以需要自己创建,并将自己的邮箱写入文件中。
例如,我测试过程中写入.yagmail
文件中的内容为:
|
|
而之前我已经通过register()
方法将该邮箱的密码保存到了系统keyring中,所以接下来就可以初始化一个SMTP客户端
另外还需要注意的是,经过测试,163邮箱很容易将邮件识别为垃圾邮件,导致邮件发送错误,而qq邮箱需要关闭邮件保护,其他邮箱没有测试,这里推荐使用qq邮箱。
|
|
yagmail.SMTP()
默认使用的gmail的SMTP服务,所以我们如果使用qq邮箱,则使用如下代码初始化一个SMTP客户端
|
|
紧接着就可以发送邮件了
|
|
至此,便像13207130066.cool@163.com
这个邮箱发送了一封邮件。
注意send()
方法的定义:
|
|
如果不指定to
参数,则发送给自己,如果to
参数是一个列表,则将该邮件发送给列表中的所有用户,attachments
表示附件,该参数可以是列表,表示发送多个附件
对于contents
参数,官方说明如下:
This is a big title
计算机行业重点问题,需要深入理解,持续更新
scrapy发出的请求是异步的,默认过滤掉相同的url。能做html/xml解析,数据能导出多种格式,还有强大的插件系统
scrapy(1.2.2)目前支持python 3,但是官方文档是也有说明,并不支持windows平台上的python3,因为scrapy的核心依赖Twisted
目前并不支持windows平台上的python 3,所以知乎上有人推荐使用python 2.7,并需要安装Visual C++ Compiler for Python 2.7,并且window10 也支持这个软件,但是按照python开发者手册上的说明,python2.7只会维护到2020年,并且python的未来也是指向python 3,基本上主流库都支持了python 3,并且很多库已经开始不支持python 2了,所以这里我还是想使用python 3.
关于为什么不支持windows平台,原因是windows上不能编译scrapy的依赖lxml
和Twisted
,但是我们可以下载已经编译好的whl
包,用pip
安装即可,详情,可以参考这篇博客: python 3.5 + scrapy1.2 windows下的安装
|
|
将会在当前工作目录下创建test_scrapy
文件夹,文件下下有以下内容:
|
|
我们编写的爬虫类必须继承scrapy.Spider
并定义好初始请求链接,并且应该将文件放置在spiders
目录下。
我们在spiders
目录下创建quotes_spider.py
:
|
|
name
是spider名称,同一项目中不能同名
start_requests()
必须返回可迭代的Requests
(一个Requests
列表或者是生成器对象),这些请求是爬虫初始的爬取对象.scrapy提供一种简单实现start_requests()
的方式,就是使用start_urls
列表,该列表在后台会被自动封装成Requests
生成器并使用默认的回掉函数parse()
|
|
parse()
是默认的回调函数。Request
可以设置得到响应后的回调函数。
在项目的根目录执行:
|
|
quotes
是爬虫名
将会看到以下输出:
|
|
并在根目录生成quotes-1.html
和quotes-2.html
使用类选择器对html/xml进行解析,同时scrapy也支持XPath表达式
|
|
response.css()
返回列表,如果想提取第一个,可以这样:
|
|
推荐使用第一种方式,这样,如果response.css()
返回空列表,前者会返回None
,后者会触发异常
除了使用 extract()
和 extract_first()
提取数据,也可以使用re()
进行正则提取
|
|
|
|
|
|
|
|
更多命令以及命令的详细使用方法请参考官方文档
除了继承scrapy.Spider
,常用的还有scrapy.spiders.CrawlSpider
,该类可以在前者的基础上添加Rule
。
|
|
scrapy.spiders.SitemapSpider
可以根据sitemaps和robots.txt进行爬去
|
|
规则中表示含有/shop/
的url的回调函数为parse_shop
,sitemap_follow
表示只跟随包含/sitemap_shops
的url
python自带的dict
没有结构体的概念,所以scrapy提供了Item
类
|
|
|
|
Item Loader能够更好将response
中的数据注入到Item
中
|
|
Item
被爬取后会发送给pipeline进行处理,一般pipeline是只用实现process_item
的类,也可以实现open_spider()
(爬虫开始前执行)和close_spider()
|
|
以上是scrapy基础内容,更多有关scrapy,如log和email等查看官方文档
正则表达式会被python解释器编译成字节码,这样查找的效率比单纯用python代码实现查找要快,但是匹配统一内容可以有多种不同的正则表达式,并且他们的效率各不相同
|
|
匹配这些特殊符号需要使用\
进行转义
.
匹配除换行符以外的任意字符,如果指定了DOTALL
标志,则匹配所有字符,但注意.
表示仅仅匹配一个字符
|
|
^
匹配字符串的开始,当指定MULTILINE
标志,则匹配每一行的开头
|
|
###
匹配字符串的结尾,当指定MULTILINE
标志,则匹配每一行的结尾(匹配换行符之前的)
|
|
*
*
表示0个或多个前一字符或正则
|
|
+
+
表示1个或多个前一字符或正则
|
|
?
?
表示0个或1个前一字符或正则
|
|
*?
+?
??
*
+
?
都是贪婪的,会匹配最长的
|
|
在这些操作符后面添加?
能够使之变为不贪婪的,也就是匹配最短的
|
|
{m}
{m}
表示m个前一字符或正则
|
|
{m,n}
{m,n}
表示m到n个前一字符或正则 注意:,
后面没有空格
|
|
省略m表示没有下限,省略n表示没有上限
|
|
{m,n}?
{m,n}
会匹配最长的,在后面加?
,则匹配最短的
|
|
[]
[]
指定一组字符
|
|
很多特殊符号在[]
环境内无效,其他特殊符号需要转义:
|
|
[]
内的^
表示非,^^
表示除^
以外的全部字符:
|
|
|
|
也就是或,注意也是短路操作
|
|
(...)
匹配圆括号里的RE匹配的内容,并指定组的开始和结束位置。组里面的内容可以被提取,要匹配(
和)
,则需要使用转义符号或者是[(]
,[)]
(?aiLmsux)
i
,L
,m
,s
,u
,x
里的一个或多个字母。表达式不匹配任何字符,但是指定相应的标志:re.I
(忽略大小写)、re.L
(依赖locale)、re.M
(多行模式)、re.S
(.匹配所有字符)、re.U
(依赖Unicode)、re.X
(详细模式)
|
|
(?P<name>...)
和普通的圆括号类似,但是子串匹配到的内容将可以用命名的name
参数来提取。组的name
必须是有效的python标识符,而且在本表达式内不重名。命名了的组和普通组一样,也用数字来提取,也就是说名字只是个额外的属性。
|
|
\number
表示之前的分组
|
|
\A
仅匹配字符串的开头
|
|
\b
表示单词开始和结尾处的空白字符以及非字母非数字的字符
|
|
\B
\b
的反面
|
|
\s
匹配空白字符,包括[ \t\n\r\f\v]
|
|
\S
\s
的反面
|
|
\w
匹配数字和字母
|
|
\W
\w
的反面
|
|
\Z
匹配字符串结尾
|
|
re
模块方法re.compile(pattern, flags=0)
编译一个正则表达式为一个正则表达式对象,之后就可以使用该对象对字符串进行匹配了
re.search(pattern, string, flags=0)
从字符串的开头开始搜索匹配,返回匹配到的第一个
re.match(pattern, string, flags=0)
返回字符串中匹配的第一个
re.fullmatch(pattern, string, flags=0)
对整个字符串进行匹配
re.split(pattern, string, maxsplit=0, flags=0)
凭正则表达式分割字符串
re.findall(pattern, string, flags=0)
如果匹配模式中包含分组,则返回分组,如果有多个分组,则返回分组组成的元组
re.finditer(pattern, string, flags=0)
返回迭代器
re.sub(pattern, repl, string, count=0, flags=0)
替换
像match()
search()
等方法返回的就是一个Match
对象,该对象包括的属性和方法请看官方文档
注意,关于分组,第0组就是匹配到的字符串
|
|
原文链接: Chris Beaumont 翻译: 极客范 - 慕容老匹夫
转载链接: http://www.geekfan.net/7862/
Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解。这些特性包括列表/集合/字典推导式,属性(property)、以及装饰器(decorator)。对于大部分特性来说,这些“中级”的语言特性有着完善的文档,并且易于学习。
但是这里有个例外,那就是描述符。至少对于我来说,描述符是Python语言核心中困扰我时间最长的一个特性。这里有几点原因如下:
但是一旦你理解了之后,描述符的确还是有它的应用价值的。这篇文章告诉你描述符可以用来做什么,以及为什么应该引起你的注意。
在这里我要告诉你:从根本上讲,描述符就是可以重复使用的属性。也就是说,描述符可以让你编写这样的代码:
|
|
而在解释器执行上述代码时,当发现你试图访问属性b = f.bar
、对属性赋值f.bar = c
或者删除一个实例变量的属性del f.bar
时,就会去调用自定义的方法。
让我们先来解释一下为什么把对函数的调用伪装成对属性的访问是大有好处的。
想象一下你正在编写管理电影信息的代码。你最后写好的Movie类可能看上去是这样的:
|
|
你开始在项目的其他地方使用这个类,但是之后你意识到:如果不小心给电影打了负分怎么办?你觉得这是错误的行为,希望Movie
类可以阻止这个错误。 你首先想到的办法是将Movie
类修改为这样:
|
|
但这行不通。因为其他部分的代码都是直接通过Movie.budget
来赋值的,这个新修改的类只会在__init__
方法中捕获错误的数据,但对于已经存在的类实例就无能为力了。如果有人试着运行m.budget = -100
,那么谁也没法阻止。作为一个Python程序员同时也是电影迷,你该怎么办?
幸运的是,Python的property
解决了这个问题。如果你从未见过property
的用法,下面是一个示例:
|
|
我们用@property
装饰器指定了一个getter
方法,用@budget.setter
装饰器指定了一个setter
方法。当我们这么做时,每当有人试着访问budget
属性,Python就会自动调用相应的getter/setter
方法。比方说,当遇到m.budget = value
这样的代码时就会自动调用budget.setter
花点时间来欣赏一下Python这么做是多么的优雅:如果没有property
,我们将不得不把所有的实例属性隐藏起来,提供大量显式的类似get_budget
和set_budget
方法。像这样编写类的话,使用起来就会不断的去调用这些getter/setter
方法,这看起来就像臃肿的Java代码一样。更糟的是,如果我们不采用这种编码风格,直接对实例属性进行访问。那么稍后就没法以清晰的方式增加对非负数的条件检查——我们不得不重新创建set_budget
方法,然后搜索整个工程中的源代码,将m.budget = value
这样的代码替换为m.set_budget(value)
。太蛋疼了!!
因此,property
让我们将自定义的代码同变量的访问/设定联系在了一起,同时为你的类保持一个简单的访问属性的接口。干得漂亮!
对property
来说,最大的缺点就是它们不能重复使用。举个例子,假设你想为rating
,runtime
和gross
这些字段也添加非负检查。下面是修改过的新类:
|
|
可以看到代码增加了不少,但重复的逻辑也出现了不少。虽然property
可以让类从外部看起来接口整洁漂亮,但是却做不到内部同样整洁漂亮。
这就是描述符所解决的问题。描述符是property
的升级版,允许你为重复的property
逻辑编写单独的类来处理。下面的示例展示了描述符是如何工作的(现在还不必担心NonNegative
类的实现):
|
|
这里引入了一些新的语法,我们一条条的来看:
NonNegative
是一个描述符对象,因为它定义了__get__
,__set__
或__delete__
方法。
Movie
类现在看起来非常清晰。我们在类的层面上创建了4个描述符,把它们当做普通的实例属性。显然,描述符在这里为我们做非负检查。
当解释器遇到print m.buget
时,它就会把budget
当作一个带有__get__
方法的描述符,调用Movie.budget.__get__
方法并将方法的返回值打印出来,而不是直接传递m.budget
来打印。这和你访问一个property
相似,Python自动调用一个方法,同时返回结果。
__get__
接收2个参数:一个是点号左边的实例对象(在这里,就是m.budget中的m),另一个是这个实例的类型Movie
。在一些Python文档中,Movie
被称作描述符的所有者(owner)。如果我们需要访问Movie.budget
,Python将会调用Movie.budget.__get__(None, Movie)
。可以看到,第一个参数要么是所有者的实例,要么是None
。这些输入参数可能看起来很怪,但是这里它们告诉了你描述符属于哪个对象的一部分。当我们看到NonNegative
类的实现时这一切就合情合理了。
当解释器看到m.rating = 100
时,Python识别出rating
是一个带有__set__
方法的描述符,于是就调用Movie.rating.__set__(m, 100)
。和__get__
一样,__set__
的第一个参数是点号左边的类实例m.rating = 100
中的m
。第二个参数是所赋的值(100)。
为了说明的完整,这里提一下删除。如果你调用del m.budget
,Python就会调用Movie.budget.__delete__(m)
。
带着前面的困惑,我们终于要揭示NonNegative
类是如何工作的了。每个NonNegative
的实例都维护着一个字典,其中保存着所有者实例和对应数据的映射关系。当我们调用m.budget
时,__get__
方法会查找与m
相关联的数据,并返回这个结果(如果这个值不存在,则会返回一个默认值)。__set__
采用的方式相同,但是这里会包含额外的非负检查。我们使用WeakKeyDictionary
来取代普通的字典以防止内存泄露——我们可不想仅仅因为它在描述符的字典中就让一个无用的实例一直存活着。
使用描述符会有一点别扭。因为它们作用于类的层次上,每一个类实例都共享同一个描述符。这就意味着对不同的实例对象而言,描述符不得不手动地管理不同的状态,同时需要显式的将类实例作为第一个参数准确传递给__get__
、__set__
以及__delete__
方法。
我希望这个例子解释清楚了描述符可以用来做什么——它们提供了一种方法将property
的逻辑隔离到单独的类中来处理。如果你发现自己正在不同的property
之间重复着相同的逻辑,那么本文也许会成为一个线索供你思考为何用描述符重构代码是值得一试的。
为了让描述符能够正常工作,它们必须定义在类的层次上。如果你不这么做,那么Python无法自动为你调用__get__
和__set__
方法。
|
|
可以看到,访问类层次上的描述符y
可以自动调用__get__
。但是访问实例层次上的描述符x只会返回描述符本身,真是魔法一般的存在啊。
你可能会像这样编写NonNegative
描述符:
|
|
这么做看起来似乎能正常工作。但这里的问题就在于所有Foo
的实例都共享相同的bar
,这会产生一些令人痛苦的结果:
|
|
这就是为什么我们要在NonNegative
中使用数据字典的原因。__get__
和__set__
的第一个参数告诉我们需要关心哪一个实例。NonNegative
使用这个参数作为字典的key
,为每一个Foo
实例单独保存一份数据。
|
|
这就是描述符最令人感到别扭的地方(坦白的说,我不理解为什么Python不让你在实例的层次上定义描述符,并且总是需要将实际的处理分发给__get__
和__set__
。这么做行不通一定是有原因的)
NonNegative
类使用了一个字典来单独保存专属于实例的数据。这个一般来说是没问题的,除非你用到了不可哈希(unhashable)的对象:
|
|
因为MoProblems
的实例(list
的子类)是不可哈希的,因此它们不能为MoProblems
.x
用做数据字典的key。有一些方法可以规避这个问题,但是都不完美。最好的方法可能就是给你的描述符加标签了。
|
|
这种方法依赖于Python的方法解析顺序(即,MRO)。我们给Foo中的每个描述符加上一个标签名,名称和我们赋值给描述符的变量名相同,比如x = Descriptor(‘x’)
。之后,描述符将特定于实例的数据保存在f.__dict__['x']
中。这个字典条目通常是当我们请求f.x
时Python给出的返回值。然而,由于Foo.x
是一个描述符,Python不能正常的使用f.__dict__[‘x’]
,但是描述符可以安全的在这里存储数据。只是要记住,不要在别的地方也给这个描述符添加标签。
|
|
我不喜欢这种方式,因为这样的代码很脆弱也有很多微妙之处。但这个方法的确很普遍,可以用在不可哈希的所有者类上。David Beazley在他的书中用到了这个方法。
由于描述符的标签名和赋给它的变量名相同,所以有人使用元类来自动处理这个簿记(bookkeeping)任务。
|
|
我不会去解释有关元类的细节——参考文献中David Beazley已经在他的文章中解释的很清楚了。 需要指出的是元类自动的为描述符添加标签,并且和赋给描述符的变量名字相匹配。
尽管这样解决了描述符的标签和变量名不一致的问题,但是却引入了复杂的元类。虽然我很怀疑,但是你可以自行判断这么做是否值得。
描述符仅仅是类,也许你想要为它们增加一些方法。举个例子,描述符是一个用来回调property
的很好的手段。比如我们想要一个类的某个部分的状态发生变化时就立刻通知我们。下面的大部分代码是用来做这个的:
|
|
这是一个很有吸引力的模式——我们可以自定义回调函数用来响应一个类中的状态变化,而且完全无需修改这个类的代码。这样做可真是替人分忧解难呀。现在,我们所要做的就是调用ba.balance.add_callback(ba, low_balance_warning)
,以使得每次balance
变化时low_balance_warning
都会被调用。
但是我们是如何做到的呢?当我们试图访问它们时,描述符总是会调用__get__
。就好像add_callback
方法是无法触及的一样!其实关键在于利用了一种特殊的情况,即,当从类的层次访问时,__get__
方法的第一个参数是None
。
|
|
__setattr__()
或者__getattr__()
方法对__dict__
字典进行处理,而是调用描述符的__get__()
,__set__()
和__delete__()
方法__init__()
函数中创建一个字典,以类实例的地址(例子中的instance
)参数作为key,以要这个实例的数据作为valueself
,因为实例化类时,会自动将分配给实例的内存地址传递该self,也就是所谓的绑定,该函数也就成为绑定函数了,而给实例动态添加的方法以及类之外定义的方法就不需要self
参数了与系统相依赖的一些操作,有些操作只支持unix系统
获取环境变量
|
|
获取当前进程或者指定pid进程的用户和用户组,仅支持unix,详情见os
其中windows平台也可以使用的:
获取当前登陆用户:
|
|
改变与获取当前工作路径
|
|
枚举指定目录,不指定path
参数则默认当前路径
|
|
scandir()
与listdir()
作用相同,但是返回的是迭代器
|
|
而DirEntry
对象包含了与文件相关的属性,详情见:os.DirEntry
mkdir()
创建目录remove()
删除文件rmdir()
删除目录rename()
重命名文件相关信息
|
|
使用电脑上默认应用打开指定文件
|
|
默认快捷键是Ctrl + space
,但是和系统输入法的切换冲突了,并且之前java开发使用习惯了Alt + /
作为代码提示的快捷键,所有将代码提示的快捷键改为了Alt + /
默认快捷键是Ctrl+Shift+Space
,同样因为冲突改为了alt+shift+/
与Ctrl+K F12
效果相同
以上便是常用的VS Code快捷键,不包括插件提供的快捷键,关于其他的快捷键请看参考文档
BeautifulSoup 3只支持python 2,并且已经停止开发,BeautifulSoup支持python2和3,以下使用方法参考4.4版说明文档
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(markup,"html.parser") |
Python的内置标准库执行速度适中文档容错能力强 | Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差 |
lxml HTML 解析器 | BeautifulSoup(markup,"lxml") |
速度快文档容错能力强 | 需要安装C语言库 |
lxml XML 解析器 | BeautifulSoup(markup,["lxml-xml"])``BeautifulSoup(markup,"xml") |
速度快唯一支持XML的解析器 | 需要安装C语言库 |
html5lib | BeautifulSoup(markup,"html5lib") |
最好的容错性以浏览器的方式解析文档生成HTML5格式的文档 | 速度慢不依赖外部扩展 |
如果不指定解析器,BeautifulSoup会自动选择最合适的解析器来解析文档
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag
, NavigableString
, BeautifulSoup
, Comment
.
Tag
对象与XML或HTML原生文档中的tag相同:
|
|
每个tag都有name和attribute:
|
|
可以通过直接赋值来增加或修改tag的名字和属性:
|
|
通过del
删除属性:
|
|
对于多值属性,会返回一个列表,使用的时候注意是返回列表还是字符串:
|
|
如果转换的文档是XML格式,那么tag中不包含多值属性
|
|
字符串常被包含在tag内.Beautiful Soup用 NavigableString
类来包装tag中的字符串:
|
|
tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 replace_with() 方法:
|
|
如果想在Beautiful Soup之外使用 NavigableString
对象,需要调用 unicode()
方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存.
BeautifulSoup
对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag
对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup
对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name
属性是很方便的,所以 BeautifulSoup
对象包含了一个值为 “[document]” 的特殊属性 .name
|
|
|
|
Comment
对象是一个特殊类型的 NavigableString
对象:
|
|
但是当它出现在HTML文档中时, Comment
对象会使用特殊的格式输出:
|
|
我们测试的文档内容:
|
|
通过点取属性的方式只能获得当前名字的第一个tag:
|
|
使用find_all()
获取所有的tag:
|
|
tag的 .contents
属性可以将tag的子节点以列表的方式输出:
|
|
字符串没有 .contents
属性,因为字符串没有子节点:
|
|
通过tag的 .children
生成器,可以对tag的子节点进行循环:
|
|
.descendants
属性可以对所有tag的子孙节点进行递归循环
BeautifulSoup
有一个直接子节点(节点),却有很多子孙节点:
|
|
输出所有string
:
|
|
通过 .parent
属性来获取某个元素的父节点.
通过元素的 .parents
属性可以递归得到元素的所有父辈节点
在文档树中,使用 .next_sibling
和 .previous_sibling
属性来查询兄弟节点
通过 .next_siblings
和 .previous_siblings
属性可以对当前节点的兄弟节点迭代输出
.next_element
属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 .next_sibling
相同,但通常是不一样的.
通过 .next_elements
和 .previous_elements
的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
除了find_all()
之外,搜索也支持正则表达式:
|
|
下面代码找到文档中所有<a>
标签和<b>
标签:
|
|
True
可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
|
|
如果包含一个名字为 id
的参数,Beautiful Soup会搜索每个tag的”id”属性.
|
|
如果传入 href
参数,Beautiful Soup会搜索每个tag的”href”属性:
|
|
下面的例子在文档树中查找所有包含 id
属性的tag,无论 id
的值是什么:
|
|
使用多个指定名字的参数可以同时过滤tag的多个属性:
|
|
通过 string
参数可以搜搜文档中的字符串内容.与 name
参数的可选值一样, string
参数接受 字符串 , 正则表达式 , 列表, True . 看例子:
|
|
虽然 string
参数用于搜索字符串,还可以与其它参数混合使用来过滤tag.Beautiful Soup会找到.string
方法与 string
参数值相符的tag.下面代码用来搜索内容里面包含“Elsie”的<a>
标签:
|
|
限制返回结果的个数:
|
|
下面两行代码是等价的:
|
|
这两行代码也是等价的:
|
|
find_all()
和 find()
只搜索当前节点的所有子节点,孙子节点等. find_parents()
和find_parent()
用来搜索当前节点的父辈节点
find_next_siblings()
方法返回所有符合条件的后面的兄弟节点, find_next_sibling()
只返回符合条件的后面的第一个tag节点.
find_previous_siblings()
方法返回所有符合条件的前面的兄弟节点, find_previous_sibling()
方法返回第一个符合条件的前面的兄弟节点
find_all_next()
方法返回所有符合条件的节点, find_next()
方法返回第一个符合条件的节点
find_all_previous()
方法返回所有符合条件的节点, find_previous()
方法返回第一个符合条件的节点.
CSS选择器:对于熟悉css选择器的开发人员来说,使用这种方法来查找比较简单:
|
|
通过tag标签逐层查找:
|
|
找到某个tag标签下的直接子标签 [6] :
|
|
找到兄弟节点标签:
|
|
通过CSS的类名查找:
|
|
通过tag的id查找:
|
|
同时用多种CSS选择器查询元素:
|
|
通过是否存在某个属性来查找:
|
|
通过属性的值来查找:
|
|
返回查找到的元素的第一个
|
|
Tag.insert()
方法与 Tag.append()
方法类似,区别是不会把新元素添加到父节点 .contents
属性的最后,而是把元素插入到指定的位置.与Python列表总的 .insert()
方法的用法下同:
|
|
Tag.clear()
方法移除当前tag的内容:
|
|
PageElement.extract()
方法将当前tag移除文档树,并作为方法结果返回:
|
|
这个方法实际上产生了2个文档树: 一个是用来解析原始文档的 BeautifulSoup
对象,另一个是被移除并且返回的tag.被移除并返回的tag可以继续调用 extract
方法:
|
|
Tag.decompose()
方法将当前节点移除文档树并完全销毁:
|
|
PageElement.replace_with()
方法移除文档树中的某段内容,并用新tag或文本节点替代它:
|
|
replace_with()
方法返回被替代的tag或文本节点,可以用来浏览或添加到文档树其它地方
PageElement.wrap()
方法可以对指定的tag元素进行包装,并返回包装后的结果:
|
|
该方法在 Beautiful Soup 4.0.5 中添加
Tag.unwrap()
方法与 wrap()
方法相反.将移除tag内的所有tag标签,该方法常被用来进行标记的解包:
|
|
与 replace_with()
方法相同, unwrap()
方法返回被移除的tag
|
|
//www.google.com
.?
separator.
|
|
会根据协议自动识别默认端口,目前仅支持ftp,ssh,http,https
|
|
|
|
|
|
|
|
可以注意到链接末尾的/
被解析为''
,因为它被当作是一个目录:
|
|
对path进行规范化:
|
|
|
|
有关query属性的例子:
|
|
''
与None
参数:
|
|
|
|
fragment的分隔符是?
|
|
|
|
copy() creates and returns a new furl object with an identical URL.
|
|
|
|
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
特点:
优点:
因为redis原生支持linux,所以出现了https://github.com/MSOpenTech/redis,支持windows平台,下载安装包安装即可,并且可以设置最高使用的内存大小,更多配置参考安装目录下的配置文件
|
|
-a
后面是密码,-n
表示连接第几个数据库,默认连接编号为0的数据库
如果默认是本机6397端口,没有密码,可以直接使用以下连接:
|
|
输入quit
退出
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
|
|
SET
与GET
都可以使用小写,但是一般都是用大写,好区分是不是redix命令
其他命令:http://www.runoob.com/redis/redis-strings.html
更多命令见参考文档
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
|
|
user:1
是key
每个 hash 可以存储2^32-1键值对(40多亿)
其他命令:http://www.runoob.com/redis/redis-hashes.html
更多命令见参考文档
|
|
列表最多可存储2^32-1 元素
其他命令:http://www.runoob.com/redis/redis-lists.html
更多命令见参考文档
通过hash实现的,不能保证顺序,元素唯一性
|
|
对于已经存在与set中的元素会返回0
其他命令:http://www.runoob.com/redis/redis-sets.html
更多命令见参考文档
元素不重复并且保持插入元素的顺序,与Set不同的是,zset中的每个元素有都个score
属性,可以理解为权重,内部是按照权重的大小进行排序的
|
|
其他命令:http://www.runoob.com/redis/redis-sorted-sets.html
更多命令见参考文档
以上的name
,test_list
,test_set
,test_zset
和uesr:1
都是key
可以通过DEL
命令来删除key
序号 | 命令及描述 |
---|---|
1 | DEL key该命令用于在 key 存在时删除 key。 |
2 | DUMP key 序列化给定 key ,并返回被序列化的值。 |
3 | EXISTS key 检查给定 key 是否存在。 |
4 | EXPIRE key seconds为给定 key 设置过期时间。 |
5 | EXPIREAT key timestamp EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 |
6 | PEXPIRE key milliseconds 设置 key 的过期时间以毫秒计。 |
7 | PEXPIREAT key milliseconds-timestamp 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计 |
8 | KEYS pattern 查找所有符合给定模式( pattern)的 key 。 |
9 | MOVE key db 将当前数据库的 key 移动到给定的数据库 db 当中。 |
10 | PERSIST key 移除 key 的过期时间,key 将持久保持。 |
11 | PTTL key 以毫秒为单位返回 key 的剩余的过期时间。 |
12 | TTL key 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 |
13 | RANDOMKEY 从当前数据库中随机返回一个 key 。 |
14 | RENAME key newkey 修改 key 的名称 |
15 | RENAMENX key newkey 仅当 newkey 不存在时,将 key 改名为 newkey 。 |
16 | TYPE key 返回 key 所储存的值的类型。 |
序号 | 命令及描述 |
---|---|
1 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC 执行所有事务块内的命令。 |
3 | MULTI 标记一个事务块的开始。 |
4 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 | WATCH key [key …] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
输入INFO
可以获取 Redis 服务器的各种信息和统计数值
|
|
该命令将在 redis 安装目录中创建dump.rdb
文件。
如果需要恢复数据,只需将备份文件 dump.rdb
移动到 redis 安装目录并启动服务即可。获取 redis 目录可以使用 CONFIG
命令
|
|
创建 redis 备份文件也可以使用命令 BGSAVE
,该命令在后台执行。
FLUSHDB
清除一个数据库,FLUSHALL
清除整个redis数据
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
redis-py
是python实现的redis客户端,关于redis-py
的使用参考:
MongoDB官方出的针对python平台的库,相当于数据库的客户端,所以需要安装MongoDB的服务器端,按照Install MongoDB Community Edition on Windows说明可以在windows平台上安装MongoDB
并在管理员权限的cmd窗口运行:
|
|
将会产生系统服务,mongod.cfg
文件内容:
|
|
针对PyMongo3.3.1
|
|
不指定服务器地址和端口就是默认localhost下的27017端口,也就是:
|
|
|
|
底层检查是否有test_database
这个属性,如果有,获取的就是test_database
数据库,如果没有,则创建test_database
数据库,也可以使用如下方式创建数据库:
|
|
|
|
与创建数据库基本一样
|
|
之前的各种操作都不会产生数据文件,只有在插入文档的时候,才连接服务器,产生相应的数据文件
|
|
也可以在插入文档的同时返回插入文档的id
:
|
|
insert_many()
可以插入多个文档
find_one()
查找的是符合条件的第一个文档
|
|
根据id
查找文档:
|
|
也可以如下:
|
|
输出所有符合条件的文档:
|
|
sort("_id")
表示按id
列排序
获取集合中文档数:
|
|
我们先将ObjectId('582404adf67a2f29f4cb8564')
中的x
值改为11
然后在x
上创建索引
|
|
列出所有的索引:
|
|
索引'_id_'
是根据_id
自动创建的
其他基础操作比如更新,删除的语法与命令行Mongo类似,在此不赘述
以上便是PyMongo的基本操作,高级操作可参考:
http://api.mongodb.com/python/3.3.1/examples/aggregation.html
API参考:
http://api.mongodb.com/python/3.3.1/api/index.html
MongoDB是对象型数据库,mysql等关系型数据库的表格式固定,如果想增添带有更多信息的属性就需要重新建一张表,然后用外键进行关联,这样查询也会造成表之间的join
,效率低,而且结构越复杂,表越多,表之间的关系就越紧密,会影响表之间的清晰度。而对象型数据库将每条记录看作是一个文档,以json格式存放在一个文件中,并且每个文档结构可以不同,一个文档中就包含了这条记录的所有相关信息,以面对对象的思维来看就是一个对象,文档的集合也就是关系型数据库记录的集合,也就是表
|
|
|
|
db
这个变量的值就是我们当前使用的数据库
|
|
在该命令中,name
是所要创建的集合名称。options
是一个用来指定集合配置的文档。
|
|
|
|
|
|
在插入的文档中,如果我们没有指定 _id
参数,那么 MongoDB 会自动为文档指定一个唯一的 ID
|
|
|
|
用格式化方式显示结果,使用的是 pretty()
方法。除了 find()
方法之外,还有一个 findOne()
方法,它只返回一个文档。
操作 | 格式 | 范例 | RDBMS中的类似语句 |
---|---|---|---|
等于 | {<key>:<value>} |
db.mycol.find({"by":"tutorials point"}).pretty() |
where by = 'tutorials point' |
小于 | {<key>:{$lt:<value>}} |
db.mycol.find({"likes":{$lt:50}}).pretty() |
where likes < 50 |
小于或等于 | {<key>:{$lte:<value>}} |
db.mycol.find({"likes":{$lte:50}}).pretty() |
where likes <= 50 |
大于 | {<key>:{$gt:<value>}} |
db.mycol.find({"likes":{$gt:50}}).pretty() |
where likes > 50 |
大于或等于 | {<key>:{$gte:<value>}} |
db.mycol.find({"likes":{$gte:50}}).pretty() |
where likes >= 50 |
不等于 | {<key>:{$ne:<value>}} |
db.mycol.find({"likes":{$ne:50}}).pretty() |
where likes != 50 |
and
语法就用逗号表示:
|
|
or
语法:
|
|
|
|
MongoDB 中的 update()
与 save()
方法都能用于更新集合中的文档。update()
方法更新已有文档中的值,而save()
方法则是用传入该方法的文档来替换已有文档。
MongoDB 默认只更新单个文档,要想更新多个文档,需要把参数 multi
设为 true
。
|
|
|
|
$inc
表示将likes
值加2
|
|
如果有多个记录,而你只想删除第一条记录,那么就设置 remove()
方法中的 justOne
参数:
|
|
如果没有指定删除标准,则 MongoDB 会将集合中所有文档都予以删除。
|
|
MongoDB 的查询文档曾介绍过 find()
方法,它可以利用 AND 或 OR 条件来获取想要的字段列表。在 MongoDB 中执行 find()
方法时,显示的是一个文档的所有字段。要想限制,可以利用 0 或 1 来设置字段列表。1 用于显示字段,0 用于隐藏字段。
|
|
假如 mycol 集合拥有下列数据:
|
|
|
|
注意:在执行 find()
方法时,_id
字段是一直显示的。如果不想显示该字段,则可以将其设为 0。
|
|
MongoDB 中的文档排序是通过 sort()
方法来实现的。sort()
方法可以通过一些参数来指定要进行排序的字段,并使用 1 和 -1 来指定排序方式,其中 1 表示升序,而 -1 表示降序。
|
|
|
|
这里的 key 是想创建索引的字段名称,1 代表按升序排列字段值。-1 代表按降序排列。
获取索引信息:
|
|
将返回所有索引,包括其名字。
删除索引:
|
|
相当于关系型数据库中的group by
|
|
比如有集合:
|
|
假如想从上述集合中,归纳出一个列表,以显示每个用户写的教程数量,需要像下面这样使用 aggregate()
方法:
|
|
表达式 | 描述 | 范例 |
---|---|---|
$sum |
对集合中所有文档的定义值进行加和操作 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}]) |
$avg |
对集合中所有文档的定义值进行平均值 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}]) |
$min |
计算集合中所有文档的对应值中的最小值 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}]) |
$max |
计算集合中所有文档的对应值中的最大值 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}]) |
$push |
将值插入到一个结果文档的数组中 | db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}]) |
$addToSet |
将值插入到一个结果文档的数组中,但不进行复制 | db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}]) |
$first |
根据成组方式,从源文档中获取第一个文档。但只有对之前应用过 $sort 管道操作符的结果才有意义。 |
db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}]) |
$last |
根据成组方式,从源文档中获取最后一个文档。但只有对之前进行过 $sort 管道操作符的结果才有意义。 |
db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}]) |
|
|
query
是查找出匹配的文档,和find
是一样的,而update
则是更新likes
这个项目。注意由于MongoDB只支持单个文档的atomic operation,因此如果query
出多于一个文档,则只会对第一个文档进行操作。
|
|
注意以上匹配都是区分大小写的,如果你要让其不区分大小写,则可以:
|
|
$i
是insensitive的意思。这样的话,即使是小写的fight,也能搜到了。
dataset号称是为懒人所写的数据库,并说明了很多程序员存储数据都会使用不易查询和更新的CSV和JSON格式,而不是数据库,主要原因是数据库的相关代码比较复杂,而dataset正式解决这个问题,为程序员提供更方便的数据库操作
|
|
Features:
all
rows in a table or all distinct
values across a set of columns.dataset
works with all major databases, such as SQLite, PostgreSQL and MySQL.
|
|
dataset会根据输入自动创建表和字段名
|
|
将产生(主键id
自动生成):
id | country | name | age | gender |
---|---|---|---|---|
1 | China | John Doe | 46 | |
2 | France | Jane Doe | 37 | female |
|
|
第二个参数相当于sql update语句中的where
,用来过滤出需要更新的记录
事务操作可以简单的使用上下文管理器来实现,出现异常,将会回滚
|
|
等同于:
|
|
也可以嵌套使用:
|
|
|
|
|
|
获取非重复数据
|
|
|
|
|
|
|
|
一个比较方便的连接mysql使用的python库,官网给的例子很简单,但是看下源码发现内容还是很多的,很多函数都没有介绍,所以只有在使用的时候查看源代码了。从github上该项目所获得的星数来看,该库还是很出名的。
|
|
结果:
|
|
可以使用geopy库来查询地址,国家,城市,地标,geopy使用的是第三方的geo解析器(包括谷歌地图,必应地图,Nominatim等)和一些数据源来获取地理信息
Each geolocation service you might use, such as Google Maps, Bing Maps, or Yahoo BOSS, has its own class in geopy.geocoders
abstracting the service’s API. Geocoders each define at least ageocode
method, for resolving a location from a string, and may define a reverse
method, which resolves a pair of coordinates to an address.
Location
对象
|
|
Location
对象
|
|
可以使用 Vincenty distance 或 great-circle distance
|
|
|
|
class
geopy.geocoders.ArcGIS
(username=None, password=None, referer=None, token_lifetime=60,scheme=’https’, timeout=1, proxies=None, user_agent=None)
class
geopy.geocoders.Baidu
(api_key, scheme=’http’, timeout=1, proxies=None, user_agent=None)
class
geopy.geocoders.Bing
(api_key, format_string=’%s’, scheme=’https’, timeout=1, proxies=None,user_agent=None)
class
geopy.geocoders.DataBC
(scheme=’https’, timeout=1, proxies=None, user_agent=None)
class
geopy.geocoders.GeocodeFarm
(api_key=None, format_string=’%s’, timeout=1, proxies=None,user_agent=None)
class
geopy.geocoders.GeocoderDotUS
(username=None, password=None, format_string=’%s’,timeout=1, proxies=None, user_agent=None)
class
geopy.geocoders.GeoNames
(country_bias=None, username=None, timeout=1, proxies=None,user_agent=None)
class
geopy.geocoders.GoogleV3
(api_key=None, domain=’maps.googleapis.com’, scheme=’https’,client_id=None, secret_key=None, timeout=1, proxies=None, user_agent=None)
class
geopy.geocoders.IGNFrance
(api_key, username=None, password=None, referer=None,domain=’wxs.ign.fr’, scheme=’https’, timeout=1, proxies=None, user_agent=None)
class
geopy.geocoders.LiveAddress
(auth_id, auth_token, candidates=1, scheme=’https’, timeout=1,proxies=None, user_agent=None)
class
geopy.geocoders.NaviData
(api_key=None, domain=’api.navidata.pl’, timeout=1, proxies=None,user_agent=None)
class
geopy.geocoders.Nominatim
(format_string=’%s’, view_box=None, country_bias=None, timeout=1,proxies=None, domain=’nominatim.openstreetmap.org’, scheme=’https’, user_agent=None)
class
geopy.geocoders.OpenCage
(api_key, domain=’api.opencagedata.com’, scheme=’https’, timeout=1,proxies=None, user_agent=None)
class
geopy.geocoders.OpenMapQuest
(api_key=None, format_string=’%s’, scheme=’https’, timeout=1,proxies=None, user_agent=None)
class
geopy.geocoders.Photon
(format_string=’%s’, scheme=’https’, timeout=1, proxies=None,domain=’photon.komoot.de’)
class
geopy.geocoders.YahooPlaceFinder
(consumer_key, consumer_secret, timeout=1, proxies=None,user_agent=None)
class
geopy.geocoders.What3Words
(api_key, format_string=’%s’, scheme=’https’, timeout=1,proxies=None, user_agent=None)
class
geopy.geocoders.Yandex
(api_key=None, lang=None, timeout=1, proxies=None, user_agent=None)
moviepy能够对音频,视频,以及git图片进行剪切,合并,标题插入等处理,并支持多种格式。
moviepy也是基于ffmpeg,如果没有安装ffmpeg,moviepy会在第一次使用moviepy的时候自动下载安装ffmpeg,如果本机安装有ffmpeg,建议修改config_defaults.py
文件中的配置为FFMPEG_BINARY = 'auto-detect'
至于其他工具,则是对应相应的工具自行决定要不要安装,比如增加文字需要ImageMagick,预览音频和视频需要PyGame
moviepy的核心对象是clips
,可以是AudioClips
或 VideoClips
|
|
VideoClip
is the base class for all the other video clips in MoviePy. If all you want is to edit video files, you will never need it. This class is practical when you want to make animations from frames that are generated by another library. All you need is to define a function make_frame(t)
which returns a HxWx3 numpy array (of 8-bits integers) representing the frame at time t. Here is an example with the graphics library Gizeh
:
|
|
This is a clip made from a series of images, you call it with:
|
|
where images_list
can be either a list of image names (that will be played) in that order, a folder name (at which case all the image files in the folder will be played in alphanumerical order), or a list of frames (Numpy arrays), obtained for instance from other clips.
Generating a TextClip requires to have ImageMagick installed and (for windows users) linked to MoviePy
|
|
Sometimes it is impossible for MoviePy to guess the duration
attribute of the clip (keep in mind that some clips, like ImageClips displaying a picture, have a priori an infinite duration). Then, the duration
must be set manually with clip.set_duration
:
|
|
To write your video as an animated GIF, use
|
|
You can write a frame to an image file with
|
|
|
|
CompositeVideoClips
也能合并clips
|
|
|
|
https://zulko.github.io/moviepy/examples/examples.html