hashlib模块,logging日志模块,xml模块,第三方模块,包的使用

hashlib模块

加密:将明文数据通过一系列算法变成密文数据(目的就是为了数据的安全)

加密算法:md系列、sha系列、base系列、hmac系列

hash加密算法:传入一段内容会得到一串hash值

hash值有三大特点:

  • 1.如果传入的内容与采用的算法一样,那么得到的hash值一定一样

  • 2.只要采用的算法是固定的,hash值的长度就是固定的,不会随着内容的增多而变长

  • 3.hash值不可逆,即不能通过hash值反解出内容是什么

    • 1 + 2 = 》效验文件的完整性

    • 1 + 3 = 》加密


基本使用

# 传入的内容与采用的算法一样,得到的hash值一定一样
import hashlib

# 1.先确定算法类型(md5普遍使用)
md5 = hashlib.md5()

# 2.将明文数据传递给md5算法(update只能接受bytes类型数据)
md5.update("你好".encode('utf-8'))
md5.update("hello".encode('utf-8'))
md5.update("哈哈".encode('utf-8'))

# 3."你好hello哈哈" hash工厂计算的是它的值
res = md5.hexdigest()

# 4.获取加密之后的密文数据(没有规则的一串随机字符串)
print(res)  # 43b2fa0da902a2d9175fb4d4b858e5d5

"""
加密之后的密文数据是没有办法反解密成明文数据的
市面上所谓的破解,其实就是提前算出一系列明文对应的密文
之后比对密文再获取明文
"""  

详细操作

# 1.明文数据只要是相同的 那么无论如何传递加密结果肯定是一样的
import hashlib
md5 = hashlib.md5()

# md5.update('hello'.encode('utf8'))
md5.update(b'hello')
md5.update(b'123')
md5.update(b'jason')
res = md5.hexdigest()
print(res)  # b009c733d4c2e52a57230c606e97f587


md5.update('hello123jason'.encode('utf8'))
res = md5.hexdigest()
print(res)  # b009c733d4c2e52a57230c606e97f587

md5.update(b'hello123jason')
res = md5.hexdigest()
print(res)  # b009c733d4c2e52a57230c606e97f587



# 2.密文数据越长表示内部对应的算法越复杂 越难被正向破解
import hashlib
md5 = hashlib.sha256()

md5.update(b'hellojason123')
res = md5.hexdigest()
print(res)  # 6ec0f865ec4e9bcf9abc7f6c0e7268acfb2ba36f3e460fd592a214f7eacc92b0

md5.update(b'hello')
md5.update(b'jason')
md5.update(b'123')  

res = md5.hexdigest()
print(res)  # 6ec0f865ec4e9bcf9abc7f6c0e7268acfb2ba36f3e460fd592a214f7eacc92b0
"""
密文越长表示算法越复杂 对应的破解算法的难度越高
但是越复杂的算法所需要消耗的资源也就越多 密文越长基于网络发送需要占据的数据也就越大
    具体使用什么算法取决于项目的要求 一般情况下md5足够了
"""

# 3.涉及到用户密码存储其实都是密文,只要用户自己知道明文是什么
"""
    1.内部程序员无法得知明文数据
    2.数据泄露也无法得知明文数据
"""

加盐处理

"""
在对明文数据做加密处理过程前添加一些干扰项
"""
import hashlib
# 1.先确定算法类型(md5普遍使用)
md5 = hashlib.md5()
# 2.将明文数据传递给md5算法(update只能接受bytes类型数据)
# 加盐(干扰项)
md5.update('公司内部自己定义的盐'.encode('utf8'))
# 真实数据
md5.update(b'hellojason123')
# 3.获取加密之后的密文数据(没有规则的一串随机字符串)
res = md5.hexdigest()
print(res)  # 4326fe1839d7c4186900eb2b356d4f8a


# 动态加盐
"""
在对明文数据做加密处理过程前添加一些变化的干扰项
"""
import hashlib
import time

md5 = hashlib.md5()
# 动态加盐(干扰项)  当前时间 用户名的部分 uuid(随机字符串(永远不会重复))
res1 = str(time.time())
md5.update(res1.encode('utf8'))
# 真实数据
md5.update(b'hellojason123')
# 获取加密之后的密文数据(没有规则的一串随机字符串)
res = md5.hexdigest()
print(res)  # 3ecbf37191890f89aa88a4b9b1c5f9c1

校验文件一致性

# 效验文件完整性

"""
文件不是很大的情况下 可以将所有文件内部全部加密处理
但是如果文件特别大 全部加密处理相当的耗时好资源 如何解决???
    针对大文件可以使用切片读取的方式 
"""
import hashlib

md5 = hashlib.md5()
with open(r'a.txt', 'rb') as f:
    for line in f:
        md5.update(line)
real_data = md5.hexdigest()
print(real_data)  # 29d8ea41c610ee5d1e76dd0a42c7e60a


# 在a.txt文件添加一行数据,结果就不一样
md5 = hashlib.md5()
with open(r'a.txt', 'rb') as f:
    for line in f:
        md5.update(line)
