正则表达式和re库

Posted Young的编程日记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正则表达式和re库相关的知识,希望对你有一定的参考价值。

基本语法

基本语法就直接搬运嵩天老师的课件了,这个没啥好说的,得多练才能熟!

正则表达式和re库

针对上面的图再补充几点:

  • .表示除了 \n换行符以外的任何字符

  • \s表示任何空白字符,包括空格。、制表符、换页符等,等价于[\f\n\r\t\v],( \f是换页符,\v是制表符)

  • \S表示任何的非空白字符,等价于[^\f\n\r\t\v]

举几个例子

 
   
   
 
  1. python+:对应字符串python,pythonn,pythonnn...

  2. p(y|yt|yth|ytho)?n:对应字符串pn,pyn,pytn,pythn,python

  3. py[th]on:对应字符串pyton,pyhon

  4. py<a href="#footnote-th"><sup>[th]</sup></a>?:对应字符串onpyon,pyaon,pybon...

  5. py{:3}n:对应字符串pn,pyn,pyyn,pyyyn

正则表达式和re库

正则表达式和re库

re库

compile方法

我们上面写的正则表达式,他其实只是符合正则表达式规则的字符串,字符串我们是无法用来匹配其他文本的,所以在Python中,我们需要利用 re库将符合正则表达式的字符串编译成正则表达式对象,然后用它来匹配原始文本最终获取所需要的匹配文本。

我们可以用 compile()方法将正则表达式文本编译成正则表达式对象,来看个例子

 
   
   
 
  1. >>> import re

  2. >>> rex = r'a.d'        #正则表达式文本

  3. >>> type(rex)

  4. <class 'str'>           #rex是字符串类型

  5. >>> text = 'and'        #原始文本

  6. >>> pattern = re.compile(rex)       #利用compile方法将正则表达式文本编译成正则表达式对象

  7. >>> pattern

  8. re.compile('a.d')

  9. >>> type(pattern)

  10. <class 're.Pattern'>                #这里可以看到使用compile方法后,rex的对象类型是正则表达式类型

  11. >>> r = pattern.match(text)         #利用match方法用正则表达式对象来匹配原始文本

  12. >>> r

  13. <re.Match object; span=(0, 3), match='and'>    

  14. >>> type(r)

  15. <class 're.Match'>                  #使用match方法返回的是一个match对象

  16. >>> r.group()                       #利用match对象的group方法获取匹配字符串

  17. 'and'

match方法

上面是一个完整的流程,但其实在日常编写中,我们可以直接利用match方法,省时省力,上面的代码等同于:

 
   
   
 
  1. >>> r = re.match(r'a.d',text)

  2. >>> r

  3. <re.Match object; span=(0, 3), match='and'>     #可以看到和上面代码效果是一样的

match方法是:从原始字符串的开始位置起匹配正则表达式,返回match对象

 
   
   
 
  1. re.match(pattern,string,flags)

  • pattern:正则表达式

  • string:待匹配原始文本

  • flags:控制标记

控制标也没啥好说的,最常用的就是re.S,来看下 match方法的使用以及它返回的match对象的属性方法。

另外要注意下,就是re库中要用原生字符串的形式,就是 r'string',因为很多字符比如 \b在ASCII码中是退格键的意义,然而在正则表达式中他是匹配边界的意思,所以我们要用原生字符串来防止反斜杠 \对字符串进行转义,这个记住就好了。

看个例子:

 
   
   
 
  1. >>> r = re.match(r'a.d',text)

  2. >>> r

  3. <re.Match object; span=(0, 3), match='and'>

  4. >>> type(r)

  5. <class 're.Match'>

  6. >>> r = re.match(r'a.b',text)

  7. >>> r

  8. >>> type(r)

  9. <class 'NoneType'>      #r的类型事空

从上面我们可以看到当match方法从原始字符串中获取匹配字符串时,他返回的是match对象,当他没有获取到匹配字符串时,他返回的是空。

再看

 
   
   
 
  1. >>> import re

  2. >>> r = re.match(r'a.d','and and')

  3. >>> r

  4. <re.Match object; span=(0, 3), match='and'>

  5. >>> r = re.match(r'a.d','and he is ...')

  6. >>> r

  7. <re.Match object; span=(0, 3), match='and'>

  8. >>> r.group()

  9. 'and'

  10. >>> r = re.match(r'a.d','this is and ...')

  11. >>> r

  12. >>> type(r)

  13. <class 'NoneType'>

从上面这个例子中我们可以看到,match方法是从原始字符串的开头进行匹配的,如果开头不匹配,那么就算你后面有匹配的字符串,他也获取不到。

再来看下match对象的属性

 
   
   
 
  1. >>> r = re.match(r'a.d','and he is ...and')

  2. >>> r

  3. <re.Match object; span=(0, 3), match='and'>

  4. >>> r.pos       #正则表达式搜索文本的开始位置

  5. 0

  6. >>> r.endpos    #正则表达式搜索文本的结束位置

  7. 16

  8. >>> r.string        #待匹配的原始文本

  9. 'and he is ...and'

  10. >>> r.re

  11. re.compile('a.d')   #匹配时使用的正则表达式

match对象的方法

 
   
   
 
  1. >>> r.start()   #获取的匹配字符串在原始字符串中的开始位置

  2. 0

  3. >>> r.end()     #获取的匹配字符串在原始字符串中的结束位置

  4. 3

  5. >>> r.span()    #返回(.start(),.end())

  6. (0, 3)

  7. >>> r.group()   #获取匹配后的字符串

  8. 'and'

  9. >>> r.group(0)  #同group(),还有group(1),group(2),但这个r变量没有,这个后面再说

  10. 'and'

search方法

我们发现match方法是从字符串开头进行匹配的,开头不匹配就获取不了,所以为了弥补这一缺陷,我们可以用 search()方法。

 
   
   
 
  1. re.search(pattern,string,flags)

search方法是从在原始字符串中搜索匹配正则表达式的字符串,如果有多个匹配的字符串,则返回第一个,同样的,search方法返回的也是match对象

看个例子

 
   
   
 
  1. >>> r = re.match(r'a.d','this is and and and')

  2. >>> r

  3. >>> type(r)

  4. <class 'NoneType'>      #可以看到,开头字符不匹配的话,match是获取不到字符串的

  5. >>> r = re.search(r'a.d','this is and and and')

  6. >>> r

  7. <re.Match object; span=(8, 11), match='and'>    #search方法是从开头进行搜索的,然后在寻找匹配字符串,所以search方法是可以获取后面的匹配字符串的,但是只获取第一个符合条件的字符串

  8. >>> r.group()

  9. 'and'              

其实我们通过 r.posr.endpos可以发现,match方法和search方法都是从开头搜索,到末尾结束的,但是match是在开头就进行匹配,而search是在搜索过程中匹配的。这就是造成他们之间差异的原因(个人理解)

findall方法

search方法只能获得第一个符合条件的字符串,那么如果我们想获取所有的满足条件的字符串呢,用 findall()方法

 
   
   
 
  1. re.findall(pattern,string,flags)

看个例子

 
   
   
 
  1. >>> r = re.findall(r'a.d','this is a and and and')

  2. >>> r

  3. ['and', 'and', 'and']

可以发现, findall方法可以获取所有的符合正则表达式的字符串,并且他返回的是一个列表。

匹配目标字符串

我们之前说过,group方法不仅有 geoup(0)还有 group(1), group(2),那么怎么获取 group(1)呢,很简单,往正则表达式中加括号 ()就行了。

看个例子:

 
   
   
 
  1. >>> text = 'my telephone number is 18888888888,my email is 888888@qq.com'

  2. >>> r = re.search(r'.*?(\d+).*?(\w+?@\w*?\.(com|cn))',text)

  3. >>> r

  4. <re.Match object; span=(0, 60), match='my telephone number is 18888888888,my email is 88>

  5. >>> r.group(0)

  6. 'my telephone number is 18888888888,my email is 888888@qq.com'

  7. >>> r.group(1)

  8. '18888888888'

  9. >>> r.group(2)

  10. '888888@qq.com'

  11. >>> r.group(3)

  12. 'com'

  13. >>> r.group(4)

  14. Traceback (most recent call last):

  15.  File "<pyshell#18>", line 1, in <module>

  16.    r.group(4)

  17. IndexError: no such group

贪婪模式

这里再说一下贪婪模式,re库默认是使用贪婪模式的,什么是贪婪模式呢,贪婪模式下 *, +这类扩展类操作符会尽可能的匹配更多的字符,从而导致覆盖掉我们想要字符。想要取消贪婪模式的话,只需要在 *+后面加个 ?就行了,比如 .*?, \d+?,这样他会让 *, +这类扩展类操作符匹配最小的长度。

看个例子:

 
   
   
 
  1. >>> text = 'my telephone number is 18888888888'

  2. >>> r = re.search(r'.*(\d+)',text)

  3. >>> r

  4. <re.Match object; span=(0, 34), match='my telephone number is 18888888888'>

  5. >>> r.group(1)

  6. '8'

  7. >>> r = re.search(r'.*?(\d+)',text)

  8. >>> r

  9. <re.Match object; span=(0, 34), match='my telephone number is 18888888888'>

  10. >>> r.group(0)

  11. 'my telephone number is 18888888888'

  12. >>> r.group(1)

  13. '18888888888'

sub、split函数

split()函数,将原始字符串按照正则表达式进行分割,返回列表类型

 
   
   
 
  1. re.split(pattern,tring,maxsplit=0,flags=0)

  • maxsplit:最大分割数,剩余部分作为一个元素输出。就把它理解为将符合条件的字符串取出来,然后进行分割

 
   
   
 
  1. >>> r = re.split(r'\d+','number_one:123456,number_two:987654')  #默认是都取出来,等同于maxsplit=2

  2. >>> r

  3. ['number_one:', ',number_two:', '']

  4. >>> r = re.split(r'\d+','number_one:123456,number_two:987654',maxsplit=1)   #只取一次字符串

  5. >>> r

  6. ['number_one:', ',number_two:987654']

sub()函数,将原始字符串中符合正则表达式的字符串全部替换,返回替换后的字符串。

 
   
   
 
  1. r = re.sub(pattern,repl,string,count=0,flags=0)

  • repl:替换匹配字符串的字符串

  • count:匹配的最大替换次数

 
   
   
 
  1. >>> re.sub(r'\d+','abc','number_one:123456,number_two:987654')

  2. 'number_one:abc,number_two:abc'

  3. >>> re.sub(r'\d+','abc','number_one:123456,number_two:987654',count=1)  #只替换一次

  4. 'number_one:abc,number_two:987654'

来个test,获取下方html中的歌手名以及歌曲名

 
   
   
 
  1. html = '''<div id="songs-list">

  2.    <h2 class="title">经典老歌</h2>

  3.    <p class="introduction">

  4.        经典老歌列表

  5.    </p>

  6.    <ul id="list" class="list-group">

  7.        <li data-view="2">一路上有你</li>

  8.        <li data-view="7">

  9.            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>

  10.        </li>

  11.        <li data-view="4" class="active">

  12.            <a href="/3.mp3" singer="齐秦">往事随风</a>

  13.        </li>

  14.        <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>

  15.        <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>

  16.        <li data-view="5">

  17.            <a href="/6.mp3" singer="邓丽君"><i class="fa fa-user"></i>但愿人长久</a>

  18.        </li>

  19.    </ul>

  20. </div>'''

代码:

 
   
   
 
  1. >>> import re

  2. >>> html = re.sub(r'<i.*?>|</i>','',html)   #将li标签去掉

  3. >>> print(html)

  4. <div id="songs-list">

  5.    <h2 class="title">经典老歌</h2>

  6.    <p class="introduction">

  7.        经典老歌列表

  8.    </p>

  9.    <ul id="list" class="list-group">

  10.        <li data-view="2">一路上有你</li>

  11.        <li data-view="7">

  12.            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>

  13.        </li>

  14.        <li data-view="4" class="active">

  15.            <a href="/3.mp3" singer="齐秦">往事随风</a>

  16.        </li>

  17.        <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>

  18.        <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>

  19.        <li data-view="5">

  20.            <a href="/6.mp3" singer="邓丽君">但愿人长久</a>

  21.        </li>

  22.    </ul>

  23. </div>

  24. >>> resoults = re.findall(r'<li.*?singer="(.*?)">(.*?)</a>',html,re.S)

  25. >>> resoults

  26. [('任贤齐', '沧海一声笑'), ('齐秦', '往事随风'), ('beyond', '光辉岁月'), ('陈慧琳', '记事本'), ('邓丽君', '但愿人长久')]

  27. >>> for resoult in resoults:

  28. ...     print(resoult[0],resoult[1])

  29. ...

  30. 任贤齐 沧海一声笑

  31. 齐秦 往事随风

  32. beyond 光辉岁月

  33. 陈慧琳 记事本

  34. 邓丽君 但愿人长久

再来个test,获取下方HTML中的邮箱:

 
   
   
 
  1. html = """

  2.        <style>

  3.            .qrcode-app{

  4.                display: block;

  5.                background: url(/pics/qrcode_app4@2x.png) no-repeat;

  6.            }

  7.        </style>

  8.        <div class="reply-doc content">

  9.            <p class="">34613453@qq.com,谢谢了</p>

  10.            <p class="">30604259@qq.com麻烦楼主</p>

  11.        </div>

  12.        <p class="">490010464@163.com<br/>谢谢</p>

  13.      """

代码:

 
   
   
 
  1. >>> resoults = re.findall(r'<p.*?>(\d+?@\w*?\.com).*?</p>',html,re.S)

  2. >>> resoults

  3. ['34613453@qq.com', '30604259@qq.com', '490010464@163.com']

  4. >>> for resoult in resoults:

  5. ...     print(resoult)

  6. ...

  7. 34613453@qq.com

  8. 30604259@qq.com

  9. 490010464@163.com


Python中的正则差不多就这些了,主要还是要多练,用得多了就熟了。

以上是关于正则表达式和re库的主要内容,如果未能解决你的问题,请参考以下文章

Python 爬虫正则表达式和re库,及re库的基本使用,提取单个页面信息

06 Python爬虫之Re(正则表达式)库

re库正则表达式基本使用

第三方库-正则re

Python 基础 - Day 5 Learning Note - 模块 之 标准库:RE (14) 正则表达式

正则表达式和re库