Python3爬虫笔记 -- 正则表达式

常用的匹配规则:

模式 描述
\w 匹配字母、数字及下划线
\W 匹配不是字母、数字及下划线的字符
\s 匹配任意空白字符,等价于[\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意数字,等价于[0-9]
\D 匹配任意非数字的字符
\A 匹配字符串开头
\Z 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结尾,如果存在换行,同时还会匹配换行符
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配一行字符串的开头
$ 匹配一行字符串的结尾
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符
[…] 用来表示一组字符,单独列出,比如[amk]匹配a、m或k
[^…] 不在[]中的字符,比如[^abc]匹配除了a、b、c之外的字符
* 匹配0个或多个表达式
+ 匹配1个或多个表达式
? 匹配0个或1个前面的正则表达式定义的片段,非贪婪方式
{n} 精确匹配n个前面的表达式
{n, m} 匹配n到m次由前面正则表达式定义的片段,贪婪方式
a|b 匹配a或b
( ) 匹配括号内的表达式,也表示一个组
  • Python的re库提供了整个正则表达式的实现,利用这个库,可以在Python中使用正则表达式。

1、match()

  • match()方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回匹配成功的结果;如果不匹配,就返回None
  • 而在match()方法中,第一个参数传入了正则表达式,第二个参数传入了要匹配的字符串。
  • 打印输出结果,可以看到结果是SRE_Match对象,这证明成功匹配。该对象有两个方法:group()方法可以输出匹配到的内容;span()方法可以输出匹配的范围。
import re

content = 'Hello 123 4567 World_This is a Regex Demo'
print(len(content))
result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}', content)
print(result)
print(result.group())
print(result.span())

#输出结果
41
<_sre.SRE_Match object; span=(0, 25), match='Hello 123 4567 World_This'>
Hello 123 4567 World_This
(0, 25)

匹配目标

  • 如果想从字符串中提取一部分内容,可以使用()括号将想提取的子字符串括起来。调用group()方法传入分组的索引(从1开始)即可获取提取的结果。
import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello\s(\d+)\sWorld', content)
print(result)
print(result.group())
print(result.group(1))
print(result.span())

#输出结果
<_sre.SRE_Match object; span=(0, 19), match='Hello 1234567 World'>
Hello 1234567 World
1234567
(0, 19)

通用匹配

  • .*(点星):其中.(点)可以匹配任意字符(除换行符),*(星)代表匹配前面的字符无限次,所以它们组合在一起就可以匹配任意字符了。
import re

content = 'Hello 123 4567 World_This is a Regex Demo'
result = re.match('^Hello.*Demo$', content)
print(result)
print(result.group())
print(result.span())

#运行结果
<_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
Hello 123 4567 World_This is a Regex Demo
(0, 41)

贪婪与非贪婪

  • 贪婪匹配:如下例,.*(点星)默认为贪婪匹配,会匹配尽可能多的字符。正则表达式中,.*后面是\d+,也就是至少一个数字,,因此,.*就尽可能匹配多的字符,这里就把123456匹配了,给\d+留下一个可满足条件的数字7,最后得到的内容就只有数字7了。
import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*(\d+).*Demo$', content)
print(result)
print(result.group(1))

#运行结果
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
7
  • 非贪婪匹配:尽可能匹配更少的字符,可以使用.*?
  • 当.*?匹配到Hello后面的空白字符时,再往后的字符就是数字了,而\d+恰好可以匹配,那么这里.*?就不再进行匹配,交给\d+去匹配后面的数字。所以这样.*?匹配了尽可能少的字符,\d+的结果就是1234567了。
import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*Demo$', content)
print(result)
print(result.group(1))

#输出结果
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
1234567
  • 下例中,.*?没有匹配到任何结果,而.*则尽量匹配多的内容:
import re

