日志模块与 re 模块

一 logging模块

1、logging 模块介绍

logging模块 :日志模块,记录日志

import logging

# 一:日志配置
logging.basicConfig(
    # 1、日志输出位置:1、终端 2、文件
    filename='access.log', #显示到文件中(自己创建文件),此时不能同时在终端与文件中显示。  #如果不指定,默认打印到终端

    # 2、日志格式 :打印的日志格式
  '''
  module:哪个模块在记录日志
  '''
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',

    # 3、时间格式
    datefmt='%Y-%m-%d %H:%M:%S %p',  #%p :显示上下午

    # 4、日志级别
    # critical => 50
    # error => 40
    # warning => 30
    # info => 20
    # debug => 10
    level=30,
)

# 二:输出日志
logging.debug('调试debug')  #调试用的 #10
logging.info('消息info') #20
logging.warning('警告warn') #30
logging.error('错误error') #40
logging.critical('严重critical') #50


#结果展示:
'''
# 注意下面的root是默认的日志名字
WARNING:root:警告warn
ERROR:root:错误error
CRITICAL:root:严重critical
'''

#为什么结果显示的上面三条信息,前两条没有显示?
'''
设置了日志级别level为30(注:默认也是30),上面两条日志级别比较低,分别为10,20
'''

2、如何定制日志格式

"""
logging配置
"""

import os

# 1、定义三种日志输出格式,日志中可能用到的格式化串如下
# %(name)s Logger的名字,是记录信息的key
# %(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用户输出的消息

# 2、强调:其中的%(name)s为getlogger时指定的名字   如#task_id:测试日志 或者 用户交易

#日志记录的格式1文件)
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'
#日志记录的格式2(终端)
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

#日志记录的格式3(文件)
test_format = '%(asctime)s] %(message)s'

BASE_DIR = os.path.dirname(os.path.abspath(__file__))  
LOG_PATH = os.path.join(BASE_DIR, 'log') #log文件的目录
ATM_LOG_PATH = os.path.join(LOG_PATH, 'ATM') 

# 3、日志配置字典
LOGGING_DIC = {
    'version': 1, #版本,自定义的
    'disable_existing_loggers': False, #关闭已存在日志 #自定义的
    'formatters': {   #formatters:日志记录的格式分类,名字是固定的
        'standard': { #文件采用的日志格式,名字是自定定义的
            'format': standard_format  #format:日志记录的格式,名字是固定的
        },
        'simple': { #终端采用的日志格式,名字是自定定义的
            'format': simple_format
        },
        'test': { #文件采用的日志格式,名字是自定定义的
            'format': test_format
        },
    },
 
    'filters': {}, #不用管
  
  	#handlers是日志的接收者,不同的 handler会将日志输出到不同的位置(如终端或文件)
    'handlers': {  
        #1) 终端接收日志
        'console': { #console是自定义的,它是接收日志的位置方式名字
            'level': 'DEBUG', #接收日志记录级别
          	#往哪里输,输出到哪个位置
            'class': 'logging.StreamHandler',  # 打印到屏幕
          	#日志记录格式
            'formatter': 'simple' 
        },
      
        #2) 文件接收日志,收集info及以上的日志(等级)
        'default': { #default是自定义的, 它是接收日志的位置方式名字
            'level': 'DEBUG', #接收日志记录级别
          	#往哪里输,输出到哪个位置
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,日志轮转
          	#日志记录格式
            'formatter': 'standard',
            # 可以定制日志文件路径
            # BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # log文件的目录
           # LOG_PATH = os.path.join(BASE_DIR,'a1.log')
          	#保存的日志文件的文件名
          	 # LOG_PATH = os.path.join(BASE_DIR,'a1.log')
            'filename': LOG_PATH,  # 日志文件  #这里应该放绝对路径
          	#日志文件大小
            'maxBytes': 1024*1024*5,  # 日志大小5M, 超过就会新建一个日志文件,将老的日志转到新建的文件中
            'backupCount': 5, #日志备份最多保存几份,超过的就会被删除
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
      
      	#3) 其他自定义日志,文件接收
        'other': {
            'level': 'DEBUG',#接收日志记录级别
          	#往哪里输
            'class': 'logging.FileHandler',  # 保存到文件
          	#日志记录格式
            'formatter': 'test',
          	#os.path.join(os.path.dirname(os.path.dirname(__file__)), 'a2.log')
            'filename': 'a2.log',   #这里应该放绝对路径
            'encoding': 'utf-8',
        },
    },
  
  	#loggers 是日志的产生者,产生的日志会传递给 handler 然后控制输出
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置,即以下:
      	#日志产生配置 1
        '测试日志': { # 日志的产生者 1 (测试日志:是记录信息的 key)
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
          	#记录日志级别
            'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制),
            'propagate': False,  # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
        },
      
      	#日志产生配置 2
        '用户交易': { # 日志的产生者 2 (用户交易:是记录信息的key)
            'handlers': ['other',], #只将日志打印到文件
          	#记录日志级别
            'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
            'propagate': False,# 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
        },
      
       #日志产生配置 3 (针对不同场景,输出不同的记录关键字:这是在logging.getLogger(__name__)找不到__anme__ 时,会使用这个日志产生配置)
       '': { #日志的产生者3  记录信息的key为:logging.getLogger(__name__)的__name__
            'handlers': ['default',], 
         		#记录日志级别
            'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
            'propagate': False,# 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
        },
    },
}


