一、概述:

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

二、常用的正则匹配规则

python正则爬虫 爬虫中正则表达式_python

三、match()

  • match()方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回匹配成功的结果;如果不匹配,就返回None
  • 在 match()方法中,第一个参数传入了正则表达式,第二个参数传入了要匹配的字符串
  • 打印输出结果,可以看到结果是 SRE_Match 对象,这证明成功匹配 该对象有两个方法: group()
    方法可以输出匹配到的内容,结果是 Hello 123 456 my mine,这恰好是正则表达式规则所匹配的
    内容;span()方法可以输出匹配的范围,结果是span=(0, 21),,这就是匹配到的结果字符串在原字符串中的
    位置范围
import re
content = 'Hello 123 456 my mine'
print(len(content))
# ^用它来匹配这个长字符串 开头的 是匹配字符串的开头,也就是以 Hello 开头
result = re.match('^Hello\s\w+\s\w+\s\w+\s\w+',content)

print(result)
print(result.group())
print(result.span())

输出:
21
<_sre.SRE_Match object; span=(0, 21), match='Hello 123 456 my mine'>
Hello 123 456 my mine
(0, 21)

3.1、匹配目标

如果想从字符串中提 一部分内容,该怎么办呢?就像最前面的实例一样,从一段文本中提取阳邮件或电话号码等内容

  • 1.这里可以使用()括号将想提取的子字符串括起来 ()实际上标记了一个子表达式的开始和结束位置
  • 2.被标记的每个子表达式会依次对应每一个分组,调用 group()方法传入分组的索引即可获取提取
  • 3.可以看到,我们成功得到了123这里用的是 group(1),它与 group()有所不同,后者会输出
    完整的匹配结果,而前者会输出第一个被()包围的匹配结果 假如正则表达式后面还有()包括的内容,那么可以依次用group(2)、group(3)等来获取
import re
content = 'Hello 123 456 my mine'
result = re.match('^Hello\s(\d+)\s(\d+)',content)
print(result)
print(result.group())
print(result.group(1))
print(result.span())

输出:
<_sre.SRE_Match object; span=(0, 13), match='Hello 123 456'>
Hello 123 456
123
(0, 13)

3.2、通用匹配

刚才写的正则表达式其实较复杂,出现空白字符我们就写\ 匹配,出现数字我们就用匹配,这样的工作量非常大 其实完全没必要这么做,因为还有一个万能匹配可以用,那就是 .*(点星) 其中 .(点)可以匹配任意字符(除换行符),(星)代表匹配前面的字符无限次,所以它们组合在一起就可以匹配任意字符了 有了它,我们就不用挨个字符地匹配了

import re
content = 'Hello 123 456 my mine'
# result = re.match('^Hello\s(\d+)\s(\d+)',content)
result = re.match('^Hello.*mine$', content)
print(result)
print(result.group())
print(result.span())

输出:
<_sre.SRE_Match object; span=(0, 21), match='Hello 123 456 my mine'>
Hello 123 456 my mine
(0, 21)

3.3、贪婪与非贪婪

import re
content = 'Hello 123 456 my mine'
# result = re.match('^Hello\s(\d+)\s(\d+)',content)
result = re.match('^He.*(\d+).*mine$', content)
print(result)
print(result.group(1))

输出:
<_sre.SRE_Match object; span=(0, 21), match='Hello 123 456 my mine'>
6
  • 我们只得到7个数字是怎么回事呢?
    这里就涉及一个贪婪匹配与非贪婪匹配的问题了在贪婪匹配下 * 会匹配尽可能多的字符,则表达式中.*后面是\d+,也就是至少一个数字,并没有指定具体多少个数字,因此, 就尽可能匹配多的字符,这里就把 123 456 匹配了,给\d+留下一个可满足条件的数字 ,最后得到的内容就只有数字
  • 但这很明显会给我们带来很大的不便 有时候,匹配结果会莫名其妙少了一部分内容 其实,这
    里只需要使用非贪婪匹配就好了 非贪婪匹配的写法是.叫,多了一个飞那么它可以达到怎样的效果?
    我们再用实例看一下
import re
content = 'Hello 123 456 my mine'
# result = re.match('^Hello\s(\d+)\s(\d+)',content)
result = re.match('^He.*?(\d+).*mine$', content)
print(result)
print(result.group(1))

# 此时就可以获取123了,原因可想而知,贪婪匹配
输出:
<_sre.SRE_Match object; span=(0, 21), match='Hello 123 456 my mine'>
123
  • 在做匹配的时候,字符串中间尽量使用非贪婪匹配,也就是用 . * ?来替代. *,以免出现匹配结果缺失的情况
  • 但是需要注意的是匹配结果在字符串结尾.*?就有可能匹配不到任何内容了,因为他会尽可能匹配少的字符
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('result1', result2.group(1))

# 可以看到.*?没有匹配任何结果,而.*则匹配多的内容
输出:
result1 
result1 kEraCN

3.4、修饰符