error_data = md5.hexdigest()
print(error_data)  # 738a56b49f24884ba758d1e4ab6ceb74


# 切片读取的方式 
import os
# 读取文件总大小以字节表示
res = os.path.getsize(r'a.txt')
print(res)  # 116字节
# 指定分片读取策略(读几段 每段几个字节)  10   f.seek()
read_method = [0,res//4,res//2,res]

"""
比特流技术
断点续传技术
"""



rizhiz_gaitubao_216x227

logging日志模块

日志级别

# 日志有五个等级(从上往下重要程度不一样)
logging.debug('debug级别')  # DEBUG = 10 调试 
logging.info('info级别')  # INFO = 20  消息
logging.warning('warning级别')  # WARNING = 30 警告 WARN = WARNING 
logging.error('error级别')  # ERROR = 40 错误
logging.critical('critical级别')  # CRITICAL = 50 严重 FATAL = CRITICAL
  • 简单使用
import logging
file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',)
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S %p',
    handlers=[file_handler,],
    level=logging.ERROR
)
logging.error('日志模块很好学 不要自己吓自己')
"""
1.如何控制日志输入的位置
    想在文件和终端中同时打印
2.不同位置如何做到不同的日志格式
    文件详细一些 终端简单一些
"""

详细介绍

  • logging模块的Logger,Filter,Handler,Formatter对象

    • logger对象:负责产生日志

    • Filter对象:负责过滤日志

    • Handler对象:负责日志产生的位置。接收日志然后控制打印到不同的地方,FileHandler用来打印到文件中,StreamHandler用来打印到终端

    • Formatter对象:负责日志的格式。可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用,以此来控制不同的Handler的日志格式

import logging
# 1.logger对象:负责产生日志
logger = logging.getLogger('转账记录')

# 2.filter对象:负责过滤日志(直接忽略)

# 3.handler对象:负责日志产生的位置
hd1 = logging.FileHandler('a1.log',encoding='utf8')  # 产生到文件的
hd2 = logging.FileHandler('a2.log',encoding='utf8')  # 产生到文件的
hd3 = logging.StreamHandler()  # 产生在终端的

# 4.formatter对象:负责日志的格式
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.绑定handler对象
logger.addHandler(hd1)
logger.addHandler(hd2)
logger.addHandler(hd3)

# 6.绑定formatter对象
hd1.setFormatter(fm1)
hd2.setFormatter(fm2)
hd3.setFormatter(fm1)

# 7.设置日志等级
logger.setLevel(30)

# 8.记录日志
logger.debug('写了半天 好累啊 好热啊')

rizhi12_gaitubao_243x239


日志配置字典

# settings.py

# 核心就在于CV
# 定义日志输出格式
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_dir = os.path.join(BASE_DIR, 'log')  # log文件的目录

logfile_name = 'log.log'  # log文件名

# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)

# 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,
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置  空字符串作为键 能够兼容所有的日志
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },  # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
    },
}

  • 导入logging加载settings字典,配置生成对应对象以及绑定关系

# common.py

import settings  # 导入settings拿到配置字典
import logging.config  # 导入logging模块标准库

def get_logger(name):
    logging.config.dictConfig(settings.LOGGING_DIC)
    return logging.getLogger(name)


logger1 = get_logger("用户交易")
logger1.debug("调试日志")
logger1.info("jason给lxx转了1个亿") # 不会被打印
logger1.warning("警告")
logger1.error("错误")
logger1.critical("危机")

logger2 = get_logger("安全相关")
logger2.debug("调试日志")
logger2.info("常规")  # 不会被打印
logger2.warning("警告")
logger2.error("jason登录失败")
logger2.critical("危机")
  • 日志格式

[DEBUG][2021-11-29 20:04:50,161][logging模块.py:10]调试日志
[INFO][2021-11-29 20:04:50,162][logging模块.py:11]jason给lxx转了1个亿
[WARNING][2021-11-29 20:04:50,162][logging模块.py:12]警告
[ERROR][2021-11-29 20:04:50,162][logging模块.py:13]错误
[CRITICAL][2021-11-29 20:04:50,162][logging模块.py:14]危机
[DEBUG][2021-11-29 20:04:50,162][logging模块.py:17]调试日志
[INFO][2021-11-29 20:04:50,162][logging模块.py:18]常规
[WARNING][2021-11-29 20:04:50,162][logging模块.py:19]警告
[ERROR][2021-11-29 20:04:50,163][logging模块.py:20]jason登录失败
[CRITICAL][2021-11-29 20:04:50,163][logging模块.py:21]危机

xml模块

XML:全称 可扩展标记语言,为了能够在不同的平台间继续数据的交换,使交换的数据能让对方看懂 就需要按照一定的语法规范来书写,xml跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
xml的格式如下,就是通过<>节点来区别数据结构的:
<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

xml数据
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
# print(root.iter('year'))  # 全文搜索
# print(root.find('country'))  # 在root的子节点找,只找一个
# print(root.findall('country'))  # 在root的子节点找,找所有

import xml.etree.ElementTree as ET
 
tree = ET.parse("xmltest.xml")
root = tree.getroot()
print(root.tag)
 
# 遍历xml文档
for child in root:
    print('========>',child.tag,child.attrib,child.attrib['name'])
    for i in child:
        print(i.tag,i.attrib,i.text)
 
# 只遍历year 节点
for node in root.iter('year'):
    print(node.tag,node.text)
#---------------------------------------

import xml.etree.ElementTree as ET
 
tree = ET.parse("xmltest.xml")
root = tree.getroot()
 
# 修改
for node in root.iter('year'):
    new_year=int(node.text)+1
    node.text=str(new_year)
    node.set('updated','yes')
    node.set('version','1.0')
tree.write('test.xml')
 
 
# 删除node
for country in root.findall('country'):
   rank = int(country.find('rank').text)
   if rank > 50:
     root.remove(country)
 
tree.write('output.xml')
# 在country内添加(append)节点year2
import xml.etree.ElementTree as ET
tree = ET.parse("a.xml")
root=tree.getroot()
for country in root.findall('country'):
    for year in country.findall('year'):
        if int(year.text) > 2000:
            year2=ET.Element('year2')
            year2.text='新年'
            year2.attrib={'update':'yes'}
            country.append(year2)  # 往country节点下添加子节点

tree.write('a.xml.swap')
自己创建xml文档:
import xml.etree.ElementTree as ET
 
 
new_xml = ET.Element("namelist")
name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"})
age = ET.SubElement(name,"age",attrib={"checked":"no"})
sex = ET.SubElement(name,"sex")
sex.text = '33'
name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
age = ET.SubElement(name2,"age")
age.text = '19'
 
et = ET.ElementTree(new_xml)  # 生成文档对象
et.write("test.xml", encoding="utf-8",xml_declaration=True)
 
ET.dump(new_xml)  # 打印生成的格式

第三方模块

  • Python吸引人的一个出众的优点就是它有众多的第三方库,可以更高效率的实现开发。但并不是python自带的,需要基于网络下载使用。

'''pip所在的路径添加环境变量'''
下载第三方模块的方式
	方式1:命令行借助于pip工具
        pip3 install 模块名  # 不知道版本默认是最新版
        pip3 install 模块名==版本号  # 指定版本下载
    	pip3 install 模块名 -i 仓库地址  # 临时切换
        '''命令行形式永久修改需要修改python解释器源文件'''
    方式2:pycharm快捷方式
        settings 
        	project 
            	project interprter
                	双击或者加号
        点击右下方manage管理添加源地址即可
# 下载完第三方模块之后 还是使用import或from import句式导入使用
"""
pip命令默认下载的渠道是国外的python官网(有时候会非常的慢)
我们可以切换下载的源(仓库)
	(1)阿里云 http://mirrors.aliyun.com/pypi/simple/
        (2)豆瓣 http://pypi.douban.com/simple/
        (3)清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
        (4)中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/
        (5)华中科技大学http://pypi.hustunique.com/
  
pip3 install openpyxl -i http://mirrors.aliyun.com/pypi/simple/
"""


"""
下载第三方模块可能报错的情况及解决措施
	1.报错的提示信息中含有关键字timeout
		原因:网络不稳定
		措施:再次尝试 或者切换更加稳定的网络

	2.找不到pip命令
		环境变量问题

	3.没有任何的关键字 不同的模块报不同的错
		原因:模块需要特定的计算机环境
		措施:拷贝报错信息 打开浏览器 百度搜索即可
			pip下载某个模块报错错误信息
"""



包的基本使用

什么是包?

  • 官网解释

    • 包是一种通过使用‘.模块名’来组织python模块名称空间的方式。

    • 具体的:包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来

需要强调的是:

  • 1.在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
  • 2.创建包的目的不是为了运行,而是被导

为何要用包

  • 包的本质就是一个文件夹,那么文件夹唯一的功能就是将文件组织起来,随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性

注意事项

  • 1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
  • 2、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
  • 3、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间

首次导入包发生的事情

  • 1、运行包下的__init__.py文件,创建一个包的名称空间,将__init__.py运行过程中产生的名字都丢到包的的名称空间中

  • 2、在当前名称空间中得到一个名字aaa,该名字是指向包名称空间

aaa下的py文件:

# m1.py    #  被导入包aaa下的py文件
def f1():
	print('m1,f1')

# __init__.py
from aaa import m1



# run.py   # 执行文件
import aaa

print(aaa.m1.f1)
aaa.m1.f1()


包总结

  • 1、导包就是在导包下__init__.py文件

  • 2、包内部的导入应该使用相对导入,相对导入也只能在包内部使用,而且...取上一级不能出包

  • 3、使用语句中的点代表的是访问属性 m.n.x -------> 向m要n,向n要x,而导入语句中的点代表的是路径分隔符 import a.b.c -----> a/b/c,文件夹下a下有子文件夹b,文件夹b下有子文件或文件夹c,所以导入语句中点的左边必须是一个包

posted @ 2021-01-07 18:22  山风有耳  阅读(137)  评论(0编辑  收藏  举报