哈希加密与日志模块

一、hashlib模块

1、简介

什么是哈希模块:

​ hashlib模块是一种加密模块,内部存有多种加密类型

加密的作用:

​ 可将明文数据进行加密,转换成一串密文,密文越长说明文件加密的越复杂

加密算法的种类:
  • md5
  • base64
  • hmac
  • sha系列(sha1、sha224、sha256等等)
补充说明:

​ 1.算法不变,如果内容相同,那么加密的结果肯定相同

​ 2.待加密的明文,可以一次传入,也可分多次传入,不会影响密文的结果

​ 3.加密的结果是无法反解密的

​ 4.加盐处理:

​ 4.1.加盐处理的意思是指:在明文内加入干扰项,可改变密文的结果,来提高被加密的安全性

​ 4.2.动态加盐:指干扰项是动态的,在加盐的基础上更大程度上提高被加密文件的安全性

应用方向:
  • 用户密码加密
    • 可应用于用户登录时,对密码的加密
  • 文件安全性校验
    • 可对文件进行加密,通过比对文件的密文来判断文件是否被修改

2、基本操作与用法

导入模块:
import hashlib
具体用法:
1、选择加密算法:
	md5 = hashlib.md5()

2、传入明文:
	md5.update(b'hello')  
   # 传入的明文需提前转换成二进制的方式

3、获取加密密文:
	res = md5.hexdigets()
	print(res)  
	# 5d41402abc4b2a76b9719d911017c592

3、补充

1.加密算法不变 内容如果相同 那么结果肯定相同
	 # md5.update(b'hello~world~python~666')  # 一次性传可以
    md5.update(b'hello')  # 分多次传也可以
    md5.update(b'~world')  # 分多次传也可以
    md5.update(b'~python~666')  # 分多次传也可以
2.加密之后的结果是无法反解密的
	 只能从明文到密文正向推导 无法从密文到明文反向推导
    常见的解密过程其实是提前猜测了很多种结果
    	123		 密文
        321	     密文
    	222      密文
3.加盐处理
	 在明文里面添加一些额外的干扰项
    # 1.选择加密算法
    md5 = hashlib.md5()
    # 2.传入明文数据
    md5.update('公司设置的干扰项'.encode('utf8'))
    md5.update(b'hello python')  # 一次性传可以
    # 3.获取加密密文
    res = md5.hexdigest()
    print(res)  # e53024684c9be1dd3f6114ecc8bbdddc
4.动态加盐
	 干扰项是随机变化的 
    	eg:当前时间、用户名部分...
5.加密实战操作
	1.用户密码加密
	2.文件安全性校验
 	3.文件内容一致性校验
  	4.大文件内容加密
    	截取部分内容加密即可
        比如一种情况就是,在一个大文件的每隔四分之一处取一千个字节进行加密,如果两个文件的密文一样说明,这里就是一样的两个文件

二、subprocess模块

模拟操作系统终端 执行命令并获取结果

import subprocess

res = subprocess.Popen(
    'ipconfig',  # 操作系统要执行的命令
    shell=True,  # 固定配置
    stdin=subprocess.PIPE,  # 输入命令
    stdout=subprocess.PIPE,  # 输出结果
)
print('正确结果', res.stdout.read().decode('gbk'))  # 获取操作系统执行命令之后的正确结果
print('错误结果', res.stderr)  # 获取操作系统执行命令之后的错误结果  # 没有就返回None

三、subprocess模块

subprocess是python内置的模块,这个模块中的Popen可以查看用户输入的命令行是否存在

如果存在,把内容写入到stdout管道中

如果不存在,把信息写入到stderr管道

需要注意的是,这个模块的返回结果只能让开发者看一次,如果想多次查看,需要在第一次输出的时候,把所有信息写入到变量中。