content = 'http://weibo.com/comment/kEraCN'
result1 = re.match('http.*?comment/(.*?)', content)
result2 = re.match('http.*?comment/(.*)', content)
print('result1', result1.group(1))
print('result2', result2.group(1))

#运行结果
result1 
result2 kEraCN

修饰符

  • 如下例,在字符串中加了换行符之后,匹配出错。这是因为.匹配除了换行符之外的所有字符。
import re

content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.match('^He.*?(\d+).*?Demo$', content)
print(result.group(1))

#运行结果:
AttributeError Traceback (most recent call last)
<ipython-input-18-c7d232b39645> in <module>()
      5 '''
      6 result = re.match('^He.*?(\d+).*?Demo$', content)
----> 7 print(result.group(1))

AttributeError: 'NoneType' object has no attribute 'group'
  • 这里只需加一个修饰符re.S,即可修正这个错误,这个修饰符的作用是使.匹配包括换行符在内的所有字符:
result = re.match('^He.*?(\d+).*?Demo$', content, re.S)
  • 常见修饰符总结
    | 修饰符 | 描述 |
    |–|--|
    re.I|使匹配对大小写不敏感
    re.L|做本地化识别(locale-aware)匹配
    re.M|多行匹配,影响^和$
    re.S|使.匹配包括换行在内的所有字符
    re.U|根据Unicode字符集解析字符。这个标志影响\w、\W、 \b和\B
    re.X|该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解

转义匹配

  • 当遇到用于正则匹配模式的特殊字符时,在前面加反斜线转义一下即可。例如.就可以用\.来匹配/

2、search()

  • 前面提到,match()方法是从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败了。这在做匹配时并不方便。它更适合用来检测某个字符串是否符合某个正则表达式的规则。
  • search():它在匹配时会扫描整个字符串,然后返回第一个(Attention:只返回第一个)成功匹配的结果。
import re

content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
result = re.search('Hello.*?(\d+).*?Demo', content)
print(result)
print (result.group(1))

#运行结果
<re.Match object; span=(13, 53), match='Hello 1234567 World_This is a Regex Demo'>
1234567

3、findall()

  • findall():获取匹配正则表达式的所有内容
  • 该方法会搜索整个字符串,然后返回匹配正则表达式的所有内容。返回结果是一个元组组成的列表
  • 如果只是获取第一个内容,可以用search()方法。当需要提取多个内容时,可以用findall()方法。

4、sub()

  • sub()方法可以用来修改文本。
import re

content = '54aK54yr5oiR54ix5L2g'
content = re.sub('\d+', '', content)
print(content)

#输出结果:
aKyroiRixLg
  • 在实际应用中,可以先使用sub()将一些无用的内容替换为空,然后在进行匹配。可以节省工作。
  • 下例中, 用sub替换时用|将两个正则表达式分割开,字符串中符合两个正则表达式的内容都会被替换掉。
html = '<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>'
html = re.sub('<a.*?>|</a>', '', html)
print (html)

#输出结果
沧海一声笑

5、compile()

  • compile():可以将正则字符串编译成正则表达式对象,以便在后面的匹配中复用。
  • 如下例,我们想分别将3个日期中的时间去掉。这里没有必要重复写3个同样的正则表达式,此时可以借助compile()方法将正则表达式编译成一个正则表达式对象,以便复用
import re
 
content1 = '2016-12-15 12:00'
content2 = '2016-12-17 12:55'
content3 = '2016-12-22 13:21'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern, '', content1)
result2 = re.sub(pattern, '', content2)
result3 = re.sub(pattern, '', content3)
print(result1, result2, result3)
  • 另外,compile()还可以传入修饰符,例如re.S等修饰符,这样在search()、findall()等方法中就不需要额外传了。所以,compile()方法可以说是给正则表达式做了一层封装,以便我们更好地复用。
发布了38 篇原创文章 · 获赞 18 · 访问量 6075

猜你喜欢

转载自blog.csdn.net/Sc0fie1d/article/details/102724298