模块

# 导读目录如下 
1 什么是模块?
 1.1 什么是模块?
 1.2 为什么要使用模块
2 模块的导入和使用
 2.1 常用语法
 2.2 __name__
 2.3 模块的搜索路径
3 常用内置模块
 3.1 collections模块
   3.1.1 namedtuple
   3.1.2 deque
   3.1.3 OrderDict
   3.1.4 defaultdict
   3.1.5 Counter
 3.2 时间模块
  3.2.1 时间戳
   3.2.2 时间对象
   3.2.3 格式化的时间字符串
   3.2.4 计算时间差的例子
 3.3 random模块
 3.4 os模块
 3.5 sys模块
 3.6 序列化模块
       3.6.1  json
   3.6.2  pickle
   3.7 re模块
 3.8 正则表达式
   3.9.1  .^$
   3.9.2  * + ? {}
   3.9.3 字符集 [ ]  [^] 
   3.9.4 分组 ()与 或 |[^]
   3.9.5 转义符 \
   3.9.6 贪婪匹配
   3.9.7 re模块下的常用方法
  3.9 综合练习与扩展
   3.9.1 匹配标签
   3.9.2 匹配整数
   3.9.3 数字匹配
   3.9.4 爬虫练习
4 常用内置模块2
 4.1 hashlib模块
   4.1.1 算法介绍
   4.1.2 摘要算法应用
 4.2 congfigparse模块
   4.2.1 创建文件
   4.2.2 查找文件
   4.2.3 增删改操作
 4.3 random模块
   4.3.1 函数式简单配置
   4.3.2 logger对象配置   
导读目录

1 什么是模块?

 1.1 什么是模块?

  import加载的模块分为四个通用类别:

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

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

  3 包好一组模块的包

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

 1.2 为什么要使用模块

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

  随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。

  这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。

2 模块的导入和使用

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

 2.1 常用语法

# 基础导入方法
import a
from xx import b

# 起别名 【重命名后,只有新名字可以用 
from xx import heiheihei as yue

 2.2 __name__

  当做脚本运行:
    __name__ 等于'__main__'

  当做模块导入:
    __name__= 模块名

def say_hai(name):
    print('Hi, {}'.format(name))

# 下面的代码在当前文件以模块的方法被导入时是不会执行的
if __name__ == "__main__":
    print(__name__)
    input_name = input('your name:').strip()
    say_hai(input_name)

 2.3 模块的搜索路径

  内存中已加载的模块 -> 内置模块 -> 扩展模块 --> 自定义模块

#找模块:
    # 先从sys.modules里查看是否已经被导入
        # 如果没有被导入,就依据sys.path路径寻找模块
        #找到了就导入
        import sys
        print(sys.modules.keys())
    #创建这个模块的命名空间
    #把文件中的名字都放到命名空间(各自的文件内部命名空间)里

3 常用内置模块

3.1 collections模块

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

   3.1.1 namedtuple:生成可以使用名字来访问元素内容的tuple

# 语法
#namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])

# namedtuple表示一个点的二维坐标就 >>> from collections import namedtuple >>> Point = namedtuple('Point', ['x', 'y']) >>> p = Point(1, 2) >>> p.x 1 >>> p.y 2

   3.1.2 deque:高效实现插入和删除操作的双向列表,适合用于队列和栈

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

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

>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])

   3.1.3 OrderDict:有序字典

  *Python3.6中,Dict已经可以记住key加入的顺序了。

  OrderDict 会按照插入的顺序排列,不是Key本身排序

>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> od.keys() # 按照插入的Key的顺序返回
['z', 'y', 'x']
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])

   3.1.4 defaultdict:带有默认值的字典

   使用 dict 时,如果引用的Key不存在,就会抛出 KeyError 。如果希望key不存在时,返回一个默认值,就可以用 defaultdict 