1、subprocess中的popen用法:

  • Popen基本格式:subprocess.Popen(‘命令’, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

  • shell=True 表示要在终端中运行的命令

    stdout=sbuprocess.PIPE 表示当命令存在的时候,把结果写入到stdout管道

    stderr=sbuprocess.PIPE 表示当命令不存在的时候,把结果吸入到stderr管道

import subprocess

res = subprocess.Popen(
    'asdas',  # 操作系统要执行的命令
    shell=True,  # 固定配置
    stdin=subprocess.PIPE,  # 输入命令
    stdout=subprocess.PIPE,  # 输出结果
)
print('正确结果', res.stdout.read().decode('gbk'))  # 获取操作系统执行命令之后的正确结果
print('错误结果', res.stderr)  # 获取操作系统执行命令之后的错误结果

2. subprocess模块中的常用函数

函数 描述
subprocess.run() Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
subprocess.call() 执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。
subprocess.check_call() Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(…, check=True)。
subprocess.check_output() Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。
subprocess.getoutput(cmd) 接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。
subprocess.getstatusoutput(cmd) 执行cmd命令,返回一个元组(命令执行状态, 命令执行结果输出),其功能类似于commands.getstatusoutput()。

说明:

1.在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用subproccess模块的功能;
2.在Python 3.5之前的版本中,我们可以通过subprocess.call(),subprocess.getoutput()等上面列出的其他函数来使用subprocess模块的功能;
3.subprocess.run()、subprocess.call()、subprocess.check_call()和subprocess.check_output()都是通过对subprocess.Popen的封装来实现的高级函数,因此如果我们需要更复杂功能时,可以通过subprocess.Popen来完成。
4.subprocess.getoutput()和subprocess.getstatusoutput()函数是来自Python 2.x的commands模块的两个遗留函数。它们隐式的调用系统shell,并且不保证其他函数所具有的安全性和异常处理的一致性。另外,它们从Python 3.3.4开始才支持Windows平台。

3、上面各函数的定义及参数说明

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, universal_newlines=False)

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)

subprocess.getstatusoutput(cmd)

subprocess.getoutput(cmd)

参数说明:

args: 要执行的shell命令,默认应该是一个字符串序列,如[‘df’, ‘-Th’]或(‘df’, ‘-Th’),也可以是一个字符串,如’df -Th’,但是此时需要把shell参数的值置为True。

shell: 如果shell为True,那么指定的命令将通过shell执行。如果我们需要访问某些shell的特性,如管道、文件名通配符、环境变量扩展功能,这将是非常有用的。当然,python本身也提供了许多类似shell的特性的实现,如glob、fnmatch、os.walk()、os.path.expandvars()、os.expanduser()和shutil等。

check: 如果check参数的值是True,且执行命令的进程以非0状态码退出,则会抛出一个CalledProcessError的异常,且该异常对象会包含 参数、退出状态码、以及stdout和stderr(如果它们有被捕获的话)。

stdout, stderr:input: 该参数是传递给Popen.communicate(),通常该参数的值必须是一个字节序列,如果universal_newlines=True,则其值应该是一个字符串。

run()函数默认不会捕获命令执行结果的正常输出和错误输出,如果我们向获取这些内容需要传递subprocess.PIPE,然后可以通过返回的CompletedProcess类实例的stdout和stderr属性或捕获相应的内容;

call()和check_call()函数返回的是命令执行的状态码,而不是CompletedProcess类实例,所以对于它们而言,stdout和stderr不适合赋值为subprocess.PIPE;

check_output()函数默认就会返回命令执行结果,所以不用设置stdout的值,如果我们希望在结果中捕获错误信息,可以执行stderr=subprocess.STDOUT。

universal_newlines: 该参数影响的是输入与输出的数据格式,比如它的值默认为False,此时stdout和stderr的输出是字节序列;当该参数的值设置为True时,stdout和stderr的输出是字符串。

四、logging日志模块

1、如何理解日志

所谓日志就是一个用来记录我们行为举止的代码,类似以前的史官,我们在平时的使用中并不要求自己可以写出来,会用别人的代码并且会改就可以了。因为以后会使用整合更好的模块来使用。

2、日志的五种级别

logging模块日志级别有DEBUG < INFO < WARNING < ERROR < CRITICAL 五种。

DEBUG - 调试模式,应用场景是问题诊断;
INFO - 通常只记录程序中一般事件的信息,用于确认工作一切正常;
WARNING - 打印警告信息,系统还在正常运行;
ERROR - 错误导致某些功能不能正常运行时记录的信息;
CRITICAL - 当发生严重错误,导致应用程序不能继续运行时记录的信息。