python正则爬虫 爬虫中正则表达式_python正则爬虫_02

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

# 运行报错,,也就是说正则表达式没有匹配到这个字符串,返回结果为None,又调用了group()导致AttrubuteError
输出:
AttributeError: 'NoneType' object has no attribute 'group'

上面加了一个换行符,则匹配不到。匹配的是除换行符之外的任意字符,当遇到换行符时,.*?就不能匹配,所有导致匹配失败,这里只需要添加修饰符re.S,则可以修正这个错误

import re

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

3.5、转义匹配

  • 我们知道正则表达式定义了许多匹配模式,如 匹配除换行符以外的任意字符,但是如果目标字符串里面就包含.的呢?
content = '(百度)www.baidu.com'
result = re.match('\(百度\)www.baidu\.com',content)
print(result)

# 当遇到用于正则匹配的特殊字符时,在前面加反斜线转义一下即可。例如.就可以用\.来匹配
输出:
<_sre.SRE_Match object; span=(0, 17), match='(百度)www.baidu.com'>

四、search()

  • 前面提到过, match()方法是从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败,我们看下面的例子:
content = 'Extra stings Hello 1234567 World This is a Regex Demo Extra stings'
result = re.match('Hello.*?(\d+)',content)
print(result)

# 这里的字符串以 Extra 开头,但是正则表达式以 ello 开头,整个正 表达式是字符串的一部分,
但是这样匹配是失败的 运行结果如下:
输出:
None
  • 这里有另外一个方法search(),它在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果。在匹配时,serach()方法会依次扫描字符串,知道找到第一个符合的字符串,然后返回匹配的内容,如果搜索完了还没找到,就返回None
# 将上面代码中的match换成search
content = 'Extra stings Hello 1234567 World This is a Regex Demo Extra stings'
result = re.search('Hello.*?(\d+)',content)
print(result)

输出:
<_sre.SRE_Match object; span=(13, 26), match='Hello 1234567'>

五、findall()

  • 前面我们介绍了 search ()方法的用法,它可以返回匹配正则表达式的第 个内容,但是如果想要获取匹配正则表达式的所有内容,那该怎么办呢?这时就要借助 findall()方法,该方法会搜索整个字符串,然后返回匹配正则表达式的所有内容
html = """< div id="songs-list" >
<h2 class ="title">经典老歌</h2>
<p class=”introduction”>
经典老歌列表
</p>
<ul id=”list” class=”list-group”><li data-view="2"〉一路上有你</li>
<li data-view="7">

<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a></li>
</ul>
</div>"""

result = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html,re.S)
print(result)
print(type(result))


输出:
[('/2.mp3', '任贤齐', '沧海一声笑'), ('/3.mp3', '齐秦', '往事随风'), ('/4.mp3', 'beyond', '光辉岁月'), ('/5.mp3', '陈慧琳', '记事本'), ('/6.mp3', '邓丽君', '但愿人长久')]
<class 'list'>

如果只获取第一个内容,则使用search()

result = re.search('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html,re.S)
print(result)
print(type(result))

输出:
<_sre.SRE_Match object; span=(123, 211), match='<li data-view="2"〉一路上有你</li>\n<li data-view="7">\>
<class '_sre.SRE_Match'>

六、sub()

  • 除了使用正则表达式提取信息外,有时候还需要借助它来修改文本 比如,想要把 串文本中的所有数字都去掉,如果只用字符串的 replace()方法,那就太烦琐了,这时可以借助 sub()方法
    如下:
content = '422rfdsa34grvfdsa'
content = re.sub('\d+','',content)
print(content)

输出:
rfdsagrvfdsa

想获取所有 li 节点的歌名,如下:

html = re.sub('<a.*?>|</a>', '', html)
results = re.findall('<li.*?>(.*?)</li>',html,re.S)
print(html)

print('--')


for result in results:
    print(result.strip())

输出:
< div id="songs-list" >
<h2 class ="title">经典老歌</h2>
<p class=”introduction”>
经典老歌列表
</p>
<ul id=”list” class=”list-group”><li data-view="2"〉一路上有你</li>
<li data-view="7">

沧海一声笑
</li>
<li data-view="4" class="active">
往事随风
</li>
<li data-view="6">光辉岁月</li>
<li data-view="5">记事本</li>
<li data-view="5">
但愿人长久</li>
</ul>
</div>
--
<li data-view="7">

沧海一声笑
往事随风
光辉岁月
记事本
但愿人长久

七、compile()

compile 函数用于编译正则表达式,生成一个 Pattern 对象

content1 = '2016-12-15 12:00'
content2 = '2016-12-17 12:55'
content3 = '2016-12-17 13:21'
partern = re.compile('\d{2}:\d{2}')
result1 = re.sub(partern, '', content1)
result2 = re.sub(partern, '', content2)
result3 = re.sub(partern, '', content3)
print(result1, result2, result3)

输出:
2016-12-15  2016-12-17  2016-12-17

参考:
崔庆才《Python3网络爬虫开发实战》