Python基础第十三天:常用模块

目录

  认识模块

    1、什么是模块

    2、模块的导入和使用

  常用模块

    collections模块

    时间模块

    random模块

    os模块

    sys模块

    序列化模块

    re模块

  常用模块二

    hashlib模块

    configparse模块

    logging模块

认识模块

  什么是模块?

  常见的场景:一个模块就是一个包含了Python定义和声明的文件,文件名就是模块名字加(.py)的后缀

  但是其实import加载的模块分为4个普通类别:

    1、使用Python编写的代码(.py文件)

    2、已经被编译为共享库或DLL的C或C++扩展

    3、包好一组模块的包

    4、使用C编写并链接到Python解释器的内置模块

  为何要使用模块?

  如果你退出Python解释器,然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script

  随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这也做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他模块中,实现了功能的重复利用

  模块的导入应该在程序开始的地方

  collections模块

  在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等

  1、nametuple:生成可以使用名字来访问元素内容的tuple

  2、deque:双端队列,可以快速的从另外一侧追加和推出对象

  3、Counter:计数器,主要用来计数

  4、OrderedDict:有序字典

  5、defaultdict:带有默认值的字典

  namedtuple

  我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:

>>> p = (1,2)

  但是,看到(1,2),很难看出这个tuple是用来表示一个坐标的

  这时,namedtuple就派上用场了

from collections import namedtuple
Point = namedtuple('Point',['x','y'])
p = Point(1,2)
print(p.x,p.y)
1 2

  类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:

from collections import namedtuple
#namedtuple('名称',[属性list])
Circle = namedtuple('Circle',['x','y','r'])

  deque

  使用list存储数据时,按照索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除的效率很低

  deque是为了高兴实现插入和删除操作的双端队列,适合用于队列和栈:

from collections import deque

q = deque(['a','b','c'])
q.append('x')
q.append('y')
print(q)
deque(['a', 'b', 'c', 'x', 'y'])

  deque除了实现list的append()和app()外,还支持appendleft() 和 popleft(),这样就可以非常高效地往头部添加或删除元素

  orderedDict

  使用dict时,key是无序的。在对dict做迭代时,我们无法确定key的顺序

  如果需要保存key的顺序,可以使用OrderedDict:

from collections import OrderedDict
d = dict([('a',1),('b',2),('c',3)])
#dict的key是无序的
print(d)

od = OrderedDict([('a',1),('b',2),('c',3),('d',4)])
#OrderedDict的key是有序的
print(od)
{'a': 1, 'e': 2, 'd': 3}
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])

  注意:OrderedDict的key会按照插入的顺序排序,不是key的本身排序

od = OrderedDict()
od['z'] = 1
od['y'] = 2
od['x'] = 3
#按照插入的key的顺序放回
print(od.keys())
odict_keys(['z', 'y', 'x'])

  defaultdict

  有如下值集合[11,22,33,44,55,66,77,88,99,90...],将所有大于66的值保存至字典的第一个keyzhong,将小于66的值保存到第二个key的值中

  即:{'k1': 大于66,'k2': 小于66}

values = [11,22,33,44,55,66,77,88,99,90]
my_dict = {}
for value in values:
    if value > 66:
        #注意python3x:没有has_key()
        if  'k1' in my_dict:      #my_dict.has_key('k1'):
            my_dict['k1'].append(value)
        else:
            my_dict['k1'] = [value]
    else:
        if 'k2' in my_dict:      #my_dict.has_key('k2'):
            my_dict['k2'].append(value)
        else:
            my_dict['k2'] = [value]

print(my_dict)
{'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99, 90]}

  使用defaultdict字典解决方法

from collections import defaultdict

values = [11,22,33,44,55,66,77,88,99,90]

my_dict = defaultdict(list)

for value in values:
    if value > 66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)

print(my_dict)
defaultdict(<class 'list'>, {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99, 90]})

  使用dict时,如果引用key不存在,就会抛弃KeyError,如果希望key不存在时,放回一个默认值,就可以使用defaultdict:

from collections import defaultdict
dd = defaultdict(lambda:'N/A')
dd['key1'] = 'abc'
print(dd['key1'])
print(dd['key2'])
abc
N/A

  counter

  counter类的目的是用来追踪值出现的次数,它是一个无序的容器类型,以字典键值对形式存储,其中元素作为key,其计数作为value。计数器可以是任意的Integet (包括0或负数)。counter类和其他语言的bags或multisets很相似

from collections import Counter

c = Counter('asdfghjgfdsaSDFGHFDSADFG')
print(c)
Counter({'D': 3, 'F': 3, 'a': 2, 's': 2, 'd': 2, 'f': 2, 'g': 2, 'S': 2, 'G': 2, 'h': 1, 'j': 1, 'H': 1, 'A': 1})

 

  时间模块

  和时间有关系的我们就要使用到时间模块,在使用模块前,应该先导入这个模块