3 使用

import settings

# !!!强调!!!
# 1、logging是一个包,需要使用其下的config、getLogger,可以如下导入
# from logging import config    #只要是导入,就会导入 logging
# from logging import getLogger

# 2、也可以使用如下导入 
import logging.config # 因为导入了 logging,所以包下面的模块都会被导入
#这样连同logging.getLogger都一起导入了,然后使用前缀logging.config.   

# 3、加载配置
logging.config.dictConfig(settings.LOGGING_DIC)

# 4、拿到日志的产生者,即 loggers 来产生日志
#第一个日志产生者:'测试日志'
#第二个日志产生者:'用户交易'
#第三个日志产生者: ''
logger1=logging.getLogger('测试日志') #日志的方式(信息往哪里输入)
logger1.debug('这debug级的日志')
logger1.info('info级的日志')
logger1.error('error级的日志')

# logger2=logging.getLogger('11') # 传入日志产生者,因为'11'不存在,所以调用第三个日志产生者: ''
# logger2.debug('debug 级的日志')
# logger2.info('正常信息级的日志')
# logger2.critical('critical级别的日志')

# logger1=logging.getLogger('用户交易') #记录日志的方式(往哪里输入)
# logger1.info('这是二条日志')

common.py

4 两个重要的知识

1、日志名的命名

日志名是区别日志业务归属的一种非常重要的标识

2、日志轮转

日志记录着程序员运行过程中的关键信息

当日志文件达到一定大小时,会进行日志轮转

二 re模块

re 模块是 python 的内置模块,提供了正则匹配方法

1、什么是正则

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

生活中处处都是正则:

​ 四条腿:桌子,椅子,马,猪等

​ 四条腿,活的:马,猪等

2、 re 模块有什么作用

​ 在文本或者字符串内获取你所需要的东西

s = '我爱中国'
import re

res = re.findall('我爱(.{2})',s)
print(res) #['中国']

​ re模块基础使用

s = '我爱中国, 中国'
import re

res = re.findall('中国',s)
print(res) #['中国', '中国']

3、元字符

元字符会有特殊的意义

常用的元字符有:

    1. ^  从开头进行匹配
    2. $  从结尾进行匹配
    3. |  相当于or,或者的意思
    4. [] 找到[]内的任意一个元素的所有元素,^放在里面就是反取
    5. .  任意一个字符
    6. {3}大括号前面的字符匹配3次,{m,n}匹配前一个字符m至n次,{m,}则匹配m至无限次
    7. *  前面的字符匹配0-无穷个
    8. +  前面的字符匹配1-无穷个
    9. ?  前面的字符匹配0-1个
    10. \d 匹配数字[0-9]                \D  匹配非数字
    11. \s 匹配空白字符(包括\t\r\n\f\v) \S  匹配非空白字符
    12. \w 匹配字符,字母,数字,下划线     \W  匹配非字母数字下划线
    13. .*?:视情况而定

1 例题

# =================================匹配模式=================================
#一对一的匹配
# 'hello'.replace(old,new)
# 'hello'.find('pattern')

