什么是正则表达式
正则表达式是对字符串操作的一种逻辑公式, 就是 事先定义好的一些特定字符, 及这些特定字符的组合, 组成一个 "规则字符", 这个 "规则字符" 来表达对字符的一种过滤逻辑.
正则并不是 python 独有的, 其他语言也都有正则
python 中的正则, 封装了 re 模块
python 正则的详细讲解
常用的匹配模式
\w 匹配字母数字及下划线
\W 匹配 f 非字母数字下划线
\s 匹配任意空白字符, 等价于[\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意数字
\D 匹配任意非数字
\A 匹配字符串开始
\Z 匹配字符串结束, 如果存在换行, 只匹配换行前的结束字符串
\z 匹配字符串结束
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配字符串的开头
$ 匹配字符串的末尾
. 匹配任意字符, 除了换行符, re.DOTALL 标记被指定时, 则可以匹配包括换行符的任意字符
[....] 用来表示一组字符, 单独列出:[amk]匹配 a,m 或 k
[^...] 不在 [] 中的字符:[^abc]匹配除了 a,b,c 之外的字符
* 匹配 0 个或多个的表达式
+ 匹配 1 个或者多个的表达式
? 匹配 0 个或 1 个由前面的正则表达式定义的片段, 非贪婪方式
{n} 精确匹配 n 前面的表示
{m,m} 匹配 n 到 m 次由前面的正则表达式定义片段, 贪婪模式
a|b 匹配 a 或者 b
() 匹配括号内的表达式, 也表示一个组
re.match()
尝试从字符串的起始位置匹配一个模式, 如果不是起始位置匹配的话, match()就会返回 None
语法格式:
re.match(pattern,string,flags=0)
最常规的匹配
- import re
- content= "hello 123 4567 World_This is a regex Demo"
- result = re.match('^hello\s\d\d\d\s\d{4}\s\w{10}.*Demo$',content)
- print(result)
- print(result.group())
- print(result.span())
结果如下:
result.group()获取匹配的结果
result.span()获去匹配字符串的长度范围
泛匹配
其实相对来说上面的方式并不是非常方便, 其实可以将上述的正则规则进行更改
- 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())
这段代码的结果和上面常规匹配的结果是一样的, 但是写起来会方便很多
匹配目标
如果为了匹配字符串中具体的目标, 则需要通过 () 括起来, 例子如下:
- import re
- content= "hello 1234567 World_This is a regex Demo"
- result = re.match('^hello\s(\d+)\sWorld.*Demo$',content)
- print(result)
- print(result.group())
- print(result.group(1))
- print(result.span())
结果如下:
这里需要说一下的是通过 re.group()获得结果后, 如果正则表达式中有括号, 则 re.group(1)获取的就是第一个括号中匹配的结果
贪婪匹配
先看下面代码:
- import re
- content= "hello 1234567 World_This is a regex Demo"
- result= re.match('^hello.*(\d+).*Demo',content)
- print(result)
- print(result.group(1))
这段代码的结果是
从结果中可以看出只匹配到了 7, 并没有匹配到 1234567, 出现这种情况的原因是前面的.* 给匹配掉了, .* 在这里会尽可能的匹配多的内容, 也就是我们所说的贪婪匹配,
如果我们想要匹配到 1234567 则需要将正则表达式改为:
result= re.match('^he.*?(\d+).*Demo',content)
这样结果就可以匹配到 1234567
匹配模式
很多时候匹配的内容是存在换行的问题的, 这个时候的就需要用到匹配模式 re.S 来匹配换行的内容
- import re
- content = """hello 123456 world_this
- my name is zhaofan
- """result =re.match('^he.*?(\d+).*?zhaofan$',content,re.S)
- print(result)
- print(result.group())
- print(result.group(1))
结果如下
转义
当我们要匹配的内容中存在特殊字符的时候, 就需要用到转移符号 \, 例子如下:
- import re
- content= "price is $5.00"
- result = re.match('price is \$5\.00',content)
- print(result)
- print(result.group())
对上面的一个小结:
尽量使用泛匹配, 使用括号得到匹配目标, 尽量使用非贪婪模式, 有换行符就用 re.S
强调 re.match 是从字符串的起始位置匹配一个模式
re.search
re.search 扫描整个字符串返回第一个成功匹配的结果
- import re
- content = "extra things hello 123455 world_this is a Re Extra things"
- result = re.search("hello.*?(\d+).*?Re",content)
- print(result)
- print(result.group())
- print(result.group(1))
结果如下:
其实这个时候我们就不需要在写 ^ 以及 $, 因为 search 是扫描整个字符串
注意: 所以为了匹配方便, 我们会更多的用 search, 不用 match,match 必须匹配头部, 所以很多时候不是特别方便
匹配演练
例子 1:
- import re
- 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.search('<li.*?active.*?singer="(.*?)">(.*?)</a>',HTML,re.S)
- print(result)
- print(result.groups())
- print(result.group(1))
- print(result.group(2))
结果为:
re.findall
搜索字符串, 以列表的形式返回全部能匹配的子串
代码例子如下:
- import re
- 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>'''results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', HTML, re.S)
- print(results)
- print(type(results))
- for result in results:
- print(result)
- print(result[0], result[1], result[2])
结果如下:
例子 2:
- import re
- 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>'''results = re.findall('<li.*?>\s*?(<a.*?>)?(\w+)(</a>)?\s*?</li>',HTML,re.S)
- print(results)
- for result in results:
- print(result[1])
结果如下:
其实这里我们就可以看出
\s*? 这种用法其实就是为了解决有的有换行, 有的没有换行的问题
(<a.*?>)? 这种用法是因为 HTML 中有的有 a 标签, 有的没有的,? 表示匹配一个或 0 个, 正好可以用于匹配
re.sub
替换字符串中每一个匹配的子串后返回替换后的字符串
re.sub(正则表达式, 替换成的字符串, 原字符串)
例子 1
- import re
- content = "Extra things hello 123455 World_this is a regex Demo extra things"
- content = re.sub('\d+','',content)
- print(content)
结果会讲数字替换为为空:
例子 2, 在有些情况下我们替换字符的时候, 还想获取我们匹配的字符串, 然后在后面添加一些内容, 可以通过下面方式实现:
- import re
- content = "Extra things hello 123455 World_this is a regex Demo extra things"
- content = re.sub('(\d+)',r'\1 7890',content)
- print(content)
结果如下:
这里需要注意的一个问题是 \ 1 是获取第一个匹配的结果, 为了防止转义字符的问题, 我们需要在前面加上 r
re.compile
将正则表达式编译成正则表达式对象, 方便复用该正则表达式
- import re
- content= """hello 12345 world_this
- 123 fan
- """pattern =re.compile("hello.*fan",re.S)
- result = re.match(pattern,content)
- print(result)
- print(result.group())
正则的综合练习
获取豆瓣网书籍的页面的书籍信息, 通过正则实现
- import requests
- import re
- content = requests.get('https://book.douban.com/').text
- pattern = re.compile('<li.*?cover.*?href="(.*?)".*?title="(.*?)".*?more-meta.*?author">(.*?)</span>.*?year">(.*?)</span>.*?</li>', re.S)
- results = re.findall(pattern, content)
- print(results)
- for result in results:
- url,name,author,date = result
- author = re.sub('\s','',author)
- date = re.sub('\s','',date)
- print(url,name,author,date)
结果如下:
来源: http://www.bubuko.com/infodetail-3101058.html