1.转义符(\)

       转义符\赋予了一个字符一个特殊的意义,如n变为了\n就表示换行符

1)python中的转义

#在字符串前加r表示取消字符串中的所有转移

print("C:\a.txt")
print(r"C:\a.txt")
输出:
C:.txt
C:\a.txt
View Code

#在转义符\前再加一个\就表示取消转义,恢复其原本的意义

print("C:\a.txt")
print("C:\\a.txt")
输出:
C:.txt
C:\a.txt
View Code

#在正则表达式中,遇到正则转义,直接在之前加r

总结:在python中'\\\\n'才能匹配到'\\n' ,所以只要早测试工具测试成功,那在程序中采用加r的方式即可,即r'\\n'匹配r'\n'

2)正则中的转义

①‘\(’    在正则中匹配一个(

②[()+*?/$.]   在字符组中一些特殊的字符会显出原形(即表示原本的意义),如()+*?/等符号

③所有的\w \d \s \n \t在字符组中都表示它原本的意义

④[-]       减号-只有写在字符组的首位的时候表示普通的减号,写在其他位置表示范围,如[1-9]就表示1-9,如果就是想匹配减号,可以写为[1\-9]

2.re模块

       使用先导入:import re

1)字符串的匹配

①findall(必会)

#语法:re.findall('正则表达式','待匹配的字符串')

#参数:正则表达式,待匹配字符串  返回值类型:列表 返回值个数:1 返回值内容:所有匹配上的项;若没有匹配上,就是一个没有值的列表

import re
ret = re.findall('\d',"47823dao123")
print(ret)
输出:
['4', '7', '8', '2', '3', '1', '2', '3']
View Code

②search(必会)

#语法:re.search('正则表达式','待匹配的字符串')

#参数:正则表达式,待匹配字符串 ;返回值类型:正则匹配结果的对象 ;返回值个数:1; 返回值内容列表: 如果匹配上了就返回对象,如果没有匹配上就返回None

import re
ret2 = re.search('\d',"47823dao123")
ret3 = re.search('\d',"jaosfjo")
print(ret2)
print(ret3)
输出:
<_sre.SRE_Match object; span=(0, 1), match='4'>
None
View Code

#返回的对象通过group来匹配到的第一个结果,其他结果无法获取

import re
ret2 = re.search('\d',"47823dao123")
ret3 = re.search('\d+',"47823dao123")
print(ret2.group())
print(ret3.group())
输出:
4
47823
View Code

③match

       表示从头开始匹配,不管正则表达式写成什么样,都默认在最前面加一个^,然后再做和search同样的工作

import re
ret2 = re.match('\d+',"47823dao123")
ret3 = re.match('\d+',"$%47823dao123")
print(ret2)
print(ret3)       #输入None
print(ret2.group())
print(ret3.group())  #报错::AttributeError: 'NoneType' object has no attribute 'group'
输出:
<_sre.SRE_Match object; span=(0, 5), match='47823'>
None
47823
报错:
AttributeError: 'NoneType' object has no attribute 'group'
View Code

#要想search实现和match一样的效果,可以再search的正则表达式最前放加一个^

import re
ret2 = re.search('^\d+',"47823dao123")
ret3 = re.search('^\d+',"^$%47823dao123")
print(ret2)
print(ret3)       #输入None
print(ret2.group())
print(ret3.group())
输出:
<_sre.SRE_Match object; span=(0, 5), match='47823'>
None
47823
报错:AttributeError: 'NoneType' object has no attribute 'group'
View Code

#一般在用search匹配时,可以先用if语句判断一下是否匹配到结果,如果匹配到了,再用group输入匹配到的结果(可避开报错)

import re
ret2 = re.search('^\d+',"47823dao123")
ret3 = re.search('^\d+',"^$%47823dao123")
if ret2:print(ret2.group())
if ret3:print(ret3.group())
输出:
47823
View Code

2)字符串的替换

回顾:replace对字符串的替换

print("HelloWorld".replace("o","e"))
输出:
HelleWerld
View Code

#replace知道替换次数

print("HelloWorld".replace("o","e",1))
输出:
HelleWorld
View Code

①sub(***)