#正则匹配
import re
'''findall:找所有字符,返回值是列表'''
#\w:字母数字下划线
print(re.findall('\w','hello egon 123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']
#\W:非字母数字下划线
print(re.findall('\W','hello egon 123')) #[' ', ' ']

#\s:空白字符
print(re.findall('\s','hello  egon  123')) #[' ', ' ']
#\S:非空字符
print(re.findall('\S','hello  egon  123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']

#\n \t都是空,都可以被\s匹配
print(re.findall('\s','hello \n egon \t 123')) #[' ', '\n', ' ', ' ', '\t', ' ']

#\n与\t
print(re.findall(r'\n','hello egon \n123')) #['\n']
print(re.findall(r'\t','hello egon\t123')) #['\n']

#\d :数字
print(re.findall('\d','hello egon 123')) #['1', '2', '3']
#\D :非数字
print(re.findall('\D','hello egon 123')) #['h', 'e', 'l', 'l', 'o', ' ', 'e', 'g', 'o', 'n', ' ']

#\A :从头开始匹配(只匹配开头) \A==>^
print(re.findall('\Ahe','hello egon 123')) #['he'],\A==>^
#\Z: 从尾部开始匹配(只匹配结尾) \Z==>$
print(re.findall('123\Z','hello egon 123')) #['123'],\Z==>$

#^:
print(re.findall('^h','hello egon 123')) #['h']
#$:
print(re.findall('3$','hello egon 123')) #['3']

# 重复匹配:| . | * | ? | .* | .*? | + | {n,m} |
#.:匹配除了 \n 之外的任意一个字符
print(re.findall('a.b','a1b')) #['a1b']
print(re.findall('a.b','a1b a*b a b aaab')) #['a1b', 'a*b', 'a b', 'aab']
print(re.findall('a.b','a\nb')) #[]
print(re.findall('a.b','a\nb',re.S)) #['a\nb']
#re.DOTALL:指定re.DOTALL之后才能匹配换行符(\n)
print(re.findall('a.b','a\nb',re.DOTALL)) #['a\nb']同上一条意思一样

#*:左侧字符重复0次或无穷次,性格贪婪,不单独使用
print(re.findall('ab*','bbbbbbb')) #[]
print(re.findall('ab*','a')) #['a']
print(re.findall('ab*','abbbb')) #['abbbb']
print(re.findall('ab*', 'abcbbbb')) #['ab']
print(re.findall('ab.*', 'abcbbb')) #[['abcbbb']
print(re.findall('ab*', 'aaaaa')) #['a', 'a', 'a', 'a', 'a']


#?:左侧字符重复 0 次或 1 次,性格非贪婪 不单独使用
print(re.findall('ab?','a')) #['a']
print(re.findall('ab?','abbb')) #['ab']


#匹配所有包含小数在内的数字,即整数或者小数 (\.: \代表转义,让符号 . 表示为一个点)
print(re.findall('\d+\.?\d*',"asdfasdf123as1.13dfa12adsf1asdf3")) #['123', '1.13', '12', '1', '3']

#.*默认为贪婪匹b
#'a.*b':匹配拿到[a,b](a为要匹配的字符串中第一个a, b为字符串中最后一个b)之间所有的非\n 字符,贪婪匹配
print(re.findall('a.*b','a1b22222222b')) #['a1b22222222b']
print(re.findall('a.*b','a\nb22222222b')) #[] #有\n,所以为空
print(re.findall('a.*b','a22222222b')) #['a22222222b']

#.*?为非贪婪匹配:推荐使用
#a.*?b:匹配拿到[a,b](a为要匹配的字符串中的第一个 a, b为字符串中找到的第一个b)之间所有的非\n 字符
print(re.findall('a.*?b','a1b22222222b22')) #['a1b']
print(re.findall('a.*?b','ab22222222b22')) #['ab']
print(re.findall('a.*?b','a22222222b22')) #['a22222222b']


#.?:非贪婪匹配
print(re.findall('a.?b','a22222222b')) #[]
print(re.findall('a.?b','ab22222222b')) #['ab']
print(re.findall('a.?b','a1b22222222b')) #['a1b']

#+:左侧字符重复 1 次或者无穷次,性格贪婪
print(re.findall('ab+','a')) #[]
print(re.findall('ab+','abbb')) #['abbb']

#{n,m}:左侧字符重复 n 次到 m 次,性格贪婪
#{0,} => *
#{1,} => +
#{0,1}=> ?
#{n}:单独一个 n 代表重复 n 次,多一次也不行
print(re.findall('ab{2}','abbb')) #['abb']
print(re.findall('ab{2,4}','abbb')) #['abbb']
print(re.findall('ab{1,}','abbb')) #'ab{1,}' ===> 'ab+'
print(re.findall('ab{0,}','abbb')) #'ab{0,}' ===> 'ab*'

#[]:匹配指定字符范围中的一个字符,[]括号内为匹配字符范围
print(re.findall('a[1*-]b','a1b a*b a-b')) #[]内的都为普通字符了,且如果-没有被转意的话,应该放到[]的开头或结尾
#[^]:[]内的^代表的意思是取反
print(re.findall('a[^1*-]b','a1b a*b a-b a=b')) #['a=b'],  []内的^代表的意思是取反,即不是 1,*,-
print(re.findall('a[0-9]b','a1b a*b a-b a=b, a5b')) #['a1b', 'a5b'] , [0-9]:[]内的-左右两边都是数字或者字母,代表区间范围[0,9]
print(re.findall('a[a-z]b','a1b a*b a-b a=b aeb, abb')) #['aeb', 'abb'] ,[a-z]:[]内的-左右两边都是数字或者字母,代表区间范围[a,z]
print(re.findall('a[a-zA-Z]b','a1b a*b a-b a=b aeb aEb')) #['aeb', 'aEb']

#\# print(re.findall('a\\c','a\c')) #对于正则来说a\\c确实可以匹配到a\c,但是在python解释器读取a\\c时,会发生转义,然后交给re去执行,所以抛出异常
print(re.findall(r'a\\c','a\c')) #r代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义
print(re.findall('a\\\\c','a\c')) #同上面的意思一样,和上面的结果一样都是['a\\c']

#():分组 ,findall的结果不是匹配的全部内容,而是组内()的内容
#()内的 ?: ,可以让结果为匹配的全部内容
print(re.findall('ab+','ababab123')) #['ab', 'ab', 'ab']
print(re.findall('(ab)+123','ababab123')) #['ab'],匹配到末尾的ab123中的ab
print(re.findall('(?:ab)+123','ababab123')) #['ababab123'] findall的结果不是匹配的全部内容,而是组内的内容,?:可以让结果为匹配的全部内容:'ab+123'
print(re.findall('(?:ab)+123','b123'))  #[]
print(re.findall('(?:ab)+123','aaaab123'))  #['ab123']

print(re.findall("ab+(.*?)","ab22222222ab123")) #[''] ['']
print(re.findall('(ab)+123','ab123abab123')) #['ab', 'ab']
print(re.findall("ab'(.*?)'","ab'22222222b123'")) #['22222222b123']

#具体示例:
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))#['http://www.baidu.com']  返回值为匹配内容中的分组内的内容
print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com">点击</a>'))#['href="http://www.baidu.com"'] 返回值为匹配内容
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com   http://www.baidu.com">点击</a>'))#['http://www.baidu.com   http://www.baidu.com']