3、组成部分

  • logger:提供记录日志的方法。
  • handler:选择日志的输出地方(一个logger添加多个handler)。
  • filter:给用户提供更加细粒度的控制日志的输出内容。
  • format:用户格式化输出日志的信息。

4、format常用格式说明

    %(levelno)s: 打印日志级别的数值
    %(levelname)s: 打印日志级别名称
    %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
    %(filename)s: 打印当前执行程序名
    %(funcName)s: 打印日志的当前函数
    %(lineno)d: 打印日志的当前行号
    %(asctime)s: 打印日志的时间
    %(thread)d: 打印线程ID
    %(threadName)s: 打印线程名称
    %(process)d: 打印进程ID
    %(message)s: 打印日志信息

五、日志的执行过程

1、产生日志

2、过滤日志

这里我们需要注意,通常来说我们会在产生日志的时候设置需要产生的日志。其他不需要的日志就不会生成,所以基本用不到这个模块

3、输出日志

4、日志格式

六、字段解释

# 字段解释
filename:日志文件名的prefix;

when:是一个字符串,用于描述滚动周期的基本单位,字符串的值及意义如下: 
“S”: Seconds 
“M”: Minutes 
“H”: Hours 
“D”: Days 
“W”: Week day (0=Monday) 
“midnight”: Roll over at midnight

interval: 滚动周期,单位有when指定,比如:when=’D’,interval=1,表示每天产生一个日志文件
backupCount: 表示日志文件的保留个数

举例:

import logging

# 1.日志的产生(准备原材料)        logger对象
logger = logging.getLogger('购物车记录')
# 2.日志的过滤(剔除不良品)        filter对象>>>:可以忽略 不用使用
# 3.日志的产出(成品)             handler对象
hd1 = logging.FileHandler('a1.log', encoding='utf-8')  # 输出到文件中
hd2 = logging.FileHandler('a2.log', encoding='utf-8')  # 输出到文件中
hd3 = logging.StreamHandler()  # 输出到终端
# 4.日志的格式(包装)             format对象
fm1 = logging.Formatter(
        fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S %p',
)
fm2 = logging.Formatter(
        fmt='%(asctime)s - %(name)s:  %(message)s',
        datefmt='%Y-%m-%d',
)
# 5.给logger对象绑定handler对象
logger.addHandler(hd1)
logger.addHandler(hd2)
logger.addHandler(hd3)
# 6.给handler绑定formmate对象
hd1.setFormatter(fm1)
hd2.setFormatter(fm2)
hd3.setFormatter(fm1)
# 7.设置日志等级
logger.setLevel(10)  # debug
# 8.记录日志
logger.debug('写了半天 好累啊 好热啊')

六、日志配置字典

通常来说我们都是使用日志字典的形式来达成功能的(功能多又方便,谁不喜欢呢)。这里也是跟之前的说的一样,会用就好,不需要了解。

import logging
import logging.config
# 定义日志输出格式 开始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'  # 其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
# 自定义文件路径
logfile_path = 'a3.log'
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'
        },
        # 打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日志文件
            'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
            'backupCount': 5,
                # 这里两个参数的意思是一个日志文件最多写5M,最多可以存在五个不同的日志文件,但是当数量达到五个之后就会出现最早的那个会被删除,
                # 然后再产生一个新的文件(类似于覆盖了最早的那个文件)
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        # logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },  # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
        # '购物车记录': {
        #     'handlers': ['default','console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
        #     'level': 'WARNING',
        #     'propagate': True,  # 向上(更高level的logger)传递
        # },  # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
    },
}
logging.config.dictConfig(LOGGING_DIC)  # 自动加载字典中的配置
# logger1 = logging.getLogger('购物车记录')
# logger1.warning('尊敬的VIP客户 晚上好 您又来啦')
# logger1 = logging.getLogger('注册记录')
# logger1.debug('jason注册成功')
logger1 = logging.getLogger('红浪漫顾客消费记录')
# 当这里的getLogger内部的参数如果字典中没有,就会自动使用字典中名称为空的那个模版来执行
logger1.debug('慢男 猛男 骚男')
posted @ 2022-10-27 22:57  逐风若梦  阅读(107)  评论(0编辑  收藏  举报