#语法:re.search('正则表达式','将匹配到的字符串替换成什么','待匹配字符产')

import re
ret = re.sub('\d','W','Hello8orld')
ret2 = re.sub('\d+','x','123*123')
print(ret)
print(ret2)
输出:
HelloWorld
x*x
View Code

注:python不能直接操作硬盘中的数据,只能先将硬盘中的数据读到内存中,再操纵内存

#sub指定替换的次数

import re
ret = re.sub('\d+','x','123*123*123',1)
print(ret)
输出:
x*123*123
View Code

②sub2(***)

#语法:re.search('正则表达式','将匹配到的字符串替换成什么','待匹配字符产')

特点:和sub的区别在于他会将匹配到的次数计算出来,将替换后的字符串与匹配到的次数以元组的形式返回

import re
ret = re.subn("\d+","X","123dad*sas789jdai456")
ret2 = re.subn("\d","X","123dad*sas789jdai456")
print(ret)
print(ret2)
输出:
('Xdad*sasXjdaiX', 3)
('XXXdad*sasXXXjdaiXXX', 9)
View Code

3)字符串的切割

回顾:字符串的split方法切割

print("阿狸_泽拉斯_塞拉斯_拉克丝".split("_"))
输出:
['阿狸', '泽拉斯', '塞拉斯', '拉克丝']
View Code

①split(***)

#语法:re.split('正则表达式','字符串')   

#将字符串以正则匹配到的字符串为分割符进行分割,然后将分割后的字符串以元组的形式返回

import re
ret = re.split("\d+","阿狸123泽拉斯456塞拉斯789拉克丝")
print(ret)
输出:
['阿狸', '泽拉斯', '塞拉斯', '拉克丝']
View Code

4)进阶方法(爬虫/自动化开发必会)

①compile(*****)---------时间效率

#写的则正则表达式首先要变成python解释器能理解的代码,然后再通过这些代码查找你要匹配的内容,但是如果是一个特别长的正则表达式,并且经常使用同一个方法,那么每次使用时都会编译一遍,导致程序效率降低,所以可以利用compile方法将正则表达式先进行编译

#匹配复数
import re
ret = re.compile('-0\.\d+|-[1-9]\d+(\.\d+)?')
res1 = ret.search("sajod-20ada")
res2 = ret.search("-30jdiaojd")
res3 = ret.search("jdiaojd-40")
print(res1.group())
print(res2.group())
print(res3.group())
输出:
-20
-30
-40
View Code

注:如果正则表达式只用一次不会节省时间,只有在多次使用某一个相同的正则表达式的时候,这个compile才会提高程序的效率

②finditer(*****)-----空间效率

#如果一次匹配出来的是海量的时候,不要用findall直接获取匹配到的内容,应该用finditer,通过finditer返回的数据是一个可迭代对象(可一个一个取值),便利迭代器拿到的是一个对象,再通过group取到每一个的值

import re
res = re.finditer("\d","a21asd5ad5959da2789da")
for r in res:
    print(r.group())
输出:
2
17
View Code

注:所以的空间效率的提升,都代表着时间的提速

总结:在re模块中,findall方法和split方法都返回一个列表;并且findall和split遇到分组会出现问题,如下:

3.正则表达式和python的re模块

1)findall会优先显示分组中的内容,要想取消分组优先,可以在分组的第一个位置前加一个?: 即(?:正则表达式)

import re
ret = re.findall('-0\.\d+|-[1-9]\d*(\.\d+)?','-1asdada-200')
print(ret)   #正常情况下应该打印处-1和-200,但是输出了['', '']
res = re.findall("www.baidu.com|www.qq.com","www.qq.com")
print(res) #输出['www.qq.com']
rep = re.findall("www.(baidu|qq).com","www.qq.com")    #正则表达式表达的意思同上,但是匹配到的内容不是想要的内容
print(rep)      #输出:['qq'],,即只匹配到了分组中的内容
输出:
['', '']
['www.qq.com']
['qq']
View Code

#在分组内的第一个位置写【?:】表示取消这个组的分组优先

import re
ret = re.findall('-0\.\d+|-[1-9]\d*(?:\.\d+)?','-1asdada-200')
print(ret)
rep = re.findall("www.(?:baidu|qq).com","www.qq.com")
print(rep)   
输出:
['-1', '-200']
['www.qq.com']
View Code