#常用方法
1、time.sleep(secs)
(线程)推迟指定的时间运行,单位为秒
2、time.time()
获取当前时间戳

  表示时间的三种方式

  在Python中,通常有这么三种方式来表示时间:时间戳、元组(struct_time)、格式化时间字符串:

  (1)时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行'type(time.time())',返回的是float类型

  (2)格式化的时间字符串(Format String):'1999-12-06'

%y 两位数的年份表示(00-99%Y 四位数的年份表示(000-9999%m 月份(01-12%d 月内中的一天(0-31%H 24小时制小时数(0-23%I 12小时制小时数(01-12%M 分钟数(00=59%S 秒(00-59%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身

  (3)元组(struct_time):struct _time元组共有9个元素:(年、月、日、时、分、秒、一年中第几周、一年中第几天等)

索引(Index)属性(Attribute)值(Values)
0 tm_year(年) 比如2011
1 tm_mon(月) 1 - 12
2 tm_mday(日) 1 - 31
3 tm_hour(时) 0 - 23
4 tm_min(分) 0 - 59
5 tm_sec(秒) 0 - 60
6 tm_wday(weekday) 0 - 6(0表示周一)
7 tm_yday(一年中的第几天) 1 - 366
8 tm_isdst(是否是夏令时) 默认为0

  首先,我们先导入time模块,来认识一下Python中表示时间的几种格式:

#导入时间模块
import time

#时间戳
t = time.time()
print(t)
#时间字符串
t = time.strftime('%Y-%m-%d %X')
print(t)
t = time.strftime('%Y-%m-%d %H-%M-%S')
print(t)

#时间元组:localtime将一个时间戳转换为当前时区struct_time
time.localtime()
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24,tm_hour=13, tm_min=59, tm_sec=37,tm_wday=0, tm_yday=205, tm_isdst=0)
1525353229.6779027
2018-05-03 21:13:49
2018-05-03 21-13-49

  小结:时间戳是计算机能够识别的时间;时间字符串是人能看懂的时间;元组则是用来操作时间的

  几种时间格式之间的转换

 

#时间戳-->结构化时间
#time.gmtime(时间戳) #UTC时间,与英国伦敦当地时间一致
#time.localtime(时间戳) #当地时间,例如我们现在在北京执行这个方法:与UTC时间相差8小时,UTC时间+8小时 = 北京时间
import  time
t = time.gmtime(1500000000)
print(t)
t = time.localtime(1500000000)
print(t)

#结构化时间 --> 时间戳
time_tuple = time.localtime(1500000000)
t = time.mktime(time_tuple)
print(t)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0)
1500000000.0
import time
#结构化时间 -->  字符串时间
#time.strftime("格式定义","结构化时间") 结构化时间参数若不传,则系统当前时间
t = time.strftime('%Y-%m-%d %X')
print(t)
t = time.strftime('%Y-%m-%d',time.localtime(1500000000))
print(t)

#字符串时间 -->结构化时间
#time.strptime(时间字符串,字符串对应格式)
t = time.strptime('2017-03-16','%Y-%m-%d')
print(t)
t = time.strptime('07/24/2017','%m/%d/%Y')
print(t)
2018-05-04 19:41:02
2017-07-14
time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)

import time

#结构化时间 --> %a %b %d %H:%M:%S %Y串
#time.sactime(结构化时间)如果不传参数,直接返回当前时间的格式化串
t = time.asctime(time.localtime(1500000000))
print(t)
t = time.asctime()
print(t)

#时间戳 --> %a %d %d %H:%M:%s %Y串
t = time.ctime()
print(t)
t = time.ctime()
print(t)
Fri Jul 14 10:40:00 2017
Thu May  3 21:50:07 2018

Thu May  3 21:50:07 2018
Thu May  3 21:50:07 2018
import time
true_time=time.mktime(time.strptime('2018-05-01 08:30:00','%Y-%m-%d %H:%M:%S'))
time_now=time.mktime(time.strptime('2018-05-02 11:00:00','%Y-%m-%d %H:%M:%S'))


dif_time=time_now-true_time
struct_time=time.gmtime(dif_time)

print('过去了%d年%d月%d天%d小时%d分钟%d秒'%(struct_time.tm_year-1970,struct_time.tm_mon-1,
                                       struct_time.tm_mday-1,struct_time.tm_hour,
                                       struct_time.tm_min,struct_time.tm_sec))
过去了0年0月1天2小时30分钟0秒

   random模块

import random
#随机小数
print(random.random()) #大于0 且小于1之间的小数
print(random.uniform(1,3)) #大于1小于3的小数

#随机整数
print(random.randint(1,5)) #大于等于1 且小于等于5之间的整数
print(random.randrange(1,10,2)) #大于等于1 且 小于10 之间的奇数

#随机选择一个返回
print(random.choice([1,'23',[4,5]])) #1或者23或者[4,5]
#随机选择多个返回,返回的个数为函数的第二个参数
print(random.sample([1,'23',[4,5]],2)) #列表元素任意2个组合

#打乱列表顺序
item = [1,3,5,7,9]
random.shuffle(item)
print(item) #打乱次序
random.shuffle(item)
print(item) #打乱次序
0.4659371428625413
2.626202424872581
2
3
1
['23', 1]
[5, 1, 9, 3, 7]
[1, 5, 9, 7, 3]

  练习:生成随机验证码

import random

def v_code():
    code = ''
    for i in range(5):
        # print(code)
        num = random.randint(0,9)
        alf = chr(random.randint(65,90))
        add = random.choice([num,alf])
        # print(num,alf,add)
        code = "".join([code,str(add)])
    return code

print(v_code())
6495P

  os模块

'''
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.curdir  返回当前目录: ('.')
os.pardir  获取当前目录的父目录字符串名:('..')
os.makedirs('dirname1/dirname2')    可生成多层递归目录
os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat('path/filename')  获取文件/目录信息
os.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")  运行shell命令,直接显示
os.popen("bash command).read()  运行shell命令,获取执行结果
os.environ  获取系统环境变量

os.path
os.path.abspath(path) 返回path规范化的绝对路径 os.path.split(path) 将path分割成目录和文件名二元组返回 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。
                        即os.path.split(path)的第二个元素
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path)  如果path是绝对路径,返回True
os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小
'''

  注意:os .stat('path/filename') 获取文件/目录信息 的结构说明

stat 结构:

st_mode: inode 保护模式
st_ino: inode 节点号。
st_dev: inode 驻留的设备。
st_nlink: inode 的链接数。
st_uid: 所有者的用户ID。
st_gid: 所有者的组ID。
st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
st_atime: 上次访问的时间。
st_mtime: 最后一次修改的时间。
st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。
import os
print(os.stat('02.py'))
os.stat_result(st_mode=33206, st_ino=25332747903980054, st_dev=1684577814, st_nlink=1, st_uid=0, st_gid=0, st_size=60, st_atime=1525434755, st_mtime=1525434755, st_ctime=1525434503)

  sys模块 顶部

  sys模块是与Python解释器交互的一个接口

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version        获取Python解释程序的版本信息
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
import sys
try:
    sys.exit(1)
except SystemExit as e:
    print(e)
1

  序列化模块

   什么叫序列化-----将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化

比如,我们在Python代码中计算一个数需要给另一段程序使用,那我们怎么给?
现在我们能想到的方法就是在文件里,然后另一个Python程序再从文件里读出来,
但是我们都知道,对于文件来说是没有字典这个概念的,所以我们只能将数据转换成字典放到文件中,
你一定问,将字典转换成一个字符串很简单,就是str(dic)就可以办到了,为什么我们还要学习序列化模块呢?
没错序列化的过程就是从dic变成str(dic)的过程。现在你可以通过str(dic),将一个名为dic的字典转换成一个字符串,
但是你要怎么把一个字符串转换成字典呢?
聪明的你肯定想到了eval(),如果我们将一个字符串类型的字典str_dic传给eval,就会得到一个返回的字典类型了,
eval()函数十分强大,但是eval()是做什么的呢?官方demo解释为:将字符串str当成有效的表达式来求职并返回计算结果。
BUT!强大的函数是有代价的,安全性是最大的缺点,
想象一下,如果我们从文件中读出的不是一个数据结果,而是一句 "删除文件" 类似的破坏性语句,那么后果不堪设想,
而使用eval()就是要担这个风险。
所以,我们并不推荐eval()方法来进行反序化操作(将str转换成Python中的数据结构)

  序列化的目的

  1、以某种存储形式使自定义对象持久化

  2、将对象从一个地方传递到另一个地方

  3、使程序更具维护性

  json

  Json模块提供了四个功能:dumps、dump、loads、load

import json
dic = {'k1':'v1','k2':'v2','k3':'123'}
#序列化:将一个字典转换成一个字符串
str_dic = json.dumps(dic)
print(type(str_dic),str_dic)
#注意,json转换完的字符串类型的字典中的字符串是由""表示的

#反序列化:将一个字符串格式的字典转换成一个字典
dic2 = json.loads(str(str_dic))
#注意:要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
print(type(dic2),dic2)

list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
#也可以处理嵌套的数据类型
str_dic = json.dumps(list_dic)
print(str_dic)

list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2)
<class 'str'> {"k1": "v1", "k2": "v2", "k3": "123"}
<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': '123'}
[1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
import json
f = open('json_file','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
#dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
json.dump(dic,f)
f.close()

f = open('json_file')
#load方法接收一个句柄,直接把文件中的json字符串转换成数据结果返回
dic2 = json.load(f)
f.close()
print(type(dic2),dic2)
<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

  ensure_ascii关键字

import json

f = open('file','w')
json.dump({'国籍':'中国'},f)
ret = json.dumps({'国籍':'中国'})
f.write(ret + '\n')
json.dump({"国籍":'美国'},f,ensure_ascii=False)
ret = json.dumps({'国籍':'美国'},ensure_ascii=False)
f.write(ret + '\n')
f.close()

  其他参数说明

 

  json格式化输出

import json
data = {'username':['李华','二愣子'],'sex':'male','age':16}
json_dict2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
print(json_dict2)
{
  "age":16,
  "sex":"male",
  "username":[
    "李华",
    "二愣子"
  ]
}

  pickle

  json & pickle 模块

   用于序列化的两个模块

    json:用于字符串 和 Python数据类型之间进行转换

    pickle:用于Python特有的类型 和Python 的数据类型之间进行转换

  pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load(不仅可以序列化字典,列表...可以把Python中任意的数据类型序列化)

import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
#一串二进制的内容
print(str_dic)

dic2 = pickle.loads(str_dic)
#字典
print(dic2)
b'\x80\x03}q\x00(X\x02\x00\x00\x00k1q\x01X\x02\x00\x00\x00v1q\x02X\x02\x00\x00\x00k2q\x03X\x02\x00\x00\x00v2q\x04X\x02\x00\x00\x00k3q\x05X\x02\x00\x00\x00v3q\x06u.'
{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
import time
struct_time = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file','wb')
pickle.dump(struct_time,f)
f.close()

f = open('pickle_file','rb')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)
time.struct_time(tm_year=2001, tm_mon=9, tm_mday=9, tm_hour=9, tm_min=46, tm_sec=40, tm_wday=6, tm_yday=252, tm_isdst=0)
2001

  这时候机智的你又要说了,既然pickle如此强大,为什么还要学习json呢?

  这里我们要说明一下,json是一种所有的语言都可以识别的数据结构

  如果我们将一个字典或者序列化成一个json存在文件里,那么java代码或者js代码也可以拿来用

  但是如果我们pickle进行序列化,其他语言就不能读懂这是什么了...

  所以,如果你序列化的内容是列表或者字典,我们非常推荐你使用json模块

  但如果出于某种原因你不得不序列化其他的数据类型,而未来你还会用Python对这个数据进行反序列化,那么就可以使用pickle

  shelve

  shelve也是Python提供给我们的序列化工具,比pickle用起来更简单一些

  shelve只是提供给我们一个open方法,是用key来访问的,使用起来和字典类似

import shelve
f = shelve.open('shelve_file')
#直接对文件句柄操作,就可以存入数据
f['key'] = {'int':10,'float':9.5,'string':'Sample data'}
f.close()

import shelve
f1 = shelve.open('shelve_file')
#取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
existing = f1['key']
f1.close()
print(existing)
{'int': 10, 'float': 9.5, 'string': 'Sample data'}

  这个模块有个限制,它不支持多个应用同一时间往同一个DB进行写操作,所以当我们知道我们的应用如果只进行读操作,我们可以让shelve通过只读方式打开DB

import shelve
f = shelve.open('shelve_file',flag='r')
existing = f['key']
f.close()
print(existing)
{'int': 10, 'float': 9.5, 'string': 'Sample data'}

  由于shelve在默认情况下是不会记录持久化对象的任何修改的,所以我们在shelve.open()时需要修改默认参数,否则对象的修改不会保存

import shelve
f1 = shelve.open('shelve_file')
print(f1['key'])
f1['key']['new_value'] = 'this was not here before'
f1.close()

f2 = shelve.open('shelev_file',writeback=True)
print(f2['key'])
f2['key']['new_value'] = 'this was not here before'
f2.close()

  writeback方式也有优缺点,优点是减少了我们出错的概率,并且让对象的持久化对用户更加透明,但是这种方式并不是所有的情况下都需要,首先,使用writeback以后,shelf在open()的时候会增加额外的内存消耗,并且当DB在close()的时候会缓存中每一个对象都写入到DB,这也带来额外的等待时间。因为shelve没有办法知道缓存中对象修改了,哪些对象没有修改,因此所有的对象都会被写入

  re模块

  假如现在你用Python写一段代码,类似:

  phone_number = input('please input your phone number:')

  你怎么判断这个Python_number是合法的呢?

  根据手机号码一共11位,并且是只以13、14、15、18开头的数字这些特点,我们用Python写了如下代码:

while True:
    phone_number = input('please input your phone number:')
    if len(phone_number) == 11 and phone_number.startswith('13') or (phone_number.startswith('14') or phone_number.startswith('15') or phone_number.startswith('18')):
        print('是合法的手机号码')
    else:
        print("不是合法的手机号码")
please input your phone number:1345678987
不是合法的手机号码
please input your phone number:124656789
不是合法的手机号码
please input your phone number:13456789098
是合法的手机号码

  这是开始的写法,现在我们改进一下:

import re
phone_number = input('please input your phone number:')
if re.match('^(13|14|15|18)[0-9]{9}$',phone_number):
    print("是合法的手机号码")
else:
    print("不是合法的手机号码")
please input your phone number:13467890987
是合法的手机号码

  re模块 和 正则表达式

不管以后你是不是去做python开发,只要你是一个程序员就应该了解正则表达式的基本使用。如果未来你要在爬虫领域发展,你就更应该好好学习这方面的知识。
但是你要知道,re模块本质上和正则表达式没有一毛钱的关系。re模块和正则表达式的关系 类似于 time模块和时间的关系
你没有学习python之前,也不知道有一个time模块,但是你已经认识时间了 12:30就表示中午十二点半(这个时间可好,一般这会儿就该下课了)。
时间有自己的格式,年月日时分秒,12个月,365天......已经成为了一种规则。你也早就牢记于心了。time模块只不过是python提供给我们的可以方便我们操作时间的一个工具而已
正则表达式和re模块

  正则表达式本身和Python没有什么关系,就是匹配字符串内容的一种规则

  官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑

  正则表达式

   以说规则可能有点晕,现在就让我们先来看一下这些实际的应用。在线测试工具:http://tool.chinaz.com/regex/

  首先要知道,正则只和字符串相关,在提供的测试工具中,你输入的每一个字都一个字符串

  其次,如果在一个位置的一个值,不会出现什么变化,那就不需要规则的

  比如:用'1' 去匹配 '1',或者 '2' 去匹配 '2',直接就可以匹配上的,就可以不需要正则

   我们更多需要考虑的是在 同一位置 出现的字符的范围

在同一位置可能出现的各种字符组成了一个字符串,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2、3、4、5、6、7、8、9这10个数之一
字符组

 

正则
待匹配字符
匹配
结果
说明
[0123456789]
8
True
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
和"待匹配字符"相同都视为可以匹配
[0123456789]
a
False
由于字符组中没有"a"字符,所以不能匹配
 
[0-9]
 
7
True
也可以用-表示范围,[0-9]就和[0123456789]是一个意思
 
[a-z]
 
s
 
True
 
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示
 
[A-Z]
 
B
 
True
 
[A-Z]就表示所有的大写字母
 
[0-9a-fA-F]
 
e
 
True
 
可以匹配数字,大小写形式的a~f,用来验证十六进制字符

字符:

 
元字符
 
匹配内容
匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线
\s 匹配任意的空白符
\d 匹配数字
\n 匹配一个换行符
\t 匹配一个制表符
\b 匹配一个单词的结尾
^ 匹配字符串的开始
$ 匹配字符串的结尾
\W
匹配非字母或数字或下划线
\D
匹配非数字
\S
匹配非空白符
a|b
匹配字符a或字符b
()
匹配括号内的表达式,也表示一个组
[...]
匹配字符组中的字符
[^...]
匹配除了字符组中字符的所有字符

 

量词:

量词
用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

 

. ^ $

正则 待匹配字符 匹配
结果
说明
海. 海燕海娇海东 海燕海娇海东   匹配所有"海."的字符
^海. 海燕海娇海东 海燕 只从开头匹配"海."
  海.$   海燕海娇海东 海东 只匹配结尾的"海.$"

 

* + ? { }

正则 待匹配字符 匹配
结果
说明
李.? 李杰和李莲英和李二棍子

李杰
李莲
李二

 
?表示重复零次或一次,即只匹配"李"后面一个任意字符
 
李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
*表示重复零次或多次,即匹配"李"后面0或多个任意字符
李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符
李.{1,2} 李杰和李莲英和李二棍子

李杰和
李莲英
李二棍

{1,2}匹配1到2次任意字符

 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

正则 待匹配字符 匹配
结果
说明
李.*? 李杰和李莲英和李二棍子

惰性匹配

 

字符集[][^]

正则 待匹配字符 匹配
结果
说明
李[杰莲英二棍子]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

 
表示匹配"李"字后面[杰莲英二棍子]的字符任意次
 
李[^和]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

表示匹配一个不是"和"的字符任意次
[\d] 456bdha3

4
5
6
3

表示匹配任意一个数字,匹配到4个结果
[\d]+ 456bdha3

456
3

表示匹配任意个数字,匹配到2个结果

 

分组 ()与 或 |[^]

 身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部🈶️数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

正则 待匹配字符 匹配
结果
说明
^[1-9]\d{13,16}[0-9x]$ 110101198001017032

110101198001017032

   表示可以匹配一个正确的身份证号
^[1-9]\d{13,16}[0-9x]$ 1101011980010170

1101011980010170

表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字
^[1-9]\d{14}(\d{2}[0-9x])?$ 1101011980010170

False

现在不会匹配错误的身份证号了
()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ 110105199812067023

110105199812067023

表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}

 

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\\'。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了。

正则 待匹配字符 匹配
结果
说明
\d \d  False
因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配
\\d \d  True
转义\之后变成\\,即可匹配
"\\\\d" '\\d'  True
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次
r'\\d' r'\d'  True
在字符串之前加r,让整个字符串不转义

 

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

正则 待匹配字符 匹配
结果
说明
<.*>

<script>...<script>

<script>...<script>
默认为贪婪匹配模式,会匹配尽量长的字符串
<.*?> r'\d'  

<script>
<script>

加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串

   几个常用的非贪婪匹配Pattern

*? 重复任意次,但尽可能少重复
+? 重复1次或者更多次,但尽可能的少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

  .*?的用法

. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式
何在一起就是,取尽量少的任意字符,一般不会这么单独写,他大多用在:   .*?x(就是取前面任意长的字符,直到第一个x出现)

  re模块下的常用方法

import re
#返回所有满足匹配条件的结果,放在列表里
ret = re.findall('a','eva egon yuan')
print(ret)

#函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象
#可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None
ret = re.search('a','eva egon yuan').group()
print(ret)

#match,不过尽在字符串开始处进行匹配
ret = re.match('a','abc').group()
print(ret)
['a', 'a']
a
a
#先按'a'分割得到'' 和 'bcd' ,在对'' 和 'bcd' 分别按'b'分割
ret = re.split('[ab]','abcd')
print(ret)
['', '', 'cd']
#将数字替换成'H',参数1表示只替换1个
ret = re.subn('\d','H','eva3egon4yuan4',1)
print(ret)
('evaHegon4yuan4', 1)
#将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
obj = re.compile('\d{3}')
#正则表达式对象调用search,参数为待匹配的字符串
ret = obj.search('abc123eeee')
print(ret.group())
123
import re
#finditer返回一个存放匹配结果的迭代器
ret = re.finditer('\d','ds3sy4784a')
print(ret)
#查看第一个结果
print(next(ret).group())
#查看第二个结果
print(next(ret).group())
print([i.group() for i in ret])
3
4
['7', '8', '4']
import re
ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
# 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
print(ret)

ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret)  # ['www.oldboy.com']


ret = re.split('\d+','eva3egon4yuan')
print(ret)

ret = re.split("(\d+)","eva3egon4yuan")
print(ret)

#匹配部分加上() 之后所见切出的结果是不同的
#没有()的不保留所匹配的项,但是有()的却能够保留了匹配的项
#这个在某些需要保留匹配部分的使用过程是非常重要的
['oldboy']
['www.oldboy.com']
['eva', 'egon', 'yuan']
['eva', '3', 'egon', '4', 'yuan']

  综合练习与扩展

   1、匹配标签

import re

ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
#还可以在分组中利用?<name>的形式给分组起名字
#获取的匹配结果可以直接group('名字')拿到对应的值
print(ret.group('tag_name'))
print(ret.group())

ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
#如果不给分组起名字,也可以用\序号来找到对应的分组,表示要找的内容和前面的组内容一致
#获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())

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

<h1>hello</h1>

<h1>hello</h1>

  匹配整数

import re

ret = re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))")
print(ret)

ret = re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret)
ret.remove("")
print(ret)
['1', '2', '60', '40', '35', '5', '4', '3']
['1', '-2', '60', '', '5', '-4', '3']
['1', '-2', '60', '5', '-4', '3']

  数字匹配

1、匹配一段文本中的每行的邮箱
http://blog.csdn.net/make164492212/article/details/51656638

2、匹配一段文本中的每行的时间字符串,比如:'1990-07-12'
分别取出1年的12个月(^(0?[1-9]|1[0-2])$)
一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$

3、匹配QQ号(腾讯QQ号从10000开始) [1,9][0,9]{4,}

4、匹配一个浮点数 
^(-?\d+)(\.\d+)?$ 或者 -?\d+\.?|d*

5、匹配汉字 
^[\u4e00-\u9fa5]{0,}$

6、匹配所有的整数
^[\d]+$

  爬虫练习

import requests

import  re
import json

def getPage(url):
    response = requests.get(url)
    # print(response.text)
    return response.text

def parsePage(s):
    com = re.compile(
        '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
        '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S)

    ret = com.finditer(s)
    for i in ret:
        yield {
            "id":i.group("id"),
            "title":i.group("title"),
            "rating_num":i.group("rating_num"),
            "comment_num":i.group("comment_num"),

        }

def main(num):
    url = 'https://movie.douban.com/top250?start=%s&filter='%num
    requests_html = getPage(url)
    ret = parsePage(requests_html)
    print(ret)
    f = open("move_inf7","a",encoding="utf-8")

    for obj in ret:
        print(obj)
        data = json.dumps(obj,ensure_ascii=False)
        f.write(data+"\n")
    f.close()

if __name__ == '__main__':
    count = 0
    for i in range(10):
        main(count)
        count += 25
爬虫
效果

  简化版

import re
import json
from urllib.request import urlopen

def getPage(url):
    reponse = urlopen(url)
    print(reponse.read())
    return reponse.read().decode('utf-8')

def parsePage(s):
    com = re.compile(
        '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
        '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S)

    ret = com.finditer(s)
    for i in ret:
        yield {
            "id": i.group("id"),
            "title": i.group("title"),
            "rating_num": i.group("rating_num"),
            "comment_num": i.group("comment_num"),
        }

def main(num):
    url = 'https://movie.douban.com/top250?start=%s&filter=' % num
    response_html = getPage(url)
    ret = parsePage(response_html)
    print(ret)
    f = open("move_info7_1", "a", encoding="utf8")

    for obj in ret:
        print(obj)
        data = str(obj)
        f.write(data + "\n")

count = 0
for i in range(10):
    main(count)
    count += 25
View Code
<generator object parsePage at 0x0000000002D42AF0>
{'id': '1', 'title': '肖申克的救赎', 'rating_num': '9.6', 'comment_num': '1025498人'}
{'id': '2', 'title': '霸王别姬', 'rating_num': '9.5', 'comment_num': '746131人'}
{'id': '3', 'title': '这个杀手不太冷', 'rating_num': '9.4', 'comment_num': '960814人'}
{'id': '4', 'title': '阿甘正传', 'rating_num': '9.4', 'comment_num': '817514人'}
{'id': '5', 'title': '美丽人生', 'rating_num': '9.5', 'comment_num': '477440人'}
{'id': '6', 'title': '千与千寻', 'rating_num': '9.3', 'comment_num': '765366人'}
...
效果
flags有很多可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释

  作业

实现能计算类似 
1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式的计算器程序

  模块二

  hsahlib模块

   Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等

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

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

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

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

import hashlib

md5 = hashlib.md5()
# md5.update('how to use md5 in python hashlib?') python2x
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
d26a53750bc40b38b65a520292f69306

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

import hashlib

md5 = hashlib.md5()
md5.update("how to use md5 in ".encode("utf-8"))
md5.update("python hashlib?".encode("utf-8"))
print(md5.hexdigest())
d26a53750bc40b38b65a520292f69306

  MD5是最常见的摘要算法,速度很快,生成结果是固定的128bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:

import hashlib

sha1 = hashlib.sha1()
sha1.update('how to use sha1 in'.encode("utf-8"))
sha1.update('python hashlib?'.encode("utf-8"))

print(sha1.hexdigest())
86e1eae2a08c152d39b55baed085c71a0cc9d10b

  SHA1的结果是160bit字节,通常用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长。

  摘要算法应用

  任何允许用户登录的网站都会存储用户登录的用户名和口令,如何存储用户名和口令呢?方法是存到数据库表中:

name    | password
--------+----------
michael | 123456
bob     | abc999
alice   | alice2008

  如果以明文保存用户口令,如果数据库数据泄露,所有用户的口令就落入了黑客的手里。此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:

username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

  考虑这么情况,很多用户喜欢123456,88888,password这些简单的口令,于是,黑客可以事先计算出这些常用口令的MD5的值,得到一个反推表:

import hashlib

md5 = hashlib.md5()
md5.update('123456'.encode("utf-8"))
print("123456:",md5.hexdigest())
md5.update('888888'.encode("utf-8"))
print("888888",md5.hexdigest())
md5.update('password'.encode("utf-8"))
print("password:",md5.hexdigest())
123456: e10adc3949ba59abbe56e057f20f883e
888888 add8a8ecb05f54a556a3f83ff2f87dab
password: a3f53e4543fd64d7c2e8a240df2f1b26

   这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号

  对于用户来讲,当然不要使用过于简单的口令。但是,我们能否在程序设计上对简单口令加强保护?

  由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,所称"加盐":

md5 = hashlib.md5("salt".encode("utf-8"))

  经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令

  但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

  如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5

  摘要算法在很多地方都有广泛的应用,要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令

   configparser模块

  该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键 = 值)

  创建文件

  一个好多软件的常见文档格式:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
  
[bitbucket.org]
User = hg
  
[topsecret.server.com]
Port = 50022
ForwardX11 = no

  如果想用Python生成一个这也的文档该怎么做呢?

import configparser

config = configparser.ConfigParser()
config['DEFAULT'] = {'ServerAliveInterval':'45',
                     'Compression':'yes',
                     'CompressionLevel':'9',
                     'ForwardX11':'yes'
}
config['bitbucket.org'] = {'User':'hg'}
config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'}

with open('example.ini', 'w') as configfile:
    config.write(configfile)
example.ini
[DEFAULT]
serveraliveinterval = 45
compression = yes
compressionlevel = 9
forwardx11 = yes

[bitbucket.org]
user = hg

[topsecret.server.com]
host port = 50022
forwardx11 = no

  查找文件

import configparser

config = configparser.ConfigParser()

#---------------------------查找文件内容,基于字典的形式

print(config.sections())        #  []

config.read('example.ini')

print(config.sections())        #   ['bitbucket.org', 'topsecret.server.com']

print('bytebong.com' in config) # False
print('bitbucket.org' in config) # True


print(config['bitbucket.org']["user"])  # hg

print(config['DEFAULT']['Compression']) #yes

print(config['topsecret.server.com']['ForwardX11'])  #no


print(config['bitbucket.org'])          #<Section: bitbucket.org>

for key in config['bitbucket.org']:     # 注意,有default会默认default的键
    print(key)

print(config.options('bitbucket.org'))  # 同for循环,找到'bitbucket.org'下所有键

print(config.items('bitbucket.org'))    #找到'bitbucket.org'下所有键值对

print(config.get('bitbucket.org','compression')) # yes       get方法Section下的key对应的value

   增删改操作

import configparser

config = configparser.ConfigParser()
config.read('example.ini')
config.add_section('yuan')

config.remove_section('bitbucket.org')
config.remove_option('topsecret.server.com',"forwardx11")

config.set('topsecret.server.com','k1','11111')
config.set('yuan','k2','22222')

config.write(open('new2.ini','w'))

  logging模块

  函数式简单配置

import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message

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

  灵活配置日志级别,日志格式,输出位置:

 

import logging  
logging.basicConfig(level=logging.DEBUG,  
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  
                    datefmt='%a, %d %b %Y %H:%M:%S',  
                    filename='/tmp/test.log',  
                    filemode='w')  
  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')

   配置参数:

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用户输出的消息

  logger对象配置

import logging

logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8') 

# 再创建一个handler,用于输出到控制台 
ch = logging.StreamHandler() 
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)

fh.setFormatter(formatter) 
ch.setFormatter(formatter) 
logger.addHandler(fh) #logger对象可以添加多个fh和ch对象 
logger.addHandler(ch) 

logger.debug('logger debug message') 
logger.info('logger info message') 
logger.warning('logger warning message') 
logger.error('logger error message') 
logger.critical('logger critical message')
logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过

fh.setLevel(logging.Debug)单对文件流设置某个级别。
import logging
# 实例化对象
logger = logging.getLogger()
#输出的格式
fmt = logging.Formatter('%(asctime)s -%(filename)s- %(name)s - %(levelname)s - %(message)s')#格式化
#logger相当于人---》练会了吸星大法
#创建了一个文件操作符
fh = logging.FileHandler('a.txt',encoding='utf-8')
#给文件操作符设置一个格式
fh.setFormatter(fmt)
#调整日志信息显示情况:显示出debug以及比debug还紧急的。
fh.setLevel(logging.INFO)
#给logger对象添加fh武功
logger.addHandler(fh)

#在屏幕上输入
sh = logging.StreamHandler()#创建了一个屏幕操作符
sh.setFormatter(fmt)
#调整日志信息显示情况:显示出info以及比info还紧急的。
sh.setLevel(logging.INFO)
logger.addHandler(sh)

logger.setLevel(logging.WARNING) #单独设置文件日志等级,必须要这一句,且单独设置的日志层级小于WARNING的都不会被记录或输出
logger.debug('debug message')  # 调试的时候
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')  # 批判的
单独设置文件或控制台错误输出等级
2018-05-29 21:29:56,352 -12.py- root - WARNING - warning message
2018-05-29 21:29:56,352 -12.py- root - ERROR - error message
2018-05-29 21:29:56,352 -12.py- root - CRITICAL - critical message
执行结果

 

posted @ 2018-05-17 20:43  你知道  阅读(276)  评论(0编辑  收藏  举报