#| 或
#a|b:匹配 a 或 b
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company')) #['companies', 'company']

2、re模块提供的方法

# ===========================re模块提供的方法介绍===========================
import re
#1
print(re.findall('e','alex make love') )   #['e', 'e', 'e'],返回所有满足匹配条件的结果,放在列表里
#2
print(re.search('e','alex make love').group()) #e,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。

#3
print(re.match('e','alex make love'))    #None,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match

#4
print(re.split('[ab]','abcd'))     #['', '', 'cd'],先按'a'分割得到''和'bcd',再对''和'bcd'分别按'b'分割

#5
print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默认替换所有
print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love
print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love
print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex

print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),结果带有总共替换的个数


#6
obj=re.compile('\d{2}')

print(obj.search('abc123eeee').group()) #12
print(obj.findall('abc123eeee')) #['12'],重用了obj

3 补充

补充一

import re
print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) #['h1']
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group()) #<h1>hello</h1>
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").groupdict()) #<h1>hello</h1>

print(re.search(r"<(\w+)>\w+</(\w+)>","<h1>hello</h1>").group())
print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>").group())

补充二

#补充二
import re

#使用|,先匹配的先生效,|左边是匹配小数,而findall最终结果是查看分组,所有即使匹配成功小数也不会存入结果
#而不是小数时,就去匹配(-?\d+),匹配到的自然就是,非小数的数,在此处即整数
#
print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出所有整数['1', '-2', '60', '', '5', '-4', '3']

#找到所有数字:
print(re.findall('\D?(\-?\d+\.?\d*)',"1-2*(60+(-40.35/5)-(-4*3))")) # ['1','2','60','-40.35','5','-4','3']


#计算器作业参考:http://www.cnblogs.com/wupeiqi/articles/4949995.html
expression='1-2*((60+2*(-3-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'

content=re.search('\(([\-\+\*\/]*\d+\.?\d*)+\)',expression).group() #(-3-40.0/5)
posted @ 2020-04-01 20:27  蛋蛋的丶夜  阅读(118)  评论(0编辑  收藏  举报