2)split方法在遇到分组的时候,会保留分组内,被切掉的内容

import re
ret = re.split("(\d+)","阿狸123泽拉斯456塞拉斯789拉克丝")
print(ret)
输出:
['阿狸', '123', '泽拉斯', '456', '塞拉斯', '789', '拉克丝']
View Code

3)search和分组:如果search方法中有分组,通过group(n)就能够获取到group中匹配的内容

import re
ret = re.search('\d+(.\d+)(.\d+)(.\d+)?','1.2.3.4-2*(60+(-40.35/5)-(-4*3))')
print(ret.group())
print(ret.group(1))
print(ret.group(2))
print(ret.group(3))
输出:
1.2.3.4
.2
.3
.4
View Code

注:在所有的re模块方法中,只有findall和split方法在遇到分组是会有问题,其他方法都正常

4.分组练习

1)从表达式1-2*(60+(-40.35/5)-(-4*3))获取到所有整数

①直接通过\d匹配,不能实现

import re
print(re.findall('\d+',"1-2*(60+(-40.35/5)-(-4*3))"))
输出:
['1', '2', '60', '40', '35', '5', '4', '3']
View Code

②匹配出每一个数字(整数和小数都显示),不管正负

import re
ret = re.findall('\d+(?:\.\d+)?',"1-2*(60+(-40.35/5)-(-4*3))")
print(ret)
输出:
['1', '2', '60', '40.35', '5', '4', '3']
View Code

③利用分组优先显示的特点,先取到分组内部的值(即整数),然后再把匹配到的空字符去掉

import re
ret = re.findall('\d+(?:\.\d+)|(\d+)',"1-2*(60+(-40.35/5)-(-4*3))")
print(ret)
ret.remove('')
print(ret)
输出:
['1', '2', '60', '', '5', '4', '3']
['1', '2', '60', '5', '4', '3']
View Code

注:当不想要的字符串和想要的字符串发生了冲突,可以把不想要的字符串先匹配出来,然后让其不显示

2)标签匹配,对于如下标签

<a>wahaha</a>

①取出<a>

import re
ret = re.findall('<\w+>',r"<a>wahaha</a>")
print(ret)
输出:
['<a>']
View Code

②只取出a

import re
ret = re.findall('<(\w+)>',r"<a>wahaha</a>")   #加上分组,使其优先显示
print(ret)
输出:
['a']
View Code

③取出里面内容,如wahaha

import re
ret = re.findall('>(\w+)<',r"<a>wahaha</a>")   #加上分组,使其优先显示
print(ret)
输出:
['wahaha']
View Code

④通过search的方式取值

import re
ret = re.search('<(\w+)>(\w+)</(\w+)>',"<a>wahaha</a>")
print(ret.group())
print(ret.group(1))
print(ret.group(2))
输出:
<a>wahaha</a>
a
wahaha
View Code

3)分组命名(两个名字相同的分组必须一样)

       当需要用到一个部分匹配到的内容和另一个地方必须一样的时候,可以用到分组命名

<?P<name>正则表达式>表示给分组起名字

<?P=name>表示使用这个分组,这里匹配到的内容应和分组中的内容完全相同

import re
ret = re.search('<(?P<name>\w+)>\w+</(?P=name)>',"<h1>hello</h1>")
print(ret.group("name"))
输出:
h1
View Code

#正常情况下也可使用分组命名的方式获取匹配到的值

import re
ret = re.search(r'<(?P<X>\w+)>(?P<Z>\w+)</(\w+)>',r'<a>hello</b>')
print(ret.group())
print(ret.group('X'))
print(ret.group('Z'))
输出:
<a>hello</b>
a
hello
View Code

4)通过索引使用分组:\1表示使用第一组,即匹配到的内容必须和第一个分组中的内容完全相同

    #分组命名第二种写法:如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致,获取的匹配结果可以直接用group(序号)拿到对应的值

import re
ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
print(ret.group(1))
输出:
h1
View Code

5.爬虫练习

补充:python中获取网页上的源代码:使用python内置的包urllib.request,这个包专门用来打开网页

