• 常用模块(二)


    一、hashlib模块

    python中的hashlib模块提供了多种算法,常见的有md5,sha1等

    什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

    摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

    摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

    我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

    import hashlib
    
    md5_obj = hashlib.md5()
    md5_obj.update('luffy0505'.encode('utf-8'))
    ret = md5_obj.hexdigest()
    print(ret, type(ret), len(ret))  # 字符串,32位
    
    >>>
    6073bad5650f16704ff978c1db5b921f <class 'str'> 32 

    摘要算法sha1:

    import hashlib
    sha_obj = hashlib.sha1()
    sha_obj.update('luffy0505'.encode('utf-8'))
    ret = sha_obj.hexdigest()
    print(ret, type(ret), len(ret)) # 字符串  40位
    
    >>>
    4fb4ec3f87e3ab0f97a23cf0840dd029da5d88a0 <class 'str'> 40
    

      

       hashlib算法

    • 不可逆
    • 不同的字符串通过这个算法的计算得到的密文总是不同的
    • 相同的算法,相同的字符串,获得的结果总是相同的(即便是不同的语言,不同的环境(操作系统,版本,时间))

    如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:

    import hashlib
    
    md5_obj = hashlib.md5()
    md5_obj.update('hellof world'.encode('utf-8'))
    ret = md5_obj.hexdigest()
    print(ret)  # 摘要值 153729f9fba1abc5aa39c8bae78a172e
    
    
    
    md5_obj1 = hashlib.md5()
    md5_obj1.update('hellof '.encode('utf-8'))
    md5_obj1.update('world'.encode('utf-8'))
    ret = md5_obj1.hexdigest()
    print(ret)  # 153729f9fba1abc5aa39c8bae78a172e
    分割

    总结如下:

    hashlib 摘要算法,不是加密算法(不能解密)
    多种算法
    md5:32位16进制的数字字符组成的字符串
        应用最广大的摘要算法
        效率高,相对不复杂,如果只是传统摘要不安全。
    sha算法:40位16进制的数字字符组成的字符串
        sha算法要比md5算法更复杂
        且shan n的数字越大算法越复杂,耗时越久,结果越长,越安全
    

      

    hashlib应用:登录验证

    登录验证 - hashlib
        两种算法 md5,sha
        常规验证 - 撞库
        加盐 - 固定的盐 会导致恶意注册的用户密码泄露
        动态加盐 - 每个用户都有一个固定的并且互不相同的盐
    
    文件的一致性校验
        给一个文件中的所有内容进行摘要算法得到一个md5的结果
    

      

    def get_md5(s):
        md5_obj = hashlib.md5()
        md5_obj.update(s.encode('utf-8'))
        ret = md5_obj.hexdigest
        return ret
    username = input('username:')
    password = input('password:')
    with open('userinfo') as f:
        for line in f:
            usr, pwd = line.strip().split('|')
            if username == usr and get_md5(password) == pwd:
                print('登录成功')
                break
        else:print('登录失败')
    登录验证
    # 加盐
    # def get_md5(s):
    #     md5_obj = hashlib.md5('盐'.encode('utf-8'))
    #     md5_obj.update(s.encode('utf-8'))
    #     ret = md5_obj.hexdigest
    #     return ret
    
    
    # 动态加盐
    # 每一个用户创建一个盐 -
    # def get_md5(user, s):
    #     md5_obj = hashlib.md5(user.encode('utf-8'))
    #     md5_obj.update(s.encode('utf-8'))
    #     ret = md5_obj.hexdigest
    #     return ret
    动态加盐
    import os,hashlib
    # def get_file_md5(file_path,buffer=1024):
    #     md5_obj = hashlib.md5()
    #     # file_path = r'3.习题讲解2.mp4'  # 路径里不能有空格
    #     file_size = os.path.getsize(file_path)
    #     with open(file_path, 'rb') as f:
    #         while file_size:
    #             content = f.read(buffer)
    #             file_size -= len(content)
    #             md5_obj.update(content)
    #     return md5_obj.hexdigest()
    #
    # print(get_file_md5(r'3.习题讲解2.mp4'))
    校验大文件

    二、configparser模块

    配置文件
        'userinfo'
            当你把你的程序copy到其他机器上其他目录下
            如果你不是在当前这个userinfo文件所在的目录去执行py文件
    
    绝对路径:
        当你把整个程序包括userinfo文件copy到另一台机器上
    
    开发环境
    测试环境
    生产环境
    
    把路径记录在文件里
    

      

    # 注释1
    ; 注释2
    
    [section1]
    k1 = v1
    k2:v2
    user=egon
    age=18
    is_admin=true
    salary=31
    
    [section2]
    k1 = v1
    配置文件
    import configparser
    
    config=configparser.ConfigParser()
    config.read('a.cfg')
    
    #查看所有的标题
    res=config.sections() #['section1', 'section2']
    print(res)
    
    #查看标题section1下所有key=value的key
    options=config.options('section1')
    print(options) #['k1', 'k2', 'user', 'age', 'is_admin', 'salary']
    
    #查看标题section1下所有key=value的(key,value)格式
    item_list=config.items('section1')
    print(item_list) #[('k1', 'v1'), ('k2', 'v2'), ('user', 'egon'), ('age', '18'), ('is_admin', 'true'), ('salary', '31')]
    
    #查看标题section1下user的值=>字符串格式
    val=config.get('section1','user')
    print(val) #egon
    
    #查看标题section1下age的值=>整数格式
    val1=config.getint('section1','age')
    print(val1) #18
    
    #查看标题section1下is_admin的值=>布尔值格式
    val2=config.getboolean('section1','is_admin')
    print(val2) #True
    
    #查看标题section1下salary的值=>浮点型格式
    val3=config.getfloat('section1','salary')
    print(val3) #31.0
    读取
    import configparser
    
    config=configparser.ConfigParser()
    config.read('a.cfg',encoding='utf-8')
    
    
    #删除整个标题section2
    config.remove_section('section2')
    
    #删除标题section1下的某个k1和k2
    config.remove_option('section1','k1')
    config.remove_option('section1','k2')
    
    #判断是否存在某个标题
    print(config.has_section('section1'))
    
    #判断标题section1下是否有user
    print(config.has_option('section1',''))
    
    
    #添加一个标题
    config.add_section('egon')
    
    #在标题egon下添加name=egon,age=18的配置
    config.set('egon','name','egon')
    config.set('egon','age',18) #报错,必须是字符串
    
    
    #最后将修改的内容写入文件,完成最终的修改
    config.write(open('a.cfg','w'))
    改写

    三、logging模块

    函数式简单配置

    import logging
    logging.basicConfig(level=logging.INFO)
    logging.debug('debug message')  # 计算或者工作的细节
    logging.info('info message')  # 记录一些用户的增删改查的操作
    logging.warning('warning message')  # 警告操作
    logging.error('error message')  # 错误操作
    logging.critical('critical message')  # 严重的错误  直接导致程序出错退出的操作
    

      默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。

    灵活配置,格式输出,输出位置

    # logging.basicConfig(level=logging.INFO,
    #                     format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    #                     datefmt='%c',
    #                     filename='test.log')
    # logging.warning('input a string type') # 警告操作
    # logging.error('EOF ERROR ') # 警告操作
    # logging.info('小明买了三条鱼') # 警告操作
    

     

    logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
    
    filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
    filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
    format:指定handler使用的日志显示格式。
    datefmt:指定日期时间格式。
    level:设置rootlogger(后边会讲解具体概念)的日志级别
    stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
    
    format参数中可能用到的格式化串:
    %(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用户输出的消息
    配置参数

    对象配置:解决中文问题。解决同时向文件和屏幕输出的问题。

    # 先创建一个log对象 logger
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    # 还要创建一个控制文件输出的文件操作符
    fh = logging.FileHandler('mylog.log')
    # 还要创建一个控制屏幕输出的屏幕操作符
    sh = logging.StreamHandler()
    # 要创建一个格式
    fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fmt2 = logging.Formatter('%(asctime)s - %(name)s[line:%(lineno)d] - %(levelname)s - %(message)s')
    
    # 文件操作符 绑定一个 格式
    fh.setFormatter(fmt)
    # 屏幕操作符 绑定一个 格式
    sh.setFormatter(fmt2)
    sh.setLevel(logging.WARNING)
    # logger对象来绑定:文件操作符, 屏幕操作符
    logger.addHandler(sh)
    logger.addHandler(fh)
    

      

    四、正则表达式与re模块

    1.什么是正则:与字符串打交道的

      正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则。(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

    2.正则表达式的常见应用场景

    a.判断某一个字符串是否符合规则:注册页

    b.将符合规则的内容从一个庞大的字符串体系中提取出来:爬虫、日志分析

    3.字符组

      在字符组中所有的字符都可以匹配任意一个字符位置上出现的内容。如果在字符串中有任意的一个字符是字符组中的内容,那么就是匹配的项。

    元字符
    d 表示匹配一个数字[0-9]
    w 匹配数字,字母, 下划线
    s 匹配任意空白键
    
     匹配换行符
    	 匹配制表符
     匹配一个单词的边界
        o hello world  hello结尾的o
        h 开头
    ^ 匹配字符串的开头
    $ 匹配字符串结尾
        ^hello$
            hello hello hello 什么都匹配不到
            hello  匹配到hello
    W 匹配非字母或数字下划线
    D
    S
    a|b 匹配字符a或b
    () 匹配括号内的表达式,也表示一个组
    [...] 匹配一个字符组
    [^...] 匹配非字符组
    . 匹配除了换行符之外的任意字符
        a. 匹配a后一个字符
    
    量词
    * 重复零次或多次   可以0次 '+' + '?'   ? + *
    + 重复一次或多次   最少1次
    ?重复零次或一次    1次或0次
    {n} 重复n次
    {n,} 重复n次或多次
    {n,m} 重复n到m次
    
    d+ 整数
    d+.d+ 小数
    d+.d+|d+  整数或小数
    d+(.d+)?   整数或小数 分组
    
    贪婪匹配:正则会尽量多的帮我们匹配
      <.*>   默认贪婪 回溯算法 

    非贪婪匹配:会尽量少的为我们匹配
      <.*?>  量词? 表示非贪婪 惰性匹配
      .*?x 表示匹配任意长度任意字符遇到一个x就立即停止
     
    元字符
    元字符 量词
    元字符 量词 ? 在量词的范围内尽量少的匹配这个元字符
    分组 对某些固定的内容坐量词约束
    或 把长的放在前面


    转义符
    pattern = r'\n'
    s = r' '

      

    身份证号码是一个长度为15或18个字符的字符串,
    如果是15位则全部 数字组成,首位不能为0;  [1-9]d{14}
    如果是18位,首位不能为0 前17位全部是数字,末位可能是数字或x [1-9]d{16}[dx]
    [1-9]d{16}[dx]|[1-9]d{14}
    [1-9]d{14}(d{2}[dx])?
    判断身份证号是否合法

    4.re模块

    import re
    
    #findall
    ret = re.findall('d+', '334df32tjgjf3f')
    print(ret)
    # 参数 正则表达式 待匹配的字符串
    # 返回值是一个列表,里面是所有匹配到的项
    
    
    
    
    # search
    ret = re.search('d+', '334df32tjgjf3f')
    print(ret)
    if ret:
        print(ret.group())
    # search  参数 正则表达式 待匹配的字符串
    # 返回值:
    #   返回一个SRE_Match对象
    #   通过group取值
    #   且只包含第一个匹配到的值
    

    # match
    # match 从头开始匹配。相当于search('^d','3423jdfjsdl')
    # 开头没有匹配上就返回None
    ret = re.match('d+', '34483djsjsf085jd8dfsj')
    print(ret)
    print(ret.group())

    # 替换sub
    s = 'luffy12zoro34sanji'
    ret = re.sub('d+', '|', s, 1) # 只替换一个
    print(ret)
    ret = re.subn('d+', '|', s, 2) #
    print(ret) # 返回替换次数



    # compile  编译正则规则
    '''
    正则-- > re模块 翻译 -- 字符串操作-- 编译-- 字节码-- 解释器执行代码


    '''
    com = re.compile('d+') # 代替正则规则
    print(com)
    ret = com.search('sdjgl3439dsfj')
    print(ret.group())
    ret = com.finditer('sg3j9sdgj0')
    for i in ret:
    print(i.group())

    # finditer  迭代器  节省空间的方法
    ret = re.finditer('d+', 'sjfsl32423jdfsd3d9')
    print(ret)
    for i in ret:
    print(i.group())


      

    findall优先级

    # findall有一个特点,会优先显示分组中的内容
    ret = re.findall('www.(baidu|bilibili).com','www.baidu.com')
    print(ret)  # ['baidu'] 直接显示分组中的内容
    
    
    ret2 = re.search('www.(baidu|bilibili).(com)','www.baidu.com')
    print(ret2.group(0))  # 默认  www.baidu.com
    print(ret2.group(1))  # 分组第一个元素 baidu
    print(ret2.group(2))  # 分组第二个元素 com
    

      

    split优先级

    # 切割split
    s = 'luffy123zoro456sanji'
    ret = re.split('d+', s)  # 不保留数字
    print(ret)
    ret2 = re.split('(d+)', s)  # 保留数字
    print(ret2)
    ret3 = re.split('d(d)', s)  # 保留数字的倒数第二位
    print(ret3)
    ret = re.split('(d)d', s)  # 保留数字第一位
    print(ret)
    
    #在匹配部分加上()之后所切出的结果是不同的,
    #没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
    #这个在某些需要保留匹配部分的使用过程是非常重要的
    

      

    分组命名、分组约束

    (?:正则表达式) 表示取消优先显示功能
    (?P<组名>正则表达式) 表示给这个组起一个名字
    (?P=组名) 表示引用之前组的名字,引用部分匹配到的内容必须和之前那个组中的内容一模一样

    分组命名、分组约束
    <h1>函数</h1>
    <a>函数</a>
    <(.*?)>.*?</(.*?)>
    
    pattern = '<(?P<tag>.*?)>.*?</(?P=tag)>'
    ret = re.search(pattern,'<h1>函数</h1>')
    print(ret)
    if ret:
        print(ret.group())
        print(ret.group(1))
        print(ret.group('tag'))
    
    
    pattern = r'<(.*?)>.*?</1>'
    ret = re.search(pattern,'<a>函数</a>')
    print(ret)
    if ret:
        print(ret.group())
        print(ret.group(1))
    

      

  • 相关阅读:
    第十二周作业
    第十二周上机练习
    第十一周作业
    第十一周上机练习
    第十周上机作业
    第九周上机练习
    第八周作业
    软件测试第一次作业
    Jsp第二次作业
    JSP第一次作业
  • 原文地址:https://www.cnblogs.com/eaoo/p/9600062.html
Copyright © 2020-2023  润新知