Python常用模块(4)—— re、logging、hashlib、subprocess

re 模块:与正则相关的模块

在使用 re 模块之前,需要先了解正则表达式(regular expression),描述了一种字符串匹配的模式(pattern),可以用来检查一个字符串是否含有某个子字符串、将匹配的子字符串替换或者从某个字符串中取出符合某个条件的子字符串等。

import re

# w 匹配字母数字及下划线
print(re.findall('w','hello 123_ */-='))

# W 匹配非字母数字下划线
print(re.findall('W','hello 123_ */-='))

# s 匹配任意空白字符, 等价于(空格, 
, 	, 
, f)
print(re.findall('s','hell
o 12	3_ */-='))

# S 匹配任意非空字符
print(re.findall('S','hell
o 12	3_ */-='))

# d 匹配任意数字, 等价于(0--9)
print(re.findall('d','hell
o 12	3_ */-='))

# D 匹配任意非数字
print(re.findall('D','hell
o 12	3_ */-='))

# 
 匹配换行符
print(re.findall('
','hell
o 1
2	3_ */-='))

# 	 匹配制表符
print(re.findall('	','hell
o 12	3_ */-='))

# ^ 匹配字符串的开头
print(re.findall('qiu', 'my name is qiu, qiu like music'))
print(re.findall('^qiu', 'my name is qiu, qiu like music'))
print(re.findall('^qiu', 'qiu my name is qiu, qiu like music'))

# $ 匹配字符串的结尾, 不匹配末尾的换行符
print(re.findall('qiu$', 'qiu my name is qiu, qiu like qiu'))
print("=" * 50)

# 重复匹配
# . 代表匹配换行符以外的任意一个字符
print(re.findall('a.c', 'abc a*c a
cfdsa cds alc a+c a	cdjsh'))  # ['abc', 'a*c', 'a c', 'alc', 'a+c']
# 如果想匹配换行符, re.DOTALL, 匹配点所有的字符
print(re.findall('a.c', 'abc a*c a
cfdsa cds alc a+c a	cdjsh',re.DOTALL))
# 匹配换行符以外的任意两个字符
print(re.findall('a..c', 'abc alc aac asd aaaaac a *c a+c adsadsa ='))



print(re.findall('a.c', 'abc alc aac aAc aBc asd aaaaac a *c a+c a-c a/c adsadsa = a1c a2c'))
# [] 代表匹配一个字符, 该字符属于中括号内指定的字符
# 取出a.c之间是小写字母的字符串
print(re.findall('a[a-z]c', 'abc alc aac aAc aBc asd aaaaac a *c a+c a-c a/c adsadsa = a1c a2c'))
# 取出a.c之间是大写字母的字符串
print(re.findall('a[A-Z]c', 'abc alc aac aAc aBc asd aaaaac a *c a+c a-c a/c adsadsa = a1c a2c'))
# 取出a.c之间只要+-*/ (注意: -只能放在首或尾, 放中间是连接符号, 一定要放中间则使用转义字符 -)
print(re.findall('a[-+/*]c', 'abc alc aac aAc aBc asd aaaaac a *c a+c a-c a/c adsadsa = a1c a2c'))
print(re.findall('a[+-/*]c', 'abc alc aac aAc aBc asd aaaaac a *c a+c a-c a/c adsadsa = a1c a2c'))
# ^放在[]内表示取反, 除了小写字母都可以取出
print(re.findall('a[^a-z]c', 'abc alc aac aAc aBc asd aaaaac a *c a+c a-c a/c adsadsa = a1c a2c'))
print("=" * 50)


# * ? + {n,m} 都不能单独使用, 必须与其他字符连用
# * 代表*左侧的字符出现0次或无穷次
print(re.findall('ab*', 'a ab abbb abbbb a1bbbb a-123'))
# 有b就获取, 有多少b就获取多少, 没有就获取匹配到的
# ['a', 'ab', 'abbb', 'abbbb', 'abbbb', 'a', 'a']
# 也可以用{n,m}表示0到无穷次
print(re.findall('ab{0,}', 'a ab abbb abbbb a1bbbb a-123'))
print("=" * 50)

# ? 代表?左侧的字符出现0次或1次
print(re.findall('ab?', 'a ab abbb abbbb a1bbbb a-123'))
# 有b就获取, 不管有多少b只获取一个, 没有就获取匹配到的
# ['a', 'ab', 'ab', 'ab', 'a', 'a']
# 也可以用{n,m}表示0到1次
print(re.findall('ab{0,1}', 'a ab abbb abbbb a1bbbb a-123'))
print("=" * 50)

# + 代表+左侧的字符出现1次或无穷次
print(re.findall('ab+', 'a ab abbb abbbb a1bbbb a-123'))
# 有b就获取, 有多少b就获取多少, 没有就不获取
# ['ab', 'abbb', 'abbbb']
# 也可以用{n,m}表示1到无穷次
print(re.findall('ab{1,}', 'a ab abbb abbbb a1bbbb a-123'))
print("=" * 50)


# {n,m} 左侧的字符最少匹配n次且最多匹配m次, n <= m
print(re.findall('ab{1,3}', 'a ab abbb abbbb a1bbbb a-123'))
# ['ab', 'abbb', 'abbb']
print("=" * 50)


# .* 贪婪匹配, 任意个数任意字符
print(re.findall('a.*c', 'ab123dsac32155sdadasc'))

# .*? 非贪婪匹配
print(re.findall('a.*?c', 'ab123dsac32155sdadasc'))


# () 分组
print(re.findall('expression=".*"','expression="1+2+3/4*5" qiu="beautiful"'))
print(re.findall('expression=".*?"','expression="1+2+3/4*5" qiu="beautiful"'))
print(re.findall('(expression)=".*?"','expression="1+2+3/4*5" qiu="beautiful"'))
# 取出表达式1+2+3/4*5
print(re.findall('expression="(.*?)"','expression="1+2+3/4*5" qiu="beautiful"'))

# 取出网址https://www.qiuxirufeng.com
print(re.findall('href="(.*?)"','<p>段落</p><a href="https://www.baidu.com">点我啊</a><h1>标题</h1><a href="https://www.qiuxirufeng.com">点我啊</a>'))


# a|b 取a或b
print(re.findall('a|b', 'ab123adbhsga'))

# 取出和公司相关的单词
print(re.findall('companies|company', 'Too many companies have gone bankrupt, and the next one is my company'))

# ['ies', 'y']
print(re.findall('compan(ies|y)', 'Too many companies have gone bankrupt, and the next one is my company'))
# ?: 取出的内容并非组内的内容, 而是包含外面的  ['companies', 'company']
print(re.findall('compan(?:ies|y)', 'Too many companies have gone bankrupt, and the next one is my company'))


# 取 ac
# r'a\c' 先交给python解释器识别, 识别为'a\c', 再交给re模块识别, 识别为'ac'
print(re.findall(r'a\c', 'ac alc aAc aac'))
# 'a\\c', 先交给python解释器识别, 识别为'a\c', 再交给re模块识别, 识别为'ac'
print(re.findall('a\\c', 'ac alc aAc aac'))
正则表达式
# -*- coding: utf-8 -*-

import re

print(re.findall('qiu', 'qiu like music, qiu like folk music'))
# search只匹配一个, 有就返回一个对象, 没有就返回None
print(re.search('qiu', 'qiu like music, qiu like folk music'))
# 想要取结果, 后面需要加上group()方法
print(re.search('qiu', 'qiu like music, qiu like folk music').group())

print(re.findall('qi(u)', 'qiu like music, qiu like folk music'))
print(re.search('qi(u)', 'qiu like music, qiu like folk music').group())

print("=" * 50)

print(re.search('qiu', '123qiu like music, qiu like folk music'))
# match是从头开始找, 相当于search在匹配中加上^
print(re.match('qiu', '123qiu like music, qiu like folk music'))
print(re.search('^qiu', '123qiu like music, qiu like folk music'))

print("=" * 50)

li = 'qiu:22:male'.split(":")
print(li)
# 将字符串以:和空格切分
l1 = re.split(':| ', 'qiu:22:male xxx')
print(l1)

print("=" * 50)

# 将qiu替换成xi, 可以指定替换次数
print(re.sub('qiu', 'xi', 'qiu is nice, qiu qiu qiu'))
print(re.sub('qiu', 'xi', 'qiu is nice, qiu qiu qiu', 1))

# 把所有以xx结尾的都替换成qiu
s = 'lxx is good, lllxx wxx cxx are good'
print(re.sub('[a-z]+xx','qiu', s))

print("=" * 50)

# print(re.findall('qiu', 'qiu like music, qiu like folk music'))
# print(re.search('qiu', 'qiu like music, qiu like folk music').group())
pattern = re.compile('qiu')
print(pattern.findall('qiu like music, qiu like folk music'))
print(pattern.search('qiu like music, qiu like folk music'))
re模块的使用

logging 模块:与日志相关操作的模块

logging模块主要可以根据自定义日志信息,在程序运行的时候将日志打印在终端及记录日志到文件中。logging支持的日志有五个级别:

  debug() 调试级别,一般用于记录程序运行的详细信息,对应数字级别10

  info() 事件级别,一般用于记录程序的运行过程,对应数字级别20

  warnning() 警告级别,,一般用于记录程序出现潜在错误的情形,对应数字级别30

  error() 错误级别,一般用于记录程序出现错误,但不影响整体运行,对应数字级别40

  critical() 严重错误级别 , 出现该错误已经影响到整体运行,对应数字级别50

# 日志级别遵循原则: 自下而上进行匹配
# debug --> info --> warning --> error --> critical
logging.debug('调试信息')
logging.info('正常信息')
logging.warning('警告信息')
logging.error('错误信息')
logging.critical('严重错误信息')

# 运行输出
WARNING:root:警告信息
ERROR:root:错误信息
CRITICAL:root:严重错误信息
简单用法,将日志打印到终端

但是这样的输出存在一定的问题:

  1、没有指定日志级别

  2、没有指定日志格式

  3、只能在屏幕上打印,没有写入文件

所以要进行基本的日志配置

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

# 进行基本的日志配置
logging.basicConfig(filename='access.log',
                    format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=10)

logging.debug('调试信息')
logging.info('正常信息')
logging.warning('警告信息')
logging.error('错误信息')
logging.critical('严重错误信息')

# 运行后结果存放在文件中(以GBK的格式存入文件)
2018-10-21 08:43:53 - root - DEBUG - logging模块: 调试信息
2018-10-21 08:43:53 - root - INFO - logging模块: 正常信息
2018-10-21 08:43:53 - root - WARNING - logging模块: 警告信息
2018-10-21 08:43:53 - root - ERROR - logging模块: 错误信息
2018-10-21 08:43:53 - root - CRITICAL - logging模块: 严重错误信息
View Code

现在解决了上面出现的三个问题,但是又出现了新的问题:

  1、不能指定字符编码

  2、只能在文件中打印

basicConfig 参数有一个方法叫 stream,设置为 True 会提示不能同时打印到屏幕和文件里,写入文件中的信息目前只能将文件的字符编码修改为GBK

import logging

# 进行基本的日志配置
logging.basicConfig(filename='access.log',
                    format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=10,
                    stream=True
                    )

logging.debug('调试信息')
logging.info('正常信息')
logging.warning('警告信息')
logging.error('错误信息')
logging.critical('严重错误信息')

# 运行后输出
Traceback (most recent call last):
  File "E:/Python日志模块的使用/logging模块.py", line 10, in <module>
    stream=True
  File "D:Program FilesPython36liblogging\__init__.py", line 1797, in basicConfig
    raise ValueError("'stream' and 'filename' should not be "
ValueError: 'stream' and 'filename' should not be specified together
View Code

logging模块包含四种角色:logger,filter,formatter,handler

logger 负责产生日志信息,filter 负责筛选日志,这一步不是我们所需要处理的,formatter 用来控制日志的输出格式,handler 负责日志输出的目标,然后通过绑定 logger 对象与 handler 对象,让产生的日志信息能够同时在文件和控制台上打印输出,接着绑定 handler 对象与 formatter 对象,让输出信息按照指定的格式输出,最后设置日志级别,可以在 logger 与 handler两层关卡进行设置

import logging

# 1. logger: 负责产生日志信息
logger1 = logging.getLogger('交易日志')
# 2. filter: 负责筛选日志
# 这一步无需我们操作
# 3. formatter: 控制日志的输出格式
formatter1 = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                  datefmt='%Y-%m-%d %X')
formatter2 = logging.Formatter(fmt='%(asctime)s - %(message)s',
                  datefmt='%Y-%m-%d %X')

# 4. handler: 负责日志输出的目标
# 在文件中打印
h1 = logging.FileHandler(filename='a1.log', encoding='utf-8')
h2 = logging.FileHandler(filename='a2.log', encoding='utf-8')
# 在控制台输出
s = logging.StreamHandler()

# 5. 绑定logger对象与handler对象
# 让产生的logger1分别有三种输出对象, 能够同时在文件和控制台中打印输出
logger1.addHandler(h1)
logger1.addHandler(h2)
logger1.addHandler(s)

# 6. 绑定handler对象与formatter对象
# 三种输出对象在打印时需要按照某种格式输出
h1.setFormatter(formatter1)
h2.setFormatter(formatter1)
s.setFormatter(formatter2)

# 7. 设置日志级别, 可以在logger与handler两层关卡进行设置
# logger1.setLevel(30)    # 这里第一层关卡设置30已经卡死, 后面再设置输出都无效
logger1.setLevel(10)
h1.setLevel(10)
h2.setLevel(10)
s.setLevel(10)

logger1.info('qiu转账给xi一个亿')
logging模块的四种角色

上面这个写法步骤有些繁琐,所以有一个写好的配置文件,使用的时候通过执行文件直接导入即可

# -*- coding: utf-8 -*-

# 定义三种日志输出格式
standard_format = '%(asctime)s - %(filename)s - 第%(lineno)d行 - %(name)s - %(levelname)s - %(message)s'
simple_format = '%(asctime)s - %(levelname)s - %(message)s'
id_simple_format = '%(asctime)s - %(message)s'

# log文件的全路径
logfile_path1 = r'E:PythonPython Fullstackday22日志模块的使用a1.log'
logfile_path2 = r'E:PythonPython Fullstackday22日志模块的使用a2.log'

# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'formatter1': {
            'format': standard_format
        },
        'formatter2': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        # 打印到终端的日志
        's': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'formatter2'
        },
        # 打印到文件的日志, 收集info及以上的日志
        'h1': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 保存到文件
            'formatter': 'formatter1',
            'filename': logfile_path1,  # 日志文件
            'encoding': 'utf-8',  # 日志文件的编码
        },
        'h2': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 保存到文件
            'formatter': 'formatter1',
            'filename': logfile_path2,  # 日志文件
            'encoding': 'utf-8',  # 日志文件的编码
        },
    },
    'loggers': {
        'logger1': {
            'handlers': ['h1', 'h2', 's'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': False,  # 向上(更高level的logger)传递
        },
    },
}
logging配置文件
import settings
import logging.config

logging.config.dictConfig(settings.LOGGING_DIC)  # 导入上面定义的logging配置

logger1 = logging.getLogger('logger1')

logger1.debug("调试日志")
执行文件

上面这个只能获取 logger1 的信息,因为此时只定义一个 logger1 ,但当需要记录的日志文件多起来,就需要获取多个日志文件,因此这里不能写死,可以在配置文件中将其定义为空,任意指定配置名,但获取的都是相同配置

# -*- coding: utf-8 -*-

# 定义三种日志输出格式
standard_format = '%(asctime)s - %(filename)s - 第%(lineno)d行 - %(name)s - %(levelname)s - %(message)s'
simple_format = '%(asctime)s - %(levelname)s - %(message)s'
id_simple_format = '%(asctime)s - %(message)s'

# log文件的全路径
logfile_path1 = r'E:PythonPython Fullstackday22日志模块的使用a1.log'
logfile_path2 = r'E:PythonPython Fullstackday22日志模块的使用a2.log'

# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'formatter1': {
            'format': standard_format
        },
        'formatter2': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        # 打印到终端的日志
        's': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'formatter2'
        },
        # 打印到文件的日志, 收集info及以上的日志
        'h1': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 保存到文件
            'formatter': 'formatter1',
            'filename': logfile_path1,  # 日志文件
            'encoding': 'utf-8',  # 日志文件的编码
        },
        'h2': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 保存到文件
            'formatter': 'formatter1',
            'filename': logfile_path2,  # 日志文件
            'encoding': 'utf-8',  # 日志文件的编码
        },
    },
    '': {
        # 将其定义为空
        '': {
            'handlers': ['h1', 'h2', 's'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': False,  # 向上(更高level的logger)传递
        },
    },
}
logging配置文件
# -*- coding: utf-8 -*-

import settings
import logging.config

logging.config.dictConfig(settings.LOGGING_DIC)  # 导入上面定义的logging配置

logger1 = logging.getLogger('用户相关')
logger2 = logging.getLogger('交易日志')

logger1.info("调试日志")
logger2.info('qiu转账给xi一个亿')
执行文件

hashlib 模块:用来进行 hash 或者 md5 加密,且这种加密是不可逆的

hash:是一种算法,该算法接受传入的内容,经过运算得到一串 hash 值,如果把 hash 算法比喻为一座工厂,那传给 hash 算法的内容就是原材料,生成的 hash 值就是生产出的产品

hash 值有三大特性:

  1、只要传入的内容一样,得到的 hash 值必然一样

  2、只要我们使用的 hash 算法固定,无论传入的内容有多大,得到的hash值的长度是固定的

  3、不可以用 hash 值逆推出原来的内容

基于 1 和 2 可以在下载文件时做文件一致性校验,基于 1 和 3 可以对密码进行加密

import hashlib

# 1. 造出hash工厂
m = hashlib.md5()

# 2、运送原材料
m.update('你好啊'.encode('utf-8'))
m.update('啊哈'.encode('utf-8'))

# 3、产出hash值
print(m.hexdigest())  # a4be72e57bc198333ed98188c48b2f85
# ==================================================================
# 1、造出hash工厂
m = hashlib.md5(''.encode('utf-8'))

# 2、运送原材料
m.update('好啊啊哈'.encode('utf-8'))

# 3、产出hash值
print(m.hexdigest())  # a4be72e57bc198333ed98188c48b2f85
# ==================================================================
# 应用一:文件一致性校验
# 1、造出hash工厂
m = hashlib.sha512(''.encode('utf-8'))

# 2、运送原材料
m.update('好啊sadfsadf啊哈asdfsafdadsadsadfsadfsadfsadfasdff的张铭言'.encode('utf-8'))

# 3、产出hash值
print(m.hexdigest())
# 977c7a4f13e76f3f026e45540c72c6ba5dbfc41357bc452fba9d9824b71c5e074298c3f62f57c6a42dd769e6a03c26be44742e4e77f284e19c106e7fba3093da

# ===================================================================
# 1、造出hash工厂
m = hashlib.md5()

# 2、运送原材料
with open(r'E:1.mp4','rb') as f:
    for line in f:
        m.update(line)

# 3、产出hash值
print(m.hexdigest())
# b5672ac47a068231f2c56da5df652f47
hashlib模块

密码加盐:对现有的密码进行处理,比如加一行文字,再对齐所有内容进行加密

password = input('>>>: ')

m = hashlib.md5()
m.update('天王盖地虎'.encode('utf-8'))
m.update(password.encode('utf-8'))
print(m.hexdigest()) 
密码加盐

Python 还有一个 hmac 模块,它内部对我们创建 key 和内容进行进一步的处理然后再加密

import hmac

m = hmac.new('小鸡炖蘑菇'.encode('utf-8'))
m.update('hello'.encode('utf-8'))
print(m.hexdigest())
hmac

subprocess 模块:用来执行系统命令

subprocess 是子进程,导入模块使用 subprocess 的 Popen() 方法调用 shell 终端执行 tasklist 命令用来显示运行在本地计算机上的所有进程,但是这时执行的 Python 程序相当于一个父进程,在子进程还没有运行结束父进程便已经结束,所以无法显示结果,可以通过 time 模块让 Python 程序睡眠一段时间,从而查看 subprocess 的执行结果

import time
import subprocess

subprocess.Popen("tasklist", shell=True)
time.sleep(3)
View Code

但是上面只能输出在控制台,倘若要输出到文件或者别的地方,所以在子进程与父进程之间要创建一个共享数据的地方,叫做管道,且是在内存中创建。当使用这些数据时,直接从管道中取,当然取出的是正确的数据,因为这是正确的管道。对于上面来说,命令有可能输入错误,于是还有错误的数据,所以应该再使用一个存放错误数据的管道,这样在取数据的时候就有了区分,得到的结果也可以做存放于文件的操作

import subprocess

obj = subprocess.Popen("tasklist",
                 shell=True,
                 # 正确数据的管道
                 stdout=subprocess.PIPE,
                 # 错误数据的管道
                 stderr=subprocess.PIPE)

stdout_res = obj.stdout.read()
print(stdout_res.decode('gbk'))

# 如果输入错误的命令,则使用下面的代码
# stderr_res = obj.stderr.read()
# print(stderr_res.decode('gbk'))
View Code
原文地址:https://www.cnblogs.com/qiuxirufeng/p/9813412.html