1 模块 re模块经典案例-发红包
1.1 什么是模块?
简言之,模块就是一组功能的集合
大家之前在编写简单的功能实现时,思路是先将程序中都需要有哪些功能定义出来,然后在需要用的地方调用即可。比起之前通篇垒代码的方式,将重复要用的功能定义成函数会让程序更加简洁,这不能不算做是一种进步,但问题是,随着程序功能越来越多,再将所有的代码都放到一起,程序的组织结构仍然会不清晰,不方便管理,以后我们写程序,都是分文件的,如果多个文件中都需要用到同一段功能,难道我们要重复编写该功能吗?很明显不能。这就需要我们找到一种解决方案,能够将程序中经常要用的功能集合到一起,然后在想用的地方随时导入使用,这几乎就是模块的全部含义了
1.2 模块类型
内置模块 |
不需要自己安装的,python自己自带的 |
第三方模块 |
需要自己安装的 |
自定义模块 |
自己写的 |
1 使用python编写的.py文件
2 已被编译为共享库或DLL的C或C++扩展
3 把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
4 使用C编写并链接到python解释器的内置模块
1.3 如何使用模块
1.3.1 Import 的使用
import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句
1.3.2 在第一次导入模块时会做三件事,重复导入会直接引用内存中已经加载好的结果
为源文件(XX模块)创建新的名称空间
在新创建的命名空间中执行模块中包含的代码
创建名字XX来引用该命名空间
1.3.3 被导入模块有独立的名称空间
每个模块都是一个独立的名称空间,定义在在这个模块中的函数,把这个模块的名称空间当作全局名称空间,这与使用者自己的全局变量不会有冲突。
1.3.4 为模块名起别名
为已经导入的模块起别名的方式对编写可扩展的代码很有用
1 import spam as sm
2 print(sm.money)
1.3.5 在一行导入多个模块
1 import sys,os,re
1.3.6 from ...... import..
from spam import read1,read2
(1)from xxx模块 import 方法1(函数),方法2
(2)from xxx模块 import * (把xx中所有的不是以下划线(_)开头的名字都导入到当前位置)
--->可以使用__all__来控制*(用来发布新版本),在spam.py中新增一行
__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
1.3.6.1 from ...... import..与import的不同
from … import…就是把模块中的名字直接导入到当前的名称中,所以在当前名称空间中,直接使用名字就可以了,无需加模块前缀。
好处:使用起来方便了
坏处:容易与当前执行文件中的名字冲突
1.3.6.2 也支持as
1 from my_module import read1 as read
也支持导入多行
1 from my_module import (read1,
2 read2,
3 money)
1.3.7 模块的循环引用问题
思考:假如有两个模块a,b。我可不可以在a模块中import b ,再在b模块中import a?
模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码
在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享的数据,可以将共享的数据集中存放到某一个地方
1.4 模块的加载和修改
考虑到性能的原因,每个模块只被导入一次,放入字典sys.modules中,如果你改变了模块的内容,你必须重启程序,python不支持重新加载或卸载之前导入的模块,
有的同学可能会想到直接从sys.modules中删除一个模块不就可以卸载了吗,注意了,你删了sys.modules中的模块对象仍然可能被其他程序的组件所引用,因而不会被清除。
特别的对于我们引用了这个模块中的一个类,用这个类产生了很多对象,因而这些对象都有关于这个模块的引用。
2 模块re
使用python的re模块,尽管不能满足所有复杂的匹配情况,但足够在绝大多数情况下能够有效地实现对复杂字符串的分析并提取出相关信息。python 会将正则表达式转化为字节码,利用 C 语言的匹配引擎进行深度优先的匹配
2.1 查找
2.1.1 findall()
2.1.1.1 语法:
re.findall(pattern, string[, flags])
以list的形式返回string中所有与pattern匹配的不重叠的字符串。String从左向右扫描,匹配的返回结果也是以这个顺序。
--------》re.findall(正则表达式,字符串,flags)
2.1.1.2 实例:
1 ret=re.findall('\d+','sjkhk172按实际花费928') 2 print(ret)
--》结果:['172', '928']
2.1.1.3 作用:
匹配所有,每一项都是列表中的一个元素
2.1.2 Search()
2.1.2.1 语法:
re.search(string[, pos[, endpos]])
扫描字符串string,查找与正则表达式匹配的位置。如果找到一个匹配就返回一个MatchObject对象(并不会匹配所有的)。如果没有找到那么返回None。
第二个参数表示从字符串的那个位置开始,默认是0
第三个参数endpos限定字符串最远被查找到哪里。默认值就是字符串的长度。.
--------》re.findall(正则表达式,字符串,flags)
2.1.2.2 实例:
1 ret=re.search('\d+','sjkhk172按实际花费928') 2 print(ret) #<_sre.SRE_Match object; span=(5, 8), match='172'> 3 print(ret.group()) #172
---->通过group()获取结果
2.1.2.3 正确的写法
1 ret=re.search('\d+','sjkhk172按实际花费928') 2 print(ret) #<_sre.SRE_Match object; span=(5, 8), match='172'> 3 if ret: 4 print(ret.group()) #172
2.1.2.4 作用:
只匹配从左到右的第一个,得到的不是直接的结果,而是一个变量,通过这个变量的group方法来获取结果
如果没有匹配到,会返回None,使用group会报错
2.1.3 match()
2.1.3.1 语法:
re.match(pattern, string, flags=0)
2.1.3.2 实例:
1 ret=re.match('\d+','sjkhk172按实际花费928') 2 print(ret) #None 3 ret=re.search('^\d+','sjkhk172按实际花费928') 4 print(ret) #None
----->功能相同
2.1.3.3 作用:
match 从头开始匹配,相当于search中的正则表达式加上一个^
2.2 字符串的处理
2.2.1 切割split()
2.2.1.1 语法:
split(pattern, string, maxsplit=0, flags=0)
(正则表达式,字符串)
2.2.1.2 案例:
1 s='alex83taibei40egon25' 2 ret = re.split('\d+',s) 3 print(ret) #['alex', 'taibei', 'egon', ''] 4 ret = re.split('(\d+)',s) 5 print(ret) #['alex', '83', 'taibei', '40', 'egon', '25', '']
------》注意:加括号数字可以保留
2.2.2 替换
2.2.2.1 Sub()
2.2.2.1.1 语法:
sub(pattern, repl, string, count=0, flags=0)
(正则表达式,repl,旧字符串,替换次数)
2.2.2.1.2 案例:
1 ret=re.sub('\d+','H','alex83taibei40egon25') 2 print(ret) #alexHtaibeiHegonH 3 ret=re.sub('\d+','H','alex83taibei40egon25',1) 4 print(ret) # alexHtaibei40egon25
2.2.2.2 Subn()
返回一个元组,第二个元素是替换的次数
2.2.2.2.1 语法:
subn(pattern, repl, string, count=0, flags=0)
2.2.2.2.2 案例:
1 ret=re.subn('\d+','H','alex83taibei40egon25') 2 print(ret) #('alexHtaibeiHegonH', 3) 3 4
2.3 优化处理
2.3.1 节省时间compile()
2.3.1.1 语法:
compile(pattern, flags=0)
2.3.1.2 案例:
1 ret = re.compile('\d+') #编译 2 print(ret) #re.compile('\\d+') 3 res = ret.findall('alex83taibei40egon25') 4 print(res) #['83', '40', '25'] 5 res= ret.search('alex83taibei40egon25') 6 if res: 7 print(res.group()) #83
2.3.1.3 作用:
节省使用正则表达式解决问题的时间
-----》编译:把正则表达式编译成字节码,在多次使用的过程中不会多次编译
2.3.2 节省空间/内存finditer()
2.3.2.1 语法:
finditer(pattern, string, flags=0)
(正则表达式,字符串)
finditer 返回一个迭代器,所有匹配到的内容需要迭代取到,迭代取到的每一个结果都需要group取具体值
# -- 节省内存空间
2.3.2.2 案例:
ret = re.finditer('\d+','alex83taibei40egon25') print(ret) #<callable_iterator object at 0x000001C80A1DDC88> for i in ret: print(i) #<_sre.SRE_Match object; span=(4, 6), match='83'> print(i.group())
2.3.2.3 作用:
--->节省使用正则表达式解决问题的空间/内存-->迭代器节省内存
3 re模块中使用正则表达式的特点和问题
3.1 分组()在re模块中的使用
分组:()
1.给不止一个字符的整体做量词约束的时候 www(\.[\w]+)+ www.baidu.com
2.优先显示,当要匹配的内容和不想匹配的内容混在一起的时候,
就匹配出所有内容,但是对实际需要的内容进行分组
3.分组和re模块中的方法 :
findall : 分组优先显示 取消(?:正则)
search :
可以通过.group(index)来取分组中的内容
可以通过.group(name)来取分组中的内容
正则 (?P<name>正则)
使用这个分组 ?P=name
split : 会保留分组内的内容到切割的结果中
3.1.1 分组优先
3.1.1.1 案例:
1 s = '<a>wahaha</b>' 2 ret = re.search('<(\w+)>(\w+)</\w+>',s) 3 if ret: 4 print(ret.group()) #<a>wahaha</b>显示所有结果 5 print(ret.group(1)) #a 匹配分组中第一部分 6 print(ret.group(2)) #wahaha 7 print(ret.group(3)) #到这会报错,正则中有两个分组,只能识别两个 8 #IndexError: no such group
1 s = '<a>wahaha</b>' 2 ret = re.search('<(\w+)>(\w+)</\w+>',s) 3 ret = re.findall('<(\w+)>(\w+)</\w+>',s) 4 print(ret) #[('a', 'wahaha')]
3.1.1.2 结论:
()分组优先,分组外的不显示
3.1.1.3 关于分组
对于正则表达式来说 有些时候我们需要进行分组,来整体约束某一组字符出现的次数
-----à (\.[\w]+)?
这里要清楚,对于python语言来说 分组可以帮助你更好更精准的找到你真正需要的内容
3.1.2 取消分组优先(?:正则表达式)
3.1.2.1 案例:
1 ret = re.findall('\d(\.\d+)?','1.234*4') 2 print(ret) #['.234', ''] 3 ret = re.findall('\d(?:\.\d+)?','1.234*4') 4 print(ret) #['1.234', '4']
3.1.3 分组在split中的使用
参考上面split
3.2 分组()命名 :(?P<这个组的名字>正则表达式)
案例1:
1 s = '<a>wahaha</a>' 2 # ret = re.search('>(?P<con>\w+)<',s) 3 # print(ret.group(1)) #wahaha 4 # print(ret.group('con')) #wahaha
案例2:
分组命名的用途:
# 判断标签中<>中内容是否一致 #方法一: 利用group s = '<a>wahaha</a>' pattern = '<(\w+)>(\w+)</(\w+)>' ret = re.search(pattern,s) print(ret.group(1) == ret.group(3)) #True # 方法二: 利用分组命名 # 使用前面的分组 要求使用这个名字的分组和前面同名分组中的内容匹配的必须一致 pattern = '<(?P<tab>\w+)>(\w+)</(?P=tab)>' ret = re.search(pattern,s) print(ret) #<_sre.SRE_Match object; span=(0, 13), match='<a>wahaha</a>'>
4 使用表达式的技巧
1 ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))") 2 print(ret) #['1', '2', '60', '40', '35', '5', '4', '3']我们想把小数和整数区分开,这样不行 3 4 ret=re.findall(r"\d+\.\d+|\d+","1-2*(60+(-40.35/5)-(-4*3))") 5 print(ret) #['1', '2', '60', '40.35', '5', '4', '3'] 6 ret=re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))") 7 print(ret) #['1', '2', '60', '', '5', '4', '3']-->分组优先的原则可以把小数过滤掉 8 ret.remove('') 9 print(ret) #['1', '2', '60', '5', '4', '3']
---》正则表达式如果写的足够好的话 能够最大限度的简化我们的操作
5 爬虫的例子
1 from urllib import request 2 ret = request.urlopen('https://movie.douban.com/top250?start=50&filter=') 3 res = ret.read().decode('utf-8') #转码 4 print(res)
注意:re.S参数是忽略换行符