第十一章 正则表达式

 
文本处理被认为是编程中最常用的功能之一,例如,从一大堆HTML代码中找到href属性值为某个Url的a标签就是网络爬虫经常要做的工作。

11.1 在python语言中使用正则表达式

python语言通过标准库中的re模块支持正则表达式。

11.1.1 使用match方法匹配字符串

匹配字符串是正则表达式中最常用的一类应用。也就是设定一个文本模式,然后判断另外一个字符串是否符合这个文本模式。

match方法用于指定文本模式和待匹配的字符串。该方法的前两个参数必须指定,第1个参数表示文本模式,第2个参数表示待匹配的字符串。如果匹配成功,则match方法返回 SRE_Match 对象,然后可以调用该对象中的group方法获取匹配成功的字符串,如果文本模式就是一个普通的字符串,那么group方法返回的就是文本模式字符串本身。

m = re.match('brid','bird')
print(m.group())

示例:

import re

m = re.match('hello', 'hello')
if m is not None:
    print(m.group())
print(m.__class__.__name__)

m = re.match('hello', 'world')
if m is not None:
    print(m.group())
print(m)

# 注意:只要模式从字符串起始位置开始,就可以匹配成功
m = re.match('hello', 'hello world')
if m is not None:
    print(m.group())
print(m)
hello
SRE_Match
None
hello
<_sre.SRE_Match object; span=(0, 5), match='hello'>

11.1.2 使用 search 方法在一个字符串中查找模式

搜索是正则表达式的另一类常用的应用场景。也就是从一段文本中找到一个或多个与文本模式相匹配的字符串。

search方法的传参与match方法类似,示例如下:

import re

# 匹配失败,返回 none
m = re.match('python','I love python.')
if m is not None: 
    print(m.group())
print(m)

# 搜索成功,返回 python 
m = re.search('python','I love python.')
if m is not None: 
    print(m.group())
print(m)

从SRE_Math 对象(m 变量)的输出信息可以看出,span的值是(7,13),表明满足文本模式的字符串的起始位置的索引是7,结束位置的下一个字符的索引是13。

11.1.3 匹配多个字符串

如果想要搜索多个字符串,最简单的方法就是在文本模式字符串中使用匹配符号 (|),只要满足任何一个就算匹配成功。

s = 'Bill|Mike|John'
m = re.match(s, 'Bill')
if m is not None: 
    print(m.group())
m = re.match(s, 'Mike')
if m is not None: 
    print(m.group())

# 以上均匹配成功

示例:

import re
s = 'Bill|Mike|John'
m = re.match(s, 'Bill')
if m is not None:
    print(m.group())
m = re.match(s, "Bill:my friend")
if m is not None:
    print(m.group())

m = re.search(s,'Where is Mike?')
if m is not None:
    print(m.group())
print(m)

# 以上均匹配成功

11.1.4 匹配任何单个字符

前边给出的文本模式字符串都是精确匹配。在正则表达式中,最常用的是匹配一类字符串。所以就需要使用一些特殊符号表示一类字符串。

点(.),这个符号可以匹配任意一个单个字符。

m = re.match('.ind','bind')    # 匹配成功

如果要匹配真正的点(.),应该如何处理?要解决这个问题,需要使用转义符(\)。

m = re.match('\.ind','bind')   # 匹配失败

示例:

import re

s = '.ind'
m = re.match(s, 'bind')
if m is not None:
    print(m.group())
# 匹配成功,但是得到的m对象需要使用group方法才能获取内容
m = re.match(s,'binding')
# 字符串拼接
print("<" + str(m))
# 匹配失败
m = re.match(s,'bin')
print(m)

# 搜索成功
m = re.search(s,'<bind>')
print(m.group())
print(m)

s1 = '3.14'
s2 = '3\.14'
# 匹配成功
m = re.match(s1, '3.14')
print(m)
# 匹配成功
m = re.match(s1, '3314')
print(m)
# 匹配成功
m = re.match(s2, '3.14')
print(m)
# 匹配失败
m = re.match(s2, '3314')
print(m)

11.1.5 使用字符集

如果匹配的字符串中,某些字符可以有多种选择,就需要使用字符集([ ])。例如,[abc] 相当于 “a|b|c”。对于长度大于1的字符串使用关系时,字符集就无能为力了。

import re
# 成功
m = re.match('[ab][cd][ef][gh]', 'adfh')
print(m.group())
# 成功
m = re.match('[ab][cd][ef][gh]', 'bceg')
print(m.group())
# 不匹配
m = re.match('[ab][cd][ef][gh]', 'abceh')  
print(m)
# 成功
m = re.match('ab[cd][ef][gh]', 'abceh')  
print(m.group())
print(m)
# 成功
m = re.match('abcd|efgh', 'efgh')
print(m.group())
print(m)

11.1.6 重复、可选和特殊字符