>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1']    # key1存在
'abc'
>>> dd['key2']    # key2不存在,返回默认值
'N/A'
#小例子:有如下值集合 [ 11 , 22 , 33 , 44 , 55 , 66 , 77 , 88 , 99 , 90. ..],将所有大于 66 的值保存至字典的第一个key中,将小于 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:
        if my_dict.has_key('k1'):
            my_dict['k1'].append(value)
        else:
            my_dict['k1'] = [value]
    else:
        if my_dict.has_key('k2'):
            my_dict['k2'].append(value)
        else:
            my_dict['k2'] = [value]

# defaultdict字典解决办法
from collections import defaultdict
values = [11, 22, 33,44,55,66,77,88,99,90]
my_dict = defaultdict(list)       # 默认value为“list”,可能为set或dict等
for value in  values:
    if value>66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)
小例子

  3.1.5 Counter:计数器,主要用来计数

  Counter类的目的是用来跟踪值出现的次数。

  它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。

>>> from collections import Counter
>>> c = Counter('abcdeabcdabcaba')
>>> c
Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

3.2 时间模块

  表示时间的三种方式:【时间戳】是计算机能够识别的时间;【时间字符串】是人能够看懂的时间;【struct_time】则是用来操作时间的。

  3.2.1 时间戳

  时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。

  我们运行“type(time.time())”,返回的是float类型。

  一般可以把时间戳理解为计算机读懂的时间。

# 导入时间模块
import time
# 时间戳
print(time.time())  # 1500875844.800804

   3.2.2 时间对象【结构化时间 —— 中转枢纽】

  一个内置的struct_time对象:是我们能够理解和操作的时间。

  struct_time元组共有9个元素共下图九个元素:

  

import time
# struct_time:localtime方法将一个时间戳转换为当前时区的struct_time格式
struct_time = time.localtime()
print(struct_time.tm_year,struct_time.tm_mon,struct_time.tm_mday)

运行结果:
2019 4 9

  3.2.3 格式化的时间字符串

  格式化的时间字符串(Format String)首先,它是一个字符串。

  这个字符串是按照固定的格式的,这个格式按如下格式:

%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 当前时区的名称
%% %号本身
import time
print(time.strftime("%Y-%m-%d %a %H:%M:%S"))  
print(time.strftime("%y/%m/%d %A %H:%M:%S"))  

运行结果:
2019-04-09 Tue 20:09:22
19/04/09 Tuesday 20:09:22

   3.2.4 几种时间格式之间的转换

  

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

t = time.time()
print("时间戳时间:",t)
print("结构化时间:",time.localtime(t))

#运行结果
时间戳时间: 1554812344.4789186
结构化时间: time.struct_time(tm_year=2019, tm_mon=4, tm_mday=9, tm_hour=20, tm_min=19, tm_sec=4, tm_wday=1, tm_yday=99, tm_isdst=0)

---------------------------------------------------------------------------------------------------------------------------------------

#结构化时间-->时间戳 
#time.mktime(结构化时间)
>>>time_tuple = time.localtime(1500000000)
>>>time.mktime(time_tuple)
1500000000.0
结构化时间 & 时间戳
#结构化时间-->字符串时间
#time.strftime("格式定义","结构化时间")  结构化时间参数若不传,则显示当前时间

import time
print(time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(3000000000)))
# 运行结果
2065/01/24 13:20:00

------------------------------------------------------------------------------------------------------------------------------------

#字符串时间-->结构化时间
#time.strptime(时间字符串,字符串对应格式)

import time
print(time.strptime('2017-12/31','%Y-%m/%d'))
#运行结果
time.struct_time(tm_year=2017, tm_mon=12, tm_mday=31, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=365, tm_isdst=-1)
结构化时间 & 字符串时间

 

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

>>>time.asctime(time.localtime(1500000000))
'Fri Jul 14 10:40:00 2017'
>>>time.asctime()
'Mon Jul 24 15:18:33 2017'

-----------------------------------------------------------------------------------------

#时间戳 --> %a %b %d %H:%M:%S %Y串
#time.ctime(时间戳)      如果不传参数,直接返回当前时间的格式化串
>>>time.ctime()
'Mon Jul 24 15:19:07 2017'
>>>time.ctime(1500000000)
'Fri Jul 14 10:40:00 2017' 

  3.2.5 计算时间差小例子

