必用模块
re
logging
hashlib hmac
subprocess
sys
json pickle
2
3
4
5
6
# re模块
正则就是用一系列具有特殊含义的字符组成规则,该规则用来描述具有某一特征的字符串
常用正则,参考网址:
https://r2coding.com/#/README?id=正则表达式
!!
# -- 'a\n'前面加r等同于'a\\c',告知解释器里面的\不是转义字符
>>> len('a\\n')
3
>>> len(r'a\n')
3
2
3
4
5
# 表达式/规则
# 单个字符
下方的正则表达式代表 一个 字符/原子.
表达式 | 描述 |
---|---|
[abc] | 原子表里面包含一组地位平等的原子, 取其中任意一个进行匹配 |
[^abc] | 除了中括号里面的原子均可以匹配. |
[a-z] | 字符范围. 匹配指定范围内的任意字符 |
. | 匹配除换行符以外的任何单个字符 |
\ | 用于转义. |
\w | 匹配一个字母数字或下划线, 等价于[A-Za-z0-9_] |
\W | 匹配一个非字母数字下划线的字符, 等价于[^A-Za-z0-9_] |
\d | 匹配一个数字, 等价于 [0-9] |
\D | 匹配一个非数字字符 |
\s | 匹配一个空白字符, 包括空格、制表符等.. 等价于 [\t\n\r\f] |
\S | 匹配一个非空白字符 |
\t | 匹配一个制表符 |
\n | 匹配一个换行符 |
>>> import re
# -- findall(pattern, string) 返回string中所有与pattern匹配的全部字符串,返回形式为数组
>>> re.findall('\s','hell\no 12\t3_ */-=')
['\n', ' ', '\t', ' ']
>>> re.findall('dc','dc is dc')
['dc', 'dc']
>>> re.findall('[ay3]','123apy')
['3', 'a', 'y']
>>> re.findall('[^y3]py','123apy')
['apy']
# \- 转义'使得 - 不再代表范围'
>>> re.findall('a[a-zA-Z+\-*/]c','abc a2c aVc a+c a-c a/c aaa\nc')
['abc', 'aVc', 'a+c', 'a-c', 'a/c']
>>> re.findall('a[a-z][A-Z]c','abc abVc aaa1ac')
['abVc']
>>> re.findall('a.c','abc a2c aer aaa\nc aaa1ac')
['abc', 'a2c']
>>> re.findall('a.c','abc a2c aer aaa\nc aaa1ac',re.DOTALL) # -- re.DOTALL 使 . 包含换行符\n
['abc', 'a2c', 'a\nc']
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 瞄点/边界
表达式 | 描述 |
---|---|
^ | 匹配字符串或行开头 |
$ | 匹配字符串或行结尾 |
\b | 匹配单词边界。比如Sheep\b 可以匹配CodeSheep 末尾的Sheep ,不能匹配CodeSheepCode 中的Sheep |
\B | 匹配非单词边界。比如Code\B 可以匹配HelloCodeSheep 中的Code ,不能匹配HelloCode 中的Code 。 |
>>> re.findall('^dc','my name is dc')
[]
>>> re.findall('^dc','dcdc is cool!')
['dc']
2
3
4
# 数量表示
注意哦!
*
、?
、+
、{n,m}
不能单独使用, 搭配字符使用.
表达式 | 描述 |
---|---|
? | 代表?左侧的那个字符可出现0次或1次 {0,1} |
+ | 代表?左侧的那个字符可出现1次或无穷多次 {1,} |
* | 代表*左侧的那个字符可出现0次或无穷多次 {0,} |
| | 或运算符. 并集, 可以匹配符号前后的表达式 eg: a|b 匹配a或b |
{m} | 代表左侧的那个字符恰好出现m次 |
{m,} | 代表左侧的那个字符至少出现m次 |
{m,n} | 代表左侧的那个字符至少出现m次,至多出现n次 |
Ps: 表格中说左侧的字符, 不准确, 要注意正则表达式中存在分组的情况.
>>> re.findall('ac*12','a012 a12 acc12 acd12 accc12')
['a12', 'acc12', 'accc12']
>>> re.findall('ac?','a012 a12 acc12')
['a', 'a', 'ac']
>>> re.findall('ac?d','ad012 ac acd accd12 acc12')
['ad', 'acd']
>>> re.findall('cd{1,}','abcdddcdcdcd')
['cddd', 'cd', 'cd', 'cd']
2
3
4
5
6
7
8
# 贪婪与非贪婪!
.*
: 贪婪匹配 (不够精准)
.*?
: 非贪婪匹配
# -- .* 除换行符的任意字符可以出现0次获无穷次 换言之可以匹配除换行符外任意多个任意字符
# -- .*? 可以没有字符 ( ̄▽ ̄)
>>> re.findall('a.*','abs2131c12312kkjkasc23')
['abs2131c12312kkjkasc23']
>>> re.findall('a.*','abs2131c1\n2312kkjkasc23') # -- 注意哦,有个\n
['abs2131c1', 'asc23']
>>> re.findall('a.*','abs2131c1\n2312kkjkasc23',re.DOTALL)
['abs2131c1\n2312kkjkasc23']
>>> re.findall('a.*?','abs2131c12312kkjkasc23')
['a', 'a']
>>> re.findall('a.*c','abs2131c12312kkjkasc23') # -- 贪婪,碰到c后还不停止
['abs2131c12312kkjkasc']
>>> re.findall('a.*?c','abs2131c12312kkjkasc23') # -- 非贪婪,碰到c就停止
['abs2131c', 'asc']
>>> re.findall('a.*c?','abs2131c12312kkjkasc23') # -- c可以出现0或1次,那不就等同于 'a.*' 嘛
['abs2131c12312kkjkasc23']
>>> re.findall('a.*?c?','abs2131c12312kkjkasc23') # -- 等同于 'a.*?'
['a', 'a']
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 分组与引用
表达式 | 描述 |
---|---|
(expression) | 分组. 小括号括起来的字符会被当成一个整体来匹配 |
(?:expression) | 非捕获分组. 匹配括号里的整个字符串但不获取匹配结果, 拿不到分组引用. |
\num | 对前面所匹配分组的引用. 比如 (\d)\1 可以匹配两个相同的数字,(Code)(Sheep)\1\2 则可以匹配 CodeSheepCodeSheep 。 |
>>> re.findall('c(d){1,}','abcdddcdcdcd')
['d', 'd', 'd', 'd']
# -- 先得到['cdr','cdcdcdr'],然后再从结果中取'cd'
>>> re.findall('(cd){1,}r','abcdrddcdcdcdr')
['cd', 'cd']
>>> re.findall('(?:cd){1,}r','abcdrddcdcdcdr')
['cdr', 'cdcdcdr']
>>> re.findall('compan(?:ies|y)','too many companies hava ..., next company is ..')
['companies', 'company']
>>> re.findall('compan(ies|y)','too many companies hava ..., next company is ..')
['ies', 'y']
>>> re.findall('exp=".*"','exp="1+2+3/4*5" egon="cool"')
['exp="1+2+3/4*5" egon="cool"']
>>> re.findall('exp=".*?"','exp="1+2+3/4*5" egon="cool"')
['exp="1+2+3/4*5"']
>>> re.findall('exp="(.*?)"','exp="1+2+3/4*5" egon="cool"')
['1+2+3/4*5']
>>> re.findall('(exp)="(.*?)"','exp="1+2+3/4*5" egon="cool"')
[('exp', '1+2+3/4*5')]
# -- 'href="(.*?)"' 取网址
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 预查断言
表达式 | 描述 |
---|---|
(?=) | 正向预查. 比如 Code(?=Sheep) 能匹配 CodeSheep 中的 Code , 但不能匹配 CodePig 中的 Code |
(?!) | 正向否定预查. 比如 Code(?!Sheep) 不能匹配 CodeSheep 中的 Code , 但能匹配 CodePig 中的Code |
(?<=) | 反向预查. 比如 (?<=Code)Sheep 能匹配 CodeSheep 中的Sheep ,但不能匹配ReadSheep 中的Sheep |
(?<!) | 反向否定预查 比如 (?<!Code)Sheep 不能匹配 CodeSheep 中的Sheep ,但能匹配ReadSheep 中的Sheep |
# 常用函数
# re.search()
re.search(): 从左往右匹配,匹配成功一个就结束
若存在返回对象,不存在返回None
注意,search方法中的正则加了括号进行分组,取的依旧是整体
>>> re.search('dc','my name is dc, dc is cool.') # -- 只会找到一个,返回的是对象
<re.Match object; span=(11, 13), match='dc'>
>>> re.search('dc','my name is dc, dc is cool.').group() # -- 将对象的结果拿出来
'dc'
>>> re.search('d(c)','my name is dc, dc is cool.') # -- search加了括号取的也是整体
<re.Match object; span=(11, 13), match='dc'>
>>> re.search('abcd','my').group() # -- 没有找到,返回None. None对象是没有group()方法的,报错.
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
re.search('abcd','my').group()
AttributeError: 'NoneType' object has no attribute 'group'
# -- 相当于,在search方法中 'd(dc){2}'等同于'd(:?dc){2}'
>>> re.search('d(dc){2}','my name is ddc, ddcdc is cool.').group()
'ddcdc'
>>> re.search('d(:?dc){2}','my name is ddc, ddcdc is cool.').group()
'ddcdc'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# re.match()
有点类似于search, 不同于的是它是从头开始匹配,开头不符合就返回None.
>>> re.search('my','myismy')
<re.Match object; span=(0, 2), match='my'>
>>> re.match('my','myismy')
<re.Match object; span=(0, 2), match='my'>
>>> re.match('my','ismy') # - 等同于 re.search('^my','ismy') 此处返回的是None
>>>
2
3
4
5
6
# re.findall()
从头到尾找到全部
>>> re.findall('dc','my name is dc, dc is cool.')
['dc', 'dc']
>>> re.findall('d(c)','my name is dc, dc is cool.')
['c', 'c']
2
3
4
# re.split()
相比于字符串内置的split函数更强大!!
>>> 'egon:18:male'.split(':')
['egon', '18', 'male']
>>> re.split(':','egon:18:male')
['egon', '18', 'male']
>>> re.split('[o8a:e]','egon:18:male') # -- []里的字符都是分割符
['', 'g', 'n', '1', '', 'm', 'l', '']
2
3
4
5
6
# re.sub()
re.sub('old','new','string','num') 替换 注意哦! 没有改变原字符串 , 因为字符串是不可变的.
>>> re.sub('egon','dc','egon:18:male')
'dc:18:male'
# -- 'sub aaxx' ' is wxx' ' is vxx' 都被替换成了'dc'
>>> re.sub('.*?xx','dc','sub aaxx is wxx is vxx is good')
'dcdcdc is good'
>>> re.sub('[a-z]+xx','dc','sub aaxx is wxx is vxx is good')
'sub dc is dc is dc is good'
2
3
4
5
6
7
# re.compile()
pattern = re.compile('正则') 可以预存一个正则表达式,反复使用!
>>> pattern = re.compile('egon')
>>> pattern.findall('egon is cool.my name is egon。')
['egon', 'egon']
>>> pattern.search('egon is cool.my name is egon。')
<re.Match object; span=(0, 4), match='egon'>
2
3
4
5
# logging模块
logging模块包含四种角色: Logger、Filter、Formatter、Handler
Filter 过滤日志 暂时不会用.参考链接:
https://www.cnblogs.com/linhaifeng/articles/6384466.html#_label12
"""
★ --日志格式
"""
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准表示时间,浮点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以来的毫秒数
%(asctime)s 字符串形式的当前时间. 默认格式是 "2003-07-08 16:49:45,896".逗号后面的是毫秒
%(thread)d 线程ID,可能没有
%(threadName)s 线程名,可能没有
%(process)d 进程ID,可能没有
%(message)s 用户输出的消息
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 第一种方式
cmd控制台屏幕输出: 2022-09-26 14:45:01: 小明给了小红100
a1.log文件输出: 2022-09-26 14:45:01 PM - 交易日志 - INFO - 1: 小明给了小红100
a2.log文件输出: 2022-09-26 14:45:01: 小明给了小红100
# -- 1.py
import logging
"""
★ --Logger: 负责产生日志信息
"""
# -- 此处'交易日志'是Logger日志的名字
logger1 = logging.getLogger('交易日志')
"""
★ --Formatter: 控制日志格式
"""
formatter1 = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
)
formatter2 = logging.Formatter(
fmt='%(asctime)s: %(message)s',
datefmt='%Y-%m-%d %X',
)
"""
★ --Handler: 负责日志输出的目标
"""
h1 = logging.FileHandler(filename='a1.log', encoding='utf-8')
h2 = logging.FileHandler(filename='a2.log', encoding='utf-8')
sm = logging.StreamHandler() # -- 屏幕
"""
★ --▲绑定logger对象与handler对象
"""
logger1.addHandler(h1)
logger1.addHandler(h2)
logger1.addHandler(sm)
"""
★ --▲绑定handler对象与formatter对象
"""
h1.setFormatter(formatter1)
h2.setFormatter(formatter2)
sm.setFormatter(formatter2)
"""
★ --设置日志级别: 可以在logger与handler这两个角色处进行设置
日志级别有5档: debug -> info -> warning(默认) -> error -> critical
分别对应数字: 调试10 - 消息20 - 警告30 - 错误40 - 严重50
1> 若设置日志级别为error,那么会记录error和critical的信息
2> 若设置了`logger1.setLevel(30)`,那么`h1.setLevel(10)`是不生效的.
handler设置的日志级别应该大于等于logger!!
"""
# -- logger1.setLevel(30)
h1.setLevel(10)
logger1.info('小明给了小红100')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 第二种方式
我们基本上都会在settings.py文件中进行日志的配置
对象 | 含义 |
---|---|
logger | 产生日志的对象 |
filter | 过滤日志的对象 |
handler | 接收日志然后控制打印到不同的地方 FileHandler用来打印到文件中,StreamHandler用来打印到终端 |
formatter | 可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用 以此来控制不同的Handler的日志格式 |
# -- settings.py
import os
import logging.config
"""
★ --自定义日志输出格式
standard_format 标准格式
simple_format 简单格式
"""
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
"""
★ --log文件的路径
"""
logfile_dir = os.path.dirname(os.path.abspath(__file__)) # -- log文件的目录
logfile_name = 'all2.log' # -- log文件名
# 如果不存在定义的日志目录就创建一个
# if not os.path.isdir(logfile_dir):
# os.mkdir(logfile_dir)
logfile_path = os.path.join(logfile_dir, logfile_name) # -- 拼接log文件路径
"""
★ --logging配置字典
▲handler&formatter logger&handler
"""
LOGGING_DIC = {
'version': 1, # -- 版本号
'disable_existing_loggers': False,
'formatters': {
# -- 注意:'standard'、'simple'是可自己定义的名字 format是固定的名字不能改
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
# -- 自定义了两个handler对象 console和default
'handlers': {
# -- 1> 打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # -- 'logging.StreamHandler'表明打印到屏幕
'formatter': 'simple' # -- 与formatter进行绑定
},
# -- 2> 打印到文件的日志,收集DEBUG及以上的日志
'default1': {
'level': 'DEBUG', # -- 设置日志级别(第二层的关卡)
'class': 'logging.FileHandler', # -- logging.FileHandler表明保存到文件
'formatter': 'standard', # -- formatter的值是前面formatters部分中自定义的
'filename': logfile_path, # -- 日志文件名,准确点说是日志输出路径
'encoding': 'utf-8', # -- 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
# -- 默认配置
'': {
# -- 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'handlers': ['console', 'default1'],
'level': 'DEBUG', # 设置日志级别(第一层关卡)
'propagate': False, # 这个配置关乎logger的继承
},
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging字典配置
# -- logging.getLogger('可指定任意的日志名')
# -- 会去字典的loggers属性里面找,没找到就用" "默认配置
logger = logging.getLogger('交易日志')
# print(logger) # -- <Logger logger1 (DEBUG)>
logger.debug('It debug!')
logger.info('It works!')
if __name__ == '__main__':
load_my_logging_cfg()
"""
cmd控制台输出:
[DEBUG][2022-09-26 15:57:30,103][settings.py:70]It debug!
[INFO][2022-09-26 15:57:30,104][settings.py:71]It works!
all2.log文件:
[2022-09-26 15:57:30,103][MainThread:4670004736][task_id:交易日志][settings.py:70][DEBUG][It debug!]
[2022-09-26 15:57:30,104][MainThread:4670004736][task_id:交易日志][settings.py:71][INFO][It works!]
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 纯享版
import logging.config
import os
base_dir = os.path.dirname(os.path.dirname(__file__))
base_db = os.path.join(base_dir, "db")
base_log = os.path.join(base_dir, "log")
"""
★ --logging日志配置
"""
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
logfile_name = 'ATM.log'
if not os.path.isdir(base_log):
os.mkdir(base_log)
logfile_path = os.path.join(base_log, logfile_name)
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'formatter': 'standard',
'filename': logfile_path,
'encoding': 'utf-8',
},
},
'loggers': {
'': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
'propagate': False,
},
},
}
if __name__ == '__main__':
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC)
logger = logging.getLogger('交易日志')
logger.debug('It debug!')
logger.info('It works!')
load_my_logging_cfg()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# hashlib模块
hash是一种算法,该算法接受传入的内容(文本文件--字符;视频文件--二进制),经过运算得到一串hash值.
主要提供SHA1 SHA224 SHA256 SHA384 SHA512 MD5算法
# hash三大特性
1> 只要传入的内容一样, 得到的hash值必然一样!
在浏览器中下载文件,最后会停顿下,就是在做hash校验,避免在网络传输过程中文件出错,保证文件完整.
2> 只要我们使用的hash算法固定,无论传入的内容有多大,得到的hash值的长度是固定的
下载的文件内容是1个T呢?是否意味着hash值也会占很大的内存?所以hash值长度需要固定.
3> 不可以用hash值逆推出原来的内容,单向的
在网络传输过程中的包可能会被截获. 若账号密码是明文的那就完蛋了.
基于1和2可以在下载文件时做文件 一致性校验
基于1和3可以对密码进行 加密
import hashlib
# -- 1> 造出hash工厂
# 还可以是其它算法 m = hashlib.sha512()
# m = hashlib.md5('python'.encode('utf8')) 可以先传一段数据.
m = hashlib.md5()
# -- 2> 运送原材料
# update值接收bytes类型的数据
# -- 注意:可以将一段很长的数据update多次,与一次update这段长数据,得到的结果是一样的.
m.update('hello'.encode('utf8'))
m.update('world'.encode('utf8'))
# -- 3> 产出hash值 "一串由小写字母和数字构成的字符串"
print(m.hexdigest()) # fc5e038d38a57032085441e7fe7010b0
2
3
4
5
6
7
8
9
10
11
12
13
# 文件一致性校验
# 原理
客户端从服务端下载一个文件, S传输文件给C的同时,会传给C一个加密该文件后得到的hash值.
传输完毕,C同样对文件进行加密,比对hash值是否相同.. 若相同则证明文件是完整的,没有被篡改过.
Q: 中间人需要如何攻击呢?
A: 它需要得到所有的数据包,(因为文件是一段一段传输的),并且篡改加入攻击代码,给一个新的hash值.
这样客户端才发觉不到异常.. 但凡某一环节出现问题,攻击都会失败..
# 实现
Q: m.update(f.read())
文件过大可能就卡死了!
A: 一行一行的拿出来虽然解决了内存问题,但若视频文件有1个T,要循环许久许久.
我们不应该将文件真的全读完,应采用比例的方式缩短算hash值的时间
c/s端都按照我们自定义的方式进行hash校验!!
import hashlib
m = hashlib.md5()
with open('01.mp4', 'rb') as f:
for line in f:
m.update(line)
print(m.hexdigest())
2
3
4
5
6
7
# 明文密码加密
(´▽`)没有绝对的安全哦! 有程序就有漏洞,只需提升安全等级,让破坏者的破解成本远远大于其收益.
# 原理
用户注册后, 服务端是有一份正确的用户名和对应的用哈希算法(假设是md5)加密后的密码的;
客户端用户在登录界面输入密码后,会用md5进行加密,并将用户名与加密后的密码传到服务端进行比对..
Q: 中间人需要如何攻击呢?
A: 哪怕中间人截获了C传给S的数据包,拿到加密后的hash值也反推不了密码明文.
但中间人可以进行 撞库 (抓包 + 猜到用的hash算法 + 常用的密码字典), 蒙你的密码是多少..
一旦密码字典中的某个value通过相同的hash算法算出的hash值与抓包截获的一致.. 密码就被破解了.
# 实现
import hashlib
pwd = input('>>>:')
m = hashlib.md5()
m.update(pwd.encode('utf-8'))
print(m.hexdigest())
2
3
4
5
6
解决'撞库', 办法一:使用强密码;办法二: 密码加盐.
m = hashlib.md5() # -- 不加盐
m.update(pwd.encode('utf-8'))
# -- C/S端是知道加的这个'盐'是'天王盖地虎',但破坏者不知道!
# -- 更狠一点,加盐加在密码的某些位置..
m = hashlib.md5('天王盖地虎'.encode('utf-8')) # -- 加盐
m.update(pwd.encode('utf-8'))
2
3
4
5
6
7
# 补充: hmac模块
python的 hmac模块 -- 基于密钥的消息验证.
它内部会对我们创建key和内容进行进一步的处理后再加密!
import hmac
# -- key值'小鸡炖蘑菇',就是我们加的盐 相当于强制加'盐'
m = hmac.new('小鸡炖蘑菇'.encode('utf8'), digestmod='md5')
m.update('hello'.encode('utf8')) # -- value
print(m.hexdigest())
2
3
4
5
6
# subprocess模块
执行系统命令 ,拿到结果,想咋操作就咋操作,而不是直接丢到控制台打印
import os
os.system('ls') # -- 可以执行系统命令,但执行结果直接打印到控制台上了
我们想要拿到该执行结果,咋搞?
2
3
4
subprocess.Popen()
第一个参数: 必须是字符串形式的命令
第二个参数: 必须写 shell=True
简单理解调用命令解释器 eg:cmd终端
第三个参数: stdout=subprocess.PIPE
命令正确输出
PIPE 管道简单理解成内存里的一种数据结构
命令结果只有一份, 丢到管道里暂存啦, 就不会直接在终端显示
第四个参数: stderr=subprocess.PIPE
命令错误输出
这与命令正确输出的管道是两根不同的管道哦
import subprocess
from sys import stdout
# -- py文件的运行开启了一个父进程,运行到此行代码开启一个子进程
# 两个进程是完全隔离独立的,不能在内存中共享数据
# 但我们需要将子进程的系统命令运行结果给父进程,只能造一个两者共享的内存空间
# 管道(进程与进程之间共享数据用的)
obj = subprocess.Popen(
'ls',
shell=True,
# -- subprocess.PIPE每调用就会产生一个新的管道
# 'oop里面的property->看似调用的是一属性实则是一功能的执行'
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# -- 父进程准备从正确管道读取结果,但子进程运行系统命令的结果可能还没产生呢.
# 因为进程与进程之间是相互隔离 独立的
# 若管道里没有结果,父进程就会在这等着子进程将结果丢到管道中,相当于取代了time.sleep()操作
# 注意哦! 从管道中读走这份数据后,再读数据结果为空.因为只有一份.
stdout_res = obj.stdout.read()
# -- 系统命令的运行结果是bytes类型,固定了的. 想要打印结果,需要解码 windows-gbk;mac-utf8
print(stdout_res.decode('utf-8'))
stderr_res = obj.stderr.read()
print(stderr_res.decode('utf-8'))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# sys模块
sys.path
处理导入模块时的环境变量!!!
sys.argv
用于命令行执行py文件时, 接收后面跟着的参数
# -- train.py
import sys
print(sys.argv) # -- ['train.py', '参数1', '参数2']
(application) One_Piece@DCdeMacBook-Air Desktop % python train.py 参数1 参数2
['train.py', '参数1', '参数2']
2
3
4
5
6
实现拷贝文件的功能
命令行执行命令 python cp.py 源文件路径 目标文件路径
# -- cp.py
src_file = sys.argv[1]
dst_file = sys.argv[2]
# -- rb模式 可以打开任何文件
# -- 注意:以w的方式打开文件,未关闭的情况下往文件里写内容指针不断后移
# 中途关掉,下次再以w的形式打开,内容将被清空
with open('src_file','rb') as read_f,\
open('dst_file','wb') as write_f:
for line in read_f:
write_f.write(line)
2
3
4
5
6
7
8
9
10
11
# json&pickle模块
# 异同
# json格式
json 跟 字典 是有区别的!!
json分为json对象和json数组
json对象就类似于字典.
json数组是用 []
包起来的多个json对象
json对象可由多个键值对组成, 键必须由""
包裹!!!,值可以是json对象或者是json数组.
JSON类型 | Python类型 |
---|---|
{} | dict |
[] | list |
"string" | str |
1234.56 | int或float |
true/false | True/False |
null | None |
# 序列化
可以保存程序的运行状态; 实现数据的跨平台的交互
json和pickle都可用于序列化, 序列化是将对象转换成易于传输的形式的过程.
通俗点就是将当前时刻处于内存中的数据转换一种格式.. 反序列化就是将数据转换成序列化之前的样子.
json 跨平台性强;只能支持/对应python部分的数据类型
pickle 可以支持/对应所有python的数据类型;只能被python识别,不能跨平台
# json模块的使用
# json序列化
dumps dump
import json
dic = {'name': 'egon', 'age': 18, 'sex': 'male'}
json_str = json.dumps(dic)
# -- 序列化后,自动将字典中的单引号转换成了双引号
# print(json_str) # -- {"name": "egon", "age": 18, "sex": "male"}
# print(type(json_str)) # -- <class 'str'>
with open('db.json', 'wt', encoding='utf-8') as f:
f.write(json_str)
# !!-- 序列化得到json_str、把json_str写入db.json文件这两步 等同于
with open('db.json','wt',encoding='utf-8') as f:
json.dump(dic,f)
2
3
4
5
6
7
8
9
10
11
12
13
14
# json反序列化
loads load
import json
with open('db.json', 'rt', encoding='utf-8') as f:
data = f.read()
dic_res = json.loads(data)
# print(type(data)) # -- <class 'str'>
# print(dic_res) # -- {'name': 'egon', 'age': 18, 'sex': 'male'}
# -- {'name': 'egon', 'age': 18, 'sex': 'male'} <class 'dict'>
# print(eval(data), type(eval(data)))
print(dic_res['sex']) # male
# !!-- 从硬盘db.json文件中读取到data、将data反序列化得到dic_res 等同于
with open('db.json','rt',encoding='utf-8') as f:
dic_res = json.load(f)
"""
提一句,在代码中我们试图用eval对json对象进行了反序列化,成功了.
因为json格式的数据在内存中也是str的数据类型. eval去除了最外层的引号.
但json格式的数据长这样呢?[1,true,null] 用eval来反序列化就不行!
建议还是使用json模块的方法,别搞这些花里胡哨的.(´▽`)
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# pickle模块的使用
必须是二进制的形式读写.
import pickle
dic = {'a': 1, 'b': 2, 'c': 3}
"""
★ --序列化
"""
pkl = pickle.dumps(dic)
# -- b'\x80\x04\x95\x17\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x01a\x94K\x01\x8c\x01b\x94K\x02\x8c\x01c\x94K\x03u.'
# print(pkl)
# print(type(pkl)) # -- <class 'bytes'>
with open('db.kpl', 'wb') as f:
f.write(pkl)
""" 等同于
with open('db.kpl', 'wb') as f:
pickle.dump(pkl,f)
"""
"""
★ --反序列化
"""
with open('db.kpl', 'rb') as f:
data = f.read()
res = pickle.loads(data)
print(res['a']) # 1
""" 等同于
with open('db.kpl', 'rb') as f:
res = pickle.load(f)
print(res['a'])
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31