正则表达式中最常见的就是匹配一些重复的字符串。

  • *)表示出现 0 到 n 次
  • +)表示出现 1 到 n 次
  • )表示 0 次 1 次
  • \w)表示任意一个字母或数字
  • \d)表示任意一个数字

示例:

import re
# abc aabc   abbbccc
s = 'a+b+c+'
strList = ['abc','aabc','bbabc','aabbbcccxyz']
for value in strList:
    m = re.match(s, value)
    if m is not None:
        print(m.group())
    else:
        print('{}不匹配{}'.format(value,s))

print('--------------')

# 任意3个数字-任意3个小写字母
# 123-abc   433-xyz
#s = '\d\d\d-[a-z][a-z][a-z]'
s = '\d{3}-[a-z]{3}'
strList = ['123-abc','432-xyz','1234-xyz','1-xyzabc','543-xyz^%ab']
for value in strList:
    m = re.match(s, value)
    if m is not None:
        print(m.group())
    else:
        print('{}不匹配{}'.format(value,s))
print('-------------')
s = '[a-z]?\d+'
strList = ['1234','a123','ab432','b234abc']
for value in strList:
    m = re.match(s, value)
    if m is not None:
        print(m.group())
    else:
        print('{}不匹配{}'.format(value,s))

print('-------------')
# email
email = '\w+@(\w+\.)*\w+\.com'
emailList = ['abc@126.com','test@mail.geekori.com','test-abc@geekori.com','abc@geekori.com.cn']
for value in emailList:
    m = re.match(email,value)
    if m is not None:
        print(m.group())
    else:
        print('{}不匹配{}'.format(value,email))
strValue = '我的email是lining@geekori.com,请发邮件到这个邮箱'
m = re.search(email, strValue)
print(m)
email = '[a-zA-Z0-9]+@(\w+\.)*\w+\.com'
m = re.search(email, strValue)
print(m)
abc
aabc
bbabc不匹配a+b+c+
aabbbccc
--------------
123-abc
432-xyz
1234-xyz不匹配\d{3}-[a-z]{3}
1-xyzabc不匹配\d{3}-[a-z]{3}
543-xyz
-------------
1234
a123
ab432不匹配[a-z]?\d+
b234
-------------
abc@126.com
test@mail.geekori.com
test-abc@geekori.com不匹配\w+@(\w+\.)*\w+\.com
abc@geekori.com
<_sre.SRE_Match object; span=(0, 26), match='我的email是lining@geekori.com'>
<_sre.SRE_Match object; span=(8, 26), match='lining@geekori.com'>

11.1.7 分组

如果一个模式字符串中有用一对圆括号括起来的部分,那么这部分就会作为一组,可以通过group方法的参数获取指定组匹配的字符串。

import re
m = re.match('(\d{3})-(\d{4})-([a-z]{2})', '123-4567-xy')

if m is not None:
    print(m.group())
    print(m.group(1))
    print(m.group(2))
    print(m.group(3))
    # 得到一个元祖
    print(m.groups())
print('-----------------')
m = re.match('(\d{3}-\d{4})-([a-z]{2})', '123-4567-xy')
if m is not None:
    print(m.group())
    print(m.group(1))
    print(m.group(2))
    # 获取每组的值,组成一个元祖
    print(m.groups())
print('-----------------')
m = re.match('\d{3}-\d{4}-([a-z]{2})', '123-4567-xy')
if m is not None:
    print(m.group())
    print(m.group(1))
    # 获取每组的值,组成一个元祖
    print(m.groups())
print('-----------------')
m = re.match('\d{3}-\d{4}-[a-z]{2}', '123-4567-xy')
if m is not None:
    print(m.group())
    # 获取每组的值,组成一个元祖
    print(m.groups())
123-4567-xy
123
4567
xy
('123', '4567', 'xy')
-----------------
123-4567-xy
123-4567
xy
('123-4567', 'xy')
-----------------
123-4567-xy
xy
('xy',)
-----------------
123-4567-xy
()

11.1.8 匹配字符串的起始和结尾以及单词边界

  • ^)表示匹配字符串的开始
  • $)表示匹配字符串的结尾
  • \b)表示匹配单词的边界(两边是空格或标点符号)

示例:

import re
# 匹配
m = re.search('^The', 'The end.')
print(m)
if m is not None:
    print(m.group())
# 匹配
m = re.search('^The', 'end. The')
print(m)
if m is not None:
    print(m.group())
# 匹配
m = re.search('The$', 'end. The')
print(m)
if m is not None:
    print(m.group())
# 不匹配
m = re.search('The$', 'The end.')
print(m)
if m is not None:
    print(m.group())
# 匹配
m = re.search(r'\bthis', "What's this?")
print(m)
if m is not None:
    print(m.group())
# 不匹配
m = re.search(r'\bthis', "What'sthis?")
print(m)
if m is not None:
    print(m.group())
# 匹配
m = re.search(r'\bthis\b', "What's this?")
print(m)
if m is not None:
    print(m.group())  
# 不匹配
m = re.search(r'\bthis\b', "What's thisa")
print(m)
if m is not None:
    print(m.group())

11.1.9 使用 findall 和 finditer 查找每一次出现的位置

findall 函数总是返回一个包含搜索结果的列表。如果findall函数没有找到匹配的部分,就会返回一个空列表。finditer 函数与 findall 函数类似,只是更节省内存。区别在于 findall 会一次性返回到列表,而 finditer 会返回一个迭代器,只有对返回结果进行迭代,才会执行匹配。findall和finditer函数相当于读取XML文档的两种技术:DOM和SAX。前者更灵活,但也更耗内存资源;后者顺序读取,不能随机读取,但更节省内存资源。

示例:

import re
s = '12-a-abc54-a-xyz---78-A-ytr'

result = re.findall(r'\d\d-a-[a-z]{3}',s)
print(result)

result = re.findall(r'(\d\d)-a-([a-z]{3})',s)
print(result)

result = re.findall(r'\d\d-a-[a-z]{3}',s,re.I)
print(result)

result = re.findall(r'(\d\d)-a-([a-z]{3})',s,re.I)
print(result)

it = re.finditer(r'(\d\d)-a-([a-z]{3})',s,re.I)
for result in it:
    print(result.group(),end=' < ')
    groups = result.groups()
    for i in groups:
        print(i,end = ' ')
    print('>')

['12-a-abc', '54-a-xyz']
[('12', 'abc'), ('54', 'xyz')]
['12-a-abc', '54-a-xyz', '78-A-ytr']
[('12', 'abc'), ('54', 'xyz'), ('78', 'ytr')]
12-a-abc < 12 abc >
54-a-xyz < 54 xyz >
78-A-ytr < 78 ytr >

11.1.10 用 sub 和 subn 搜索与替换

sub函数与subn函数用于实现搜索和替换功能。这两个函数的功能几乎完全一样。都是将某个字符串中所有匹配正则表达式的部分替换成其他字符串。sub 函数返回替换后的结果,subn函数返回一个元组,元组的第1个元素是替换后的结果,第2个元素是替换的总数。

替换的字符串可以是普通字符串,也可以通过 “\N” 形式取出替换字符串中的分组信息,其中N是分组编号,从1开始。

import re
# 参数:匹配模式、要替换字符串、被替换的整个字符串
result = re.sub('Bill', 'Mike', 'Bill is my son')
print(result)

# 以元组形式输出
result = re.subn('Bill', 'Mike', 'Bill is my son,I like Bill')
print(result)
print(result[0])
print('替换总数','=',result[1])

# 取元组匹配模式,进行替换
result = re.sub('([0-9])([a-z]+)', r'产品编码(\1-\2)','01-1abc,02-2xyz,03-9hgf')
print(result)

def fun():
    return r'产品编码(\1-\2)'
result = re.subn('([0-9])([a-z]+)', fun(),'01-1abc,02-2xyz,03-9hgf')
print(result)
print(result[0])
print('替换总数','=',result[1])

11.1.11 使用 split 分隔字符串

split 函数用于根据正则表达式分隔字符串。第1个参数是模式字符串,第2个参数待分隔的字符串。如果待分隔的字符串非常大,那么可以使用关键字maxsplit指定最大分隔次数。

import re
result = re.split(';','Bill;Mike;John')
print(result)
result = re.split('[,;.\s]+','a,b,,d,d;x    c;d.  e')
print(result)
result = re.split('[a-z]{3}-[0-9]{2}','testabc-4312productxyz-43abill')
print(result)
result = re.split('[a-z]{3}-[0-9]{2}','testabc-4312productxyz-43abill',maxsplit=1)
print(result)
['Bill', 'Mike', 'John']
['a', 'b', 'd', 'd', 'x', 'c', 'd', 'e']
['test', '12product', 'abill']
['test', '12productxyz-43abill']

11.2 一些常用的正则表达式

具体要求不同,相应的正则表达式也可能不相同。

import re
# Email
email = '[0-9a-zA-Z]+@[0-9a-zA-Z]+\.[a-zA-Z]{2,3}'
result = re.findall(email, 'lining@geekori.com')
print(result)
result = re.findall(email, 'abcdefg@aa')
print(result)
result = re.findall(email, '我的email是lining@geekori.com,不是bill@geekori.cn,请确认输入的Email是否正确')
print(result)

# 匹配IPv4的正则表达式
ipv4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
result = re.findall(ipv4, '这是我的IP地址:33.12.54.34,你的IP地址是100.32.53.13吗')
print(result)

# 匹配url的正则表达式
url = 'https?:/{2}\w.+'
url1 = 'https://geekori.com'
url2 = 'ftp://geekori.com'
print(re.match(url,url1))
print(re.match(url,url2))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值