import re
from urllib.request import  urlopen
#获取网页的源代码,对于python而言获取到的都是字符串
ret = urlopen("http://www.baidu.com")  #返回一个对象
print(ret.read().decode("utf-8"))  #获取到这个网页的html代码,直接获取到的是一个被编码的,所以需要解码
View Code

6.random模块

随机:在某个范围内取到的每一个值的概率都是相同的

#和随机有关的操作,都用到random模块(如抽奖、彩票、发红包、验证码、洗牌)

1)随机小数

#产生0-1之内的随机小数

import random
print(random.random())
输出:
0.9196700983181918
View Code

#取到某个特定范围内的小数

import random
print(random.uniform(2,5)) #取2-5之间的随机小数
输出:
2.8264542818054768
View Code

2)随机整数

#包含范围内的随机整数

import random
print(random.randint(10,20))   #获取一个10-20之间的随机数,包含边界值
输出:
18
View Code

#左闭右开区间取随机数

import random
print(random.randrange(10,20))    #有可能取到10,但取不到20
输出:
17
View Code

#在[1,10]不包含10在内的范围内随机取奇数(即间隔两个数取一个)

import random
print(random.randrange(1,10,2))
输出:
5
View Code

3)随机抽取

#随机抽取一个值

import random
lst = ["阿狸","泽拉斯","拉克丝",("惩戒之箭","韦鲁斯")]
print(random.choice(lst))
输出:
泽拉斯
View Code

#随机获取多个值

import random
lst = ["阿狸","泽拉斯","拉克丝",("惩戒之箭","韦鲁斯")]
print(random.sample(lst,2))
输出:
['阿狸', ('惩戒之箭', '韦鲁斯')]
View Code

4)打乱顺序(在原列表的基础上打乱顺序 )

import random
lst = ["阿狸","泽拉斯","拉克丝",("惩戒之箭","韦鲁斯")]
random.shuffle(lst)
print(lst)
输出:
['阿狸', ('惩戒之箭', '韦鲁斯'), '拉克丝', '泽拉斯']
View Code

注:永远不要起和知道的模块名相同的文件名

7.random模块练习

1)生成随机验证码(四位数字的)

import random
code = ''
for i in range(4):
    num = random.randint(0,9)            #random.randrange(0,10)
    code += str(num)       #将产生的每一个数字转化为字符串,进行拼接
print(code)
输出:
9154
View Code

#通过sample的方式无法完成一个随机验证码,因为列表中的某个数字被选中后,无法再次被选中,即少了2234这种情况

import random
lst = list(range(0,10))
print(lst)
num = random.sample(lst,4)
print(num)
输出:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[4, 1, 2, 8]
View Code

#通过函数的方式实现验证码

import random
def random_func(n=4):
    code = ''
    for i in range(n):
        num = random.randint(0,9)            #random.randrange(0,10)
        code += str(num)       #将产生的每一个数字转化为字符串,进行拼接
    return code
print(random_func()) #不传参数,默认生成4位验证码
print(random_func(6)) #可传参控制验证码的个数
输出:
0043
570297
View Code

2)生成验证码(6位的数字+字母)

#按照如下方法,每一位出现数字、大写字母、小写字母的概率为1/3,可通过choice的方式,将所有的数字、字母放入一个列表,然后随机取

import random
code = ''
for i in range(6):
    code_num = str(random.randint(0,9))
    code_alph = chr(random.randint(97,122))
    code_alph_upper = chr(random.randint(65,90))
    code_choice = random.choice([code_num,code_alph,code_alph_upper])
    code += code_choice
print(code)
输出:
S22JU8
View Code

#函数实现即可以生成数字,又可生成数字+字母的验证码(具体情况由参数决定)

def func_random(n=6,flag=True):
    code = ''
    for i in range(n):
        code_num = str(random.randint(0,9))
        if flag:
            code_alph = chr(random.randint(97,122))
            code_alph_upper = chr(random.randint(65,90))
            code_num = random.choice([code_num,code_alph,code_alph_upper])
        code += code_num
    return code
print(func_random())
print(func_random(4,False))
输出:
CLn8nP
2599
View Code

书籍推荐:《正则指引》