import time
true_time = time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
now_time = time.mktime(time.strptime('2018-09-12 11:10:00','%Y-%m-%d %H:%M:%S'))
dif_time = now_time - 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,

运行结果:
过去了1年0个月1天2小时40分钟0秒

3.3 random模块

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

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

#随机选择一个返回
random.choice([1,'23','ab',[4,5]])
#随机选择多个返回,函数的第二个参数:要返回的个数
random.sample([1,'23','ab',[4,5]],3)

#打乱列表顺序
item = [1,3,5,7,9]
random.shuffle(item)
# print(item)

练习:随机生成6位验证码例子

list1 = []
for i in range(6):
    statu = random.randint(1,3)
    if statu == 1: #随机大写字母
        a = random.randint(65,90)
        a_chr = chr(a)
        list1.append(a_chr)
    elif statu == 2: #随机小写字母
        b = random.randint(97,122)
        b_chr = chr(b)
        list1.append(b_chr)
    elif statu == 3: #0-9的随机数
        r = random.randint(0,9)
        list1.append(str(r))
ver_code = "".join(list1)  # 此处指定的字符位“”
print(ver_code)

运行结果:

  361T5M

3.4 os模块

os模块:是与操作系统交互的一个接口

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.system("bash command")  运行shell命令,直接显示
os.popen("bash command).read()  运行shell命令,获取执行结果
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd

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(filename)  返回文件或者目录的最后访问时间
os.path.getmtime(filename)  返回文件或者目录的最后修改时间
os.path.getsize(filename) 返回filename的大小

3.5 sys模块

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

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

3.6 序列化模块

序列化(Serialization):是将对象的状态信息转换为可以存储或传输的形式的过程。

序列化的目的:

1、以某种存储形式使自定义 对象持久化 
2、将对象从一个地方传递到另一个地方。
# 序列化 —— 都是转向一个字符串数据类型:【从数据类型(字典、列表等对象) --> 字符串的过程】
# 反系列化:从字符串 --> 数据类型
# json   *****
    # 通用的序列化格式,只有很少的一部分数据类型能够通过json转换成字符串
# pickle ****
    # 所有的python中的数据类型都可以转化成字符串形式
    # pickel序列化的内容只有python能理解
    # 且反序列化依赖python代码
# shelve ***
    # 序列化句柄
    # 使用句柄直接操作,非常方便

  3.6.1  json

  JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

  JSON支持转换的是数据类型:数字 字符串 列表 字典 元组(列表-列表) 【set除外】

  Python中的json模块提供了四个功能:dumps、dump、loads、load(dump和load只能一次性地写入,一次性地读出)

#注意,json序列化dumps完的字符串类型的字典中的字符串是由""表示的
#注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示

list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic)          #可以处理嵌套的数据类型 
print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
dumps和loads
import json
#dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
#load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回

f = open('json_file','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
json.dump(dic,f)  
f.close()

f = open('json_file')
dic2 = json.load(f)  
f.close()
print(type(dic2),dic2)
dump和load
import json
data = {'username':['李华','二愣子'],'sex':'male','age':16}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
print(json_dic2)

运行结果:
{
  "age":16,
  "sex":"male",
  "username":[
    "李华",
    "二愣子"
  ]
}
json格式化输出

  JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:

  3.6.2  pickle

  首先我们来看一下pickle和json的一个区别:

  • 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)    #字典

-------------------------------------------------------------------------------------------

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)

# 运行结果:
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'}

----------------------------------------------------------------------------------------

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

3.7 re模块

牛刀小试:

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

那怎么判断这个phone_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.isdigit()\
            and (phone_number.startswith('13') \
            or phone_number.startswith('14') \
            or phone_number.startswith('15') \
            or phone_number.startswith('18')):
        print('是合法的手机号码')
    else:
        print('不是合法的手机号码')

用re模块+正则表达式实现:

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('不是合法的手机号码')

3.8 正则表达式

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

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

  在线测试工具   http://tool.chinaz.com/regex/

  在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。

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

  字符匹配表:

  量词用法表:

  3.8.1  . ^ $

 

   3.8.2  *  +  ?  { }

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

   3.8.3 字符集 [ ]  [^] 

   3.8.4 分组 ()与 或 |[^]

 

   3.8.5 转义符 \

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

  如果匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了。这个时候我们就用到了r'\n'这个概念,此时的正则是r'\\n'就可以了。

  

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

   

  几个常用的非贪婪匹配Pattern:

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
# . *  ?的用法 
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
# 合在一起就是:取尽量少的任意字符,一般不会这么单独写,他大多用在:.*?x
就是取前面任意长度的字符,直到一个x出现

3.8.7 re模块下的常用方法

import re
# findall:返回所有满足匹配条件的结果,放在列表里
    # ret = re.findall('[a-z]+','evan egon yuan')
    # print(ret)      # ['evan', 'egon', 'yuan']
# findall 注意 优先级
    # ret = re.findall('www.(baidu|oldboy).com','www.baidu.com')
    # print(ret)   #['baidu']   优先匹配分组里的

    # ret = re.findall('www.(?:baidu|oldboy).com','www.baidu.com')  #? 取消分组优先
    # print(ret)   # ['www.baidu.com']


# search:从前往后,找到一个就返回;返回的变量需要调用group才能拿到结果
        # 如果没有找到,那么返回None,调用group会出错
    # ret = re.search('a','evan egon yuan')
    # if ret:
    #     print(ret.group())    # a
    #     print(ret.group(1))   #取分组中的内容
    #     print(ret.group(2))

# match:从 头 开始匹配,如果正则规则从头开始可以匹配上,就返回一个变量(对象)
        # 匹配的内容也需要调用group才能拿到结果
        # 如果没有找到,那么返回None,调用group会出错
    # ret = re.match('e','evan egon yuan')
    # if ret:
    #     print(ret.group())    # e
# 根据正则关系进行split
    # ret = re.split('[ab]','abcd')  #先按'a'分割得到''和‘bcd’,再按'b'对''和‘bcd’进行分割得到'',‘’和‘cd’
    # print(ret)   #['', '', 'cd']
    
# 注意split 分组优先:在匹配规则部分加上()之后所切出的结果是不同的
# ret = re.split('\d+','eva3egon4yuan')    #没有()的没有保留所匹配的项
# print(ret)  #['eva', 'egon', 'yuan']

# ret = re.split('(\d+)','eva3egon4yuan')    #有()的保留所匹配的项
# print(ret)  #['eva', '3', 'egon', '4', 'yuan']
# 替换sub
    # ret = re.sub('\d','H','eva3egon4yuan4',2)  #将数字替换成'H',参数2表示替换的数量
    # print(ret)  #evaHegonHyuan4
# 替换subn
    # ret = re.subn('\d','H','eva3egon4yuan4')  #将数字替换成'H',返回元组(替换的结果,替换的次数)
    # print(ret)  # ('evaHegonHyuanH', 3)
# compile(编译规则):正则表达式很长且要多次使用时用到 --> 一次编译多次使用
    # obj = re.compile('\d{3}')   #将正则表达式编译成为一个正则表达式对象,规则要匹配的是3个数字   compile(规则)
    # ret = obj.search('abc23456eeeee')   #正则表达式对象多用search,参数为待匹配的字符串
    # if ret:
    #     print(ret.group())  # 234
# finditer:返回一个存放结果的迭代器,为了节省内存空间
    # ret = re.finditer('\d','sdf3468awujd124')
    # print(ret)   # <callable_iterator object at 0x0000023E1FBA3630>
    # print(next(ret).group())   #3  查看第一个结果
    # print(next(ret).group())   #4  查看第二个结果
    # print([i.group() for i in ret])    # ['6', '8', '1', '2', '4']  查看剩余的结果

3.9 综合练习与扩展

  3.9.1 匹配标签

import re

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

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

   3.9.2 匹配整数

import re

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

   3.9.3 数字匹配

1、 匹配一段文本中的每行的邮箱
    ^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$ 参考: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、 匹配出所有整数

   3.9.4 小练习

import re
import json
from urllib.request import urlopen

def getPage(url):
    response = urlopen(url)
    return response.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", "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
爬豆瓣电影
import re
    # ret = re.findall(r"\d+\.\d+|\d+","1-2*(60+(-40.35/5)-(-4*3))")  # x|y  先匹配前边的
    # print(ret)   #['1', '2', '60', '40.35', '5', '4', '3']
ret = re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))")  # x|y  先匹配前边的
print(ret)   #['1', '2', '60', '', '5', '4', '3']
ret.remove('')
print(ret)   #['1', '2', '60', '5', '4', '3']
匹配整数,去除小数

4 常用内置模块2

 4.1 hashlib模块

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

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

  【原理

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

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

# 不管算法多么不同,摘要的功能始终不变
    # 对于相同的字符串使用同一个算法进行摘要,得到的值是不变的
    # 使用不同算法对相同的字符串进行摘要,得到的值应该不同
    # 不管使用什么算法,hashlib的方式永远不变
    #sha算法随着算法复杂度的增加,摘要的时间和空间成本都会提高

# 应用【防撞库】
    # 密码的密文存储
    # 文件的一致性检验【这里不需要加盐】
        # 在下载的时候。检查我们下载的问价你和远程服务器上的文件是否一致
        # 两台机器上的两个文件,想检查这两个文件是否相等

       4.1.1 算法介绍

# MD5算法

import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?')
print md5.hexdigest()

计算结果如下:
d26a53750bc40b38b65a520292f69306

----------------------------------------------------------------------------------------

# 给加密算法加盐
import hashlib
md5 = hashlib.md5(bytes('salt',encoding='utf-8'))    # 加盐
md5.update(b'123456')
print(md5.hexdigest())     # f51703256a38e6bab3d9410a070c32ea
 

# 动态加盐
# 用户名,密码(使用用户名的一部分作为盐)
import hashlib
md5 = hashlib.md5(bytes('',encoding='utf-8')+bytes('用户名的一部分',encoding='utf-8'))    # 加盐
# 可以一点一点读出来,分次进行update,最后结果是一样的
# 如admin123456,一次性update完后是821611d6cebee2cb751d05b12c0a76cc
md5.update(b'admin')
md5.update(b'123456')
print(md5.hexdigest())  # 分开后执行结果也是一样的,821611d6cebee2cb751d05b12c0a76cc

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

import hashlib

sha1 = hashlib.sha1()
sha1.update(b'how to use sha1 in ')
sha1.update(b'python hashlib?')
print(sha1.hexdigest())    # 2c76b57293ce30acef38d98f6046927161b46a44

   4.1.2 摘要算法应用

# 用户注册
    # import hashlib
    # reg_usr = input('input username:')
    # reg_pwd = input('input password:')
    # with open('userinfo','a') as f:
    #         md5 = hashlib.md5()
    #         md5.update(bytes(reg_pwd,encoding = 'utf-8'))
    #         md5_reg_pwd = md5.hexdigest()
    #         f.writelines('{}|{}'.format(reg_usr,md5_reg_pwd))
    #         print()
    #         print('恭喜您注册成功')
# 用户登录验证
    # usr = input('username:')
    # pwd = input('password:')
    # with open('userinfo') as f:
    #     for line in f:           # 读每一行
    #         user,passwd = line.split('|')
    #         md5 = hashlib.md5()
    #         md5.update(bytes(pwd,encoding = 'utf-8'))
    #         md5_pwd = md5.hexdigest()
    #         if usr == user and  md5_pwd == passwd:
    #             print('登陆成功')
用户注册 登录验证

 4.2 congfigparse模块

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

   4.2.1 创建文件

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)

   4.2.2 查找文件

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

   4.2.3 增删改操作

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"))

 4.3 logging模块

   4.3.1 函数式简单配置

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

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

  4.3.2 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)单独对某个日志handler设置级别。

 

posted @ 2019-04-09 17:17  timetellu  阅读(227)  评论(0编辑  收藏  举报