什么是正则表达式

  正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。另外这个正则表达式是非python独有的,可以通过re模块里面的函数来实现
样例展示

  可以点我访问这个在线正则表达式测试的网站,然后当你在上面输入一些信息,不管里面有多么的杂乱,我们都可以用右边的工具去转换,如下图所示
85cU.png
  还有当我们遇到很多的数据,要进行数据处理的话,我们就可以用正则表达式将其一一筛选出来,取我们有用的内容。
用法详解

  正则表达式都是要有一个匹配模式的,不然我们怎么去筛选呢,下面列举一下常用的匹配模式:

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

  如果我们实际处理问题这些还不够详细的话,可以点我去官网查看详情。

  • re.match函数
      re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

  函数语法就是下面这个样子:

re.match(pattern, string, flags=0)

  其中,pattern是指匹配的正则表达式,string指的是要匹配的字符串,最后的flags是指标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

  • 最常规的匹配
      现在我们看一些最常见的匹配,让我们熟悉一下。
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}.*Demo$', content)
print(result)
print(result.group())
print(result.span())

  上面的代码,先引用re,然后写了一个content变量里面有很多的参数,之后打印出来这个长度41。
  第五行我们就开始写正则表达式了,根据上面的表,<font color="red">^</font>表示了字符串的开头,所以说我们写之前先把这个写上,然后就特指了Hello,注意这里就写具体点,要写<font color="red">^w</font>,这样程序就不知道你是从哪个字母开始的了,所以指明了是从Hello开始的,然后有一个空格,我们就<font color="red">s</font>一下,然后遇到几个数字我们就<font color="red">d</font>几下,到了后面的“4567”那里,如果我们懒得整4个<font color="red">d</font>的话,我们就先写一个<font color="red">d</font>然后在后面用大括号写上几个,就代表了这里有几个数字,之后我们空格<font color="red">s</font>一下,遇到了World_This这里,这里刚刚好是10个字符,我们就用10个<font color="red">w</font>来表示,最后后面的一堆空格跟字母,我们直接用<font color="red">.</font>代替,然后再用<font color="red">*</font>匹配那些表达式,最后再说清楚是在哪个字符那里结尾,加上美元符<font color="red">$</font>结尾就可以了。
  第六行就是打印我们刚才写的正则表达式,里面有个span就是长度,匹配结果还有一些别的方法,比如第七行的group就是返回我们过滤的结果,最后一行列举的方法是长度,这个在第六行也有的:
8Yaz.png

  • 泛匹配
      刚才使用的就是最常规的匹配,可以看到每个字符都要表示一下很麻烦,很啰嗦,我们可以用<font color="red">.</font>来代替所有的字符串:
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())

  还是刚才的那段话,我们这次用<font color="red">.</font>和<font color="red">*</font>来表示中间所有的空格呀,数字呀还有字母下划线这些,当然了这样的三种输出结果和刚才的是一样的:
8uNt.png
  可以看到匹配结果都没有变,如果我们没有特殊要求,可以用<font color="red">.</font>和<font color="red">*</font>来匹配所有的。

  • 匹配目标
      现在我们还是刚才那段话,我们只要中间的数字123456看怎么写:
import re

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

  然后我们看看打印结果:
895C.png
  可以看到,代码里面把数字用括号扩了两次,然后在打印的时候分别打印了group(1)和group(2),那么group(1)就是代码里的第一个<font color="red">(d+)</font>,也就是数字123,第二个同理就是4567,所以在代码里面的两次<font color="red">(d+)</font>之间是有一个<font color="red">s</font>空格的,因为我原话中就是有空格的。

  • 贪婪匹配
      还是刚才那段话,我们用另一种写法把数字打印出来,来理解一下什么是贪婪匹配
import re

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

  跟前面有所不同的就是我们现在把1234567合在一起了方便我们理解贪婪匹配,当然我们打印的span方法就会少一个字节。直接看打印结果吧:
8EL3.png
  按道理说我们打印的group(1)对应着代码里面就应该是1234567,但是却只打印了一个7,原因是前面的123456都被<font color="red">.</font>所匹配了,但是既然写了<font color="red">(d+)</font>那么就要意思意思,所以程序就会给我们把前面的123456都算成<font color="red">.</font>的,最后留了一个7给<font color="red">(d+)</font>,所以<font color="red">.*</font>太贪婪了,哈哈

  • 非贪婪匹配
      现在看一下非贪婪匹配,可以结合上面说<font color="red">.</font>将所有的数字都拿走了,现在我们就要用非贪婪匹配把<font color="red">.</font>限制一下:
import re

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

  我们可以看到,基本没什么变化,但是在前面的<font color="red">.</font>后面加了一个<font color="red">?</font>,这里的问好是什么意思呢,感觉看看上面的表格:匹配0个或1个由前面的正则表达式定义的片段,非贪婪模式,那么就是说在非贪婪模式的时候用这个<font color="red">?</font>,这里的<font color="red">.?</font>就是尽可能匹配非常少的字符。看看打印结果:
8ftB.png
  这里就看到了打印结果就是我们想要的。

  • 匹配模式
      现在看一下换行符的问题:
import re

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

  可以看到代码里面我们使用了换行,然后匹配的时候还是老一套,看看能不能打印出来:
8T3W.png
  可以看到打印结果是None,也就是说没有匹配到,我们要在代码里面加上一个方法就行了,因为<font color="red">.</font>在上面说了是匹配任意字符的,除了换行符:

import re

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

  这次我们再来看看打印结果:
8sbg.png
  可以看到该有的都有了,这样换行符也有了,但是注意在打印的result里面有一个<font color="red">nis</font>,按理说我们是只有一个is的,这个<font color="red">n</font>参照上面的表就说了是一个换行符。

  • 转义
      几种的匹配模式我们都搞定了,但是我们发现一个问题,在结尾时用到的美元符,如果在我们的原语句中也有美元符该怎么表示呢,如果我们写个美元符就会给我们终止掉,这时候我们就需要将美元符进行转义了:
import re

content = 'fish is $5.00'
result = re.match('fish is $5.0', content)
print(result)

  上面我们直接把原话照搬进来,运行结果就是none,看来如果里面有特殊字符我们直接写是不行的:
8ViO.png
  解决方法就是在特殊字符里面加两个反斜杠:

import re

content = 'fish is $5.0'
result = re.match('fish is \$5\.0', content)
print(result)

  这时候我们再看看打印结果:
8WeT.png
  可以看到正常输出了。
  总结:在正则表达式里面尽量使用泛匹配,使用括号得到匹配目标、尽量使用非贪婪模式、有换行符就用re.S

  • re.search
      刚才说的那个re.match方法是必须说明开头和结尾的,也就是不能从中间插入。如果遇到下面这样的情况就不可以用了:
import re

content = 'python 5433 Hello 1234567 World_This is a Regex Demo linux 2jad'
result = re.match('^Hello.*?(/d).*?Demo$',content)
print(result)

  上面这段代码表面看起来是没有啥问题的,但是我们发现这个Hello是在原语句的最中间的,然后re.match是只能用开头的来匹配的
8bf1.png
  这时候我们把方法换成re.search就可以了:

import re

content = 'python 5433 Hello 1234567 World_This is a Regex Demo linux 2jad'
result = re.search('Hello.*?(\d+).*?Demo', content)
print(result)
print(result.group(1))

  这样我们就可以正常输出了:
80pp.png
  总结:为了匹配方便,能用search就用search,不能就用match,总之灵活应用吧。

匹配演练

  现在来实际演示一段html代码:

import re

html = '''< a
href = "javascript:;"
title = "周杰伦"
class ="singer_name" > 周杰伦 < / a >
< / div >
< div
class ="songlist__album" >
< a
href = "javascript:;"
title = "等你下课" > 等你下课 < / a >'''
result = re.search('title.*?"(.*?)".*?title.*?"(.*?)"', html, re.S)
if result:
    print(result.group(1), result.group(2))

  上面是我从qq音乐找的一段源代码,可以看看我写的正则表达式,能用.*?的就用,然后把需要的内容用括号括起来,然后打印两组括号即可:
8CN9.png

  • re.findall
      搜索字符串,以列表的形式返回全部能匹配的字符串。
import re

html = '''<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200425/59961.html'>2019年高分剧情《7号房的礼物》BD中英双字幕</a><br/>
</td>
<td width="15%" class="inddline"><font color=#FF0000>2020-04-24</font></td>
</tr>
<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200424/59958.html'>2019年高分获奖剧情《看不见的女人》BD中英双字幕</a><br/>
</td>
<td width="15%" class="inddline"><font color=#FF0000>2020-04-24</font></td>
</tr>
<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200424/59957.html'>2020年剧情动作《我们永不言弃》HD国语中字</a><br/>
</td>
<td width="15%" class="inddline"><font color=#FF0000>2020-04-23</font></td>
</tr>
<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200423/59954.html'>2019年获奖奇幻《匹诺曹/木偶奇遇记》BD中英双字幕</a><br/>
</td>'''
result = re.findall("href='(.*?)'.*?href='(.*?)'>(.*?)</td>", html, re.S)
print(result)
print(type(result))
for result in result:
    print(result)
    print(result[0], result[1], result[2])

  我们可以看到打印的结果是一个list的类型,都给我们加了中括号,我们可以把元组都去掉:
8GKN.png
  不过有一说一这个写法真的很麻烦,还要转换,我们感受一下即可,现在看看re.sub这种简单方法:

  • re.sub
      替换字符串中每一个匹配的字符串然后返回替换后的字符串:
import re

content = 'ajsdajs wwiiix isipa 1238767 kskksu skkska_q'
result = re.sub('\d+', '', content)
print(result)

  上面这个代码里面,我就是把所有的数字都替换成了空的字符,所以打印出来就是下面的样子:
8JU7.png
  这个也可以替换,这里就不演示了,重点说一下,如果我们替换的内容是原来的类型该怎么办:

import re

content = 'ajsdajs wwiiix isipa 3238767 kskksu skkska_q'
result = re.sub('(\d+)', '\1 8910', content)
print(result)

  上面这个写法里面的1就是转义的意思,这个r的意思就是把原来的也带上,如果不写r的话,那么就会变成8910:
8Ut5.png
  回到刚才的电影网站的例子,我们可以把所有的a标签都删除掉:

import re

content = '''<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200425/59961.html'>2019年高分剧情《7号房的礼物》BD中英双字幕</a><br/>
</td>
<td width="15%" class="inddline"><font color=#FF0000>2020-04-24</font></td>
</tr>
<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200424/59958.html'>2019年高分获奖剧情《看不见的女人》BD中英双字幕</a><br/>
</td>
<td width="15%" class="inddline"><font color=#FF0000>2020-04-24</font></td>
</tr>
<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200424/59957.html'>2020年剧情动作《我们永不言弃》HD国语中字</a><br/>
</td>
<td width="15%" class="inddline"><font color=#FF0000>2020-04-23</font></td>
</tr>
<tr>
<td width="85%" height="22" class="inddline">
·[<a href="/html/gndy/dyzz/index.html">最新电影下载</a>]<a href='/html/gndy/dyzz/20200423/59954.html'>2019年获奖奇幻《匹诺曹/木偶奇遇记》BD中英双字幕</a><br/>
</td>'''
content = re.sub('<a.*?>|</a>', '', content)
# print(content)
results = re.findall('](.*?)<br/>', content, re.S)
# print(results)
for result in results:
    print(result.strip())

  我们首先把所有的a标签删掉,然后匹配到我们的电影名字,最后再把结果专成一列:
8Z6F.png

  • re.compile
      最后这个方法就是把正则表达式编译成正则表达式对象的方法,前面都是把正则表达式变成了字符串,如果后期我们想要复用的话该怎么办呢。
import re

one = 'Hello 123 4567 World_This is a Regex Demo'
two = re.compile('Hello.*Demo', re.S)
three = re.match(two, one)
print(three)

  上面的操作也是可以打印出来的但是这样写是方便以后再调用这个字符串,我们不可能又去重新写一下,可以看看结果:

import re

one = 'Hello 123 4567 World_This is a Regex Demo'
two = re.compile('Hello.*Demo', re.S)
three = re.match(two, one)
four = re.match('^H.*?o$',one,re.S)
print(four)
print(three)

  看下结果:
HHiA.png
  本文到此结束,需要多多联系。

最后修改:2020 年 05 月 01 日 09 : 55 AM
请俺喝杯咖啡呗