Python课程回顾(day20)
常用模块3
一、logging模块
日志模块 import logging logging.basicConfig( filename='a.log', # 指定文件打印路径,不指定文件名会默认打印到控制台 filemode='a', # 默认是a模式,可以不写 format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s', datefmt='%Y-%m-%d %X %p', # 定制(asctime)的时间格式 level=10 # 设置日志的级别,代表当前级别及以上级别都可以打印 ) # 打印格式 # %(asctime)s %(name)s %(levelname)s %(module)s: %(message)s # 格式化时间 日志名称 日志的级别 当前使用日志的模块名 日志具体的信息 logging.debug('调试信息') # 10 logging.info('运行结果') # 20 logging.warning('一级警告') # 30 { 不同数字代表不同的级别 logging.error('二级警告') # 40 logging.critical('三级警告') # 50 # logging模块的四类对象 # logger 负责产生日志 logger1 = logging.getLogger('') # 使用getLogger产生一个产生日志的对象 # filter 过滤日志(不常用) # handler 控制日志打印到文件或是终端 # 可能很多文件都需要用到日志,所以可以在多个文件内打印 fh1 = logging.FileHandler(filename='a1.log', encoding='utf-8') # 控制文件打印路径 fh2 = logging.FileHandler(filename='a2.log', encoding='utf-8') # 控制文件打印路径 sh = logging.StreamHandler() # 控制日志打印到终端 # formatter 控制日志的格式 fmt1 = logging.Formatter( fmt='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s', datefmt='%Y-%m-%d %X %p' ) fmt2 = logging.Formatter( fmt='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %X %p' ) # 让logger1对象产生的日志可以分别打印到不同的地方,要让他们绑定关系 logger1.addHandler(fh1) logger1.addHandler(fh2) # handler对象 logger1.addHandler(sh) # 让logger1对象所绑定的handler对象再次绑定打印格式 fh1.setFormatter(fmt1) fh2.setFormatter(fmt1) sh.setFormatter(fmt2) # 日志级别 # 日志级别要必须通过两层关卡(logger1级别,文件级别)才可以正常记录 logger1.setLevel(10) # 基础级别,可以打印所有级别的信息 fh1.setLevel(20) # 只处理打印大于等于20级别的信息 fh2.setLevel(30) # 只处理打印大于等于30级别的信息 sh.setLevel(40) # 只处理打印大于等于40级别的信息 logger1.warning('调试信息') # 注意本层级别要 >= logger1的级别 了解:日志继承 import logging logger1 = logging.getLogger('grandfather') logger2 = logging.getLogger('grandfather.father') # 继承关系要在设置文件名时将子集放置父集后 logger3 = logging.getLogger('grandfather.father.son') sh = logging.StreamHandler() fmt3 = logging.Formatter( fmt='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %X %p' ) sh.setFormatter(fmt3) logger1.addHandler(sh) logger2.addHandler(sh) logger3.addHandler(sh) logger1.setLevel(10) logger2.setLevel(10) logger3.setLevel(10) sh.setLevel(10) logger3.debug('----------') # logger3会将得到的所有信息反继承给logger3的所有父集
二、hashlib模块
import hashlib # hash是一种算法,该算法接收传入的内容经过运算后会得到一个hash值
# 特点1:只要传入的值一样,得到的hash值必然一样
m = hashlib.md5() # 使用.md5拿到一个hash工厂
m.update('hello'.encode('utf-8')) # 使用update进行加工
m.update('world'.encode('utf-8'))
m.update('klf'.encode('utf-8'))
print(m.hexdigest()) # hexdigest拿到hash值1079e6f8e24cecd0c41c8841f727b31e
m.update('helloworldklf'.encode('utf-8'))
print(m.hexdigest()) # 拿到hash值1079e6f8e24cecd0c41c8841f727b31e
# 特点2:不能由hash值反解成原本内容
# 基于hash值的这种特性,我们可以在接收用户输入的时候将用户输入的密码加工成hash值,
# 再由网络通讯发到我们的服务端进行数据保存,以达到用户密码的安全性。
# 用户再次校验时只需要跟原本的hash值做对比即可
# 但考虑到用户的个人习惯与撞库(一些常用密码格式的hash值),用户的密码还是不够安全
# 这时候我们就需要帮助用户来提高密码安全性:即密码加盐
user_pwd = 'klf123'
m = hashlib.md5()
m.update('abcdefg'.encode('utf-8')) # 在用户密码之前加入其他字符的hash值
m.update(user_pwd.encode('utf-8')) # 中间是用户密码
m.update('hijklmn'.encode('utf-8')) # 在用户密码之后加入其他字符的hash值
print(m.hexdigest()) # 生成新的hash值
# 注意:加盐的方式是随机的,想要破解就必须知道使用的hash工厂,加盐方式以及加盐位置
# 服务端反解的时候按照当初加盐的规则来反解即可
# 特点3:只要使用同一种hash算法,得到的hash值的长度的固定的
# 这种特点大大降低了用户所传入数据的大小所带来的空间不足
# 无论用户传入的数据有多大,得到的hash值的长度是固定的
三、re模块(重点******)
import re # re模块之正则表达式re.findall的应用1:单次匹配 # print(re.findall('\w', 'abc 123 \n!@#$%\r\t\f')) # '\w'匹配字符串内所有的数字、字母、下划线 # print(re.findall('\W', 'abc 123 \n!@#$%\r\t\f')) # '\W'匹配的是字符串所有的非数字字母下划线,若字符串内有\则会使用一个斜杠转译后面的斜杠 # print(re.findall('\s', 'abc 123 \n!@#$%\r\t\f')) # '\s'匹配的是字符串内所有的空白字符,('空字符'\n\r\t\f) # print(re.findall('\S', 'abc 123 \n!@#$%\r\t\f')) # '\S'匹配的是字符串内所有的非空白字符 # print(re.findall('\d', 'abc 123 \n!@#$%\r\t\f')) # '\d'匹配任意数字(0~9) # print(re.findall('\D', 'abc 123 \n!@#$%\r\t\f')) # '\D'匹配任意非数字 # print(re.findall('\alex', 'egon,alex_sb123yxx_sb,lxx_sb')) # 单纯输入的字符串则会将字符串作为整体全部匹配,若匹配不成功则返回空,有多少个匹配多少个 # print(re.findall('\Aalex', 'egon,alex_sb123yxx_sb,lxx_sb')) # '\A'表示只从头开始找,若匹配不成功则返回空,若匹配成功只匹配一次,不会继续向后匹配 # print(re.findall('^alex', 'egon,alex_sb123yxx_sb,lxx_sb')) # 作用等同于大A # print(re.findall('alex\Z', 'egon,alex_sb123yxx_sb,lxx_sb')) # '\Z'表示只从末尾开始找,若匹配不成功则返回空,若匹配成功只匹配一次,不会继续向后匹配 # print(re.findall('alex$', 'egon,alex_sb123yxx_sb,lxx_sb')) # '$'作用等同于大Z # 组合应用之*,$ # print(re.findall('^abc$', 'abc1')) # 表示字符串必须为^与$之间的字符串,否则匹配不成功 # ============================================================================== # re模块之正则表达式的应用2:重复匹配 # print(re.findall('a.c', 'a123c a2c anc aaaaaaac')) # .表示匹配除换行符之外的任意'一个'字符,可加多个.,每个点都代表一个字符 # 结果:在a与c中间只有一个字符的情况下,才会匹配成功 # print(re.findall('a.c', 'a123c a2c anc aaaaaaac a\nc', re.DOTALL)) # re.DOTALL表示可以打破'.'不能匹配换行符的定义,当前是可以匹配任意字符的 # print(re.findall('ab?', 'a ab abb abbb abbbb abbbbb')) # ?表示?左边的1个字符重复0次或1次(可以是0次,最多是1次) # 结果:只要a匹配成功,b可以匹配0次(若为0次则只打印a),最多只匹配1次 # print(re.findall('ab*', 'a ab abb abbb abbbb abbbbb')) # *表示*左边的1个字符重复0次或N次(可以是0次,没有最大匹配次数,有多少要多少) # 结果:只要a匹配成功,b可以匹配0次(若为0次则只打印a),也可以匹配无限次 # print(re.findall('ab+', 'a ab abb abbb abbbb abbbbb')) # *表示*左边的1个字符重复1次或N次(至少是1次,没有最大匹配次数,有多少要多少) # 结果:只要a匹配成功,b可以匹配1次(若为0次则不打印),也可以匹配无限次 # print(re.findall('ab{0,1}', 'a ab abb abbb abbbb abbbbb')) # {}内可以传入范围,指定匹配的次数,若后面的参数不写则代表N次(等同于*号的作用) # 结果:等同于?号的作用 # 组合应用之.*(贪婪匹配) # print(re.findall('a.*c', 'ac a123c a%$^$c aasdsdasdad')) # .*表示可以匹配任意长度任意字符(除\n),但*会尽可能的去匹配多个字符 # 结果:.*的组合会将距离开头最近的a当做起始,将距离末尾最近的c当做结束,形成1个匹配然后结束,不建议使用 # 组合应用之.*?(非贪婪匹配) # print(re.findall('a.*?c', 'ac a123c a%$^$c aasdsdasdad')) # .*?中的问号的作用就是将贪婪匹配转换成非贪婪匹配 # 结果:会将距离开头最近的a当做起始,将距离a后面最近的c当做结束,形成一个匹配然后结束 # ============================================================================== # re模块之正则表达式的应用3:分组() # print(re.findall('(alex)_sb', 'alex_sb sdasfadsgasgalex_sb')) # ()的作用是不影响正则表达式基本的匹配方法的前提下,只打印括号内的内容 # 结果:若匹配成功后只打印alex # 组合应用之.*?()(爬取网址) # print(re.findall('href="(.*?)"', '<link href="/bundles/admin-new.css?v=Ye9IYl3rG1TPa1mMw-tr9jlbN_BMEt9-1G3QChTzFC01" rel="stylesheet"/>')) # 结果: ['/bundles/admin-new.css?v=Ye9IYl3rG1TPa1mMw-tr9jlbN_BMEt9-1G3QChTzFC01'] # ============================================================================== # re模块之正则表达式的应用4:[]指定范围 # print(re.findall('a[0-9][0-9]c', 'a1c adc a4c aAc a11c aac')) # []内可以使用-号来定义一个范围,英文是a-zA-Z,数字是0-9,在这个自定义范围内的才算匹配成功 # 结果:会将a与c之间是0-9的字符串匹配出来,若想匹配两位数则需要再加中括号进行再次匹配(会以两个字符去匹配),a1c a4c便不再成立 # 注意:若中括号内要匹配特殊符号例如+-*^之类的,'-'不可以放在两个符号之间,要放左右两边。 # print(re.findall('^a[0-9]c', 'a1c adc a4c aAc a11c aac')) # []内前面加^号就代表取反的意思(原始默认为开头)即不再这个范围内的就匹配成功 # 结果:a与c之间不是0-9之间的就可以匹配 # 组合应用: # print(re.findall('[a-z]+_sb', 'alex_sb egon_sb 123wxxxxxxxxx_sb ryc_sb')) # [匹配条件]'+'匹配条件 '+'代表中括号内的条件可以匹配1次或N次,直到遇见不是括号范围内的字符然后与'+'后面的字符做匹配 # 结果:会优先匹配中括号内的(一直匹配),直到字符不属于中括号范围内的,开始匹配_sb # print(re.findall('([a-z]+)_sb', 'alex_sb egon_sb 123wxxxxxxxxx_sb ryc_sb')) # 加入小括号来只取名称 # ============================================================================== # re模块之正则表达式的应用5:| 或者 # print(re.findall('compan(ies|y)', 'too many companies have bank rupt , and the next one company')) # 在|左右两边的内容只要成立一个即匹配成功 # 结果:在compan后若为ies或为y的情况下,则匹配成功,因为有分组,所以只打印括号内的内容,(ies,y) # print(re.findall('compan(?:ies|y)', 'too many companies have bank rupt , and the next one company')) # 在分组内加入?:则代表打印分组外与分组内的所有内容,不单单只要括号内的内容(companies,company) # ============================================================================== # re模块之正则表达式search与match的应用: # print(re.search('alex', '123 alex sb egon nb alex sb')) # print(re.search('^alex', '123 alex sb egon nb alex sb')) # search代表从起始到结束依次匹配,若匹配成功返回第一个匹配成功的对象,可以使用group打印结果 # 若匹配不成功则返回None,再使用group打印结果则会报错 # 表达式之前加^代表只匹配起始位置,若不成功则返回None,再使用group打印结果则会报错 # 一般使用search返回的对象(不加group)判断是否存在你需要找的字符 # print(re.match('alex', '123 alex sb egon nb alex sb')) # match代表默认只从起始位置匹配,若匹配成功返回一个对象,使用group打印结果 # 若匹配不成功则返回None,再使用group打印结果则会报错 # ============================================================================== # re模块之正则表达式split的应用 # info = 'a,b c:\*d/p' # print(re.split('[ ,:\\\/*]', info)) # split的原理基本等同于字符串的split,不过re中的split可以使用多个符号来切分 # 将表达式内的各种符号对字符依次切分,只要字符内包含其中的某个切分符就进行切分 # 若两个字符之间存在两个或多个切分符则会切出相应的空字符 # ============================================================================== # re模块之正则表达式sup的应用 # 要求:将第二个egon替换成大写 # print(re.sub('(.*?)(egon)(.*?)(egon)(.*?)', r'\1\2\3EGON\5', '123 egon is sb egon 123')) # sub的作用基本相同于replace,但sub功能可以在指定位置替换指定的字符 # 结果:事先将字符分组,然后使用\1\2\3\4\5可以将分组替换位置,也可以在分组内直接替换字符 # 要求:将is左右两边的单词调换位置lqz123+ is sb09 # print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)', r'\5\2\3\4\1\6', r'lqz123+ is sb09')) # 首先明白单词的定义:由连续的字母组成。 # 基于这个理念来将字符进行分组,然后将分组调换位置即可 # ============================================================================== # re模块之正则表达式compile的应用 # res = re.compile('alex') # 使用compile将经常用到的表达式定义成一个变量 # print(res.findall('alex_sb alex123 ale')) # 通过变量就可以直接去匹配相应的字符