《零基础入门学习Python》第058讲:论一只爬虫的自我修养6:正则表达式2

上一节课我们通过一个例子(匹配 ip 地址)让大家初步了解到正则表达式的魔力,也让大家充分了解到学习正则表达式是一个相对比较困难的事情。所以这一节课我们将继续学习 正则表达式的语法。

我们依稀还记得在Python中,正则表达式是以字符串的形式来描述的,正则表达式的强大之处在于特殊符号的应用,我们上节课举了例子,例如 点号(.),在正则表达式中表示匹配除了 换行符之外的任何字符,它就是一个特殊的字符。正是这些特殊符号,使得一个正则表达式可以匹配一个复杂的规则,而不仅仅是匹配一个字符串,如果你只需要匹配一个字符串,那用 find() 方法就可以了。

我这里给大家准备了一个列表,Python3 正则表达式特殊符号及用法(详细列表)。

这里罗列了python3 所支持的所有正则表达式的特殊符号以及具体的含义,在难以理解的地方,用斜体举了例子给大家看,方便大家理解,大家可以将这个文章收藏起来,以后用到的时候查询就可以了,切记不要死记硬背,因为根本背不住,如果背错了更扎心。

大家看到这里,可能就会犯嘀咕了:“好歹我也是见过世面的人啊,为了查找一个字符串,有必要掌握这么多规则吗?”。

实话说,没必要。我这里只是给大家做一个总结,我们正常使用的情况下,只是使用这里的一小部分,另外一大部分只是为了应对突发情况而准备的。例如某一天,你心血来潮,想为你的规则再增加复杂一点的规则,那么这里边正则表达式就可以应付自如了。一定要记住的是,这里面的东西不要去背,多做练习才是重要的啊。你掌握的东西才是你的,背下来的东西过两天就不是你的了。

特殊符号是由两部分组成的,一部分是 元字符。(例如我们上节课讲的 点号(.),方括号([]),反斜杠(\)等)。

所有的元字符包括:.   ^    $     *     +     ?     { }     [ ]     \     |     ( )  

另一部分就是 反斜杠加上普通符号组成的特殊符号,它拥有特殊的含义。

首先来谈谈元字符:

点号(.):是匹配除了换行符之外的任何字符。

| :就相当于逻辑或,学过C语言的同学们都知道,这就是按位或。A|B 表示匹配正则表达式 A或者B

例如:

 
  
  1. >>> import re

  2. >>> re.search(r"Python(E|F)", "PythonE")

  3. <_sre.SRE_Match object; span=(0, 7), match='PythonE'>

  4. >>> re.search(r"Python(E|F)", "PythonF")

  5. <_sre.SRE_Match object; span=(0, 7), match='PythonF'>

  6. >>> re.search(r"Python(E|F)", "PythonD")

  7. >>>

托字符(^):定位匹配,匹配字符串的开始位置(即确定一个位置)。

例如:

 
  
  1. >>> re.search(r"^Python", "I love Python")

  2. >>>

  3. >>> re.search(r"^Python", "Python, I love")

  4. <_sre.SRE_Match object; span=(0, 6), match='Python'>

跟托字符(^)对应的就是 美元符号($),$ 匹配输入字符串的结束位置.

 
  
  1. >>> re.search(r"Python$", "Python, I love")

  2. >>>

  3. >>> re.search(r"Python$", "I love Python")

  4. <_sre.SRE_Match object; span=(7, 13), match='Python'>

我们刚刚提到了值组,就是用小括号括起来的,我们上节课也用过了,用小括号括起来跟数学的括号是一样的,把一个东西当做一个整体,那么就把它括起来。

接下来是史上最困难、最复杂的 反斜杠(\),反斜杠在正则表达式中应用是最广泛的,它既可以将一个普通的字符变为特殊字符(这部分内容下节课继续讲解),同时也可以解除元字符的特殊功能,这在上节课已经讲过,例如 \. 匹配的就不是除换行符之外的任何字符了,他匹配的就是一个点(.)了。

如果在反斜杠后面加的是数字,那么还有两种表示方案:

①如果跟着的数字是 1~99,就表示引用序号对应的值组所匹配的字符串,其中序号所对应的值组:为 \ 前面的值组,\序号必须在对应的值组的正后面,序号为第几个值组。

例如:

 
  
  1. >>> re.search(r"(Python)\1", "I love Python")

  2. >>>

  3. >>> re.search(r"(Python)\1", "I love PythonPython")

  4. <_sre.SRE_Match object; span=(7, 19), match='PythonPython'>

上面的(Python)是第一个值组(序号是从1开始计算的,因为0表示一个八进制数),所以 \1,且\1 表示Python,其实 r'(Python)\1' 就等于 'PythonPython'。

 
  
  1. >>> re.search(r"(love)(Python)\1", "I lovelovePythonPython") #这样\1 是找不到 (love)的,\序号必须在对应值组的正后方

  2. >>>

  3. >>> re.search(r"(love)\1(Python)", "I lovelovePythonPython")

  4. <_sre.SRE_Match object; span=(2, 16), match='lovelovePython'>

  5. >>> re.search(r"(love)(Python)\2", "I lovelovePythonPython")

  6. <_sre.SRE_Match object; span=(6, 22), match='lovePythonPython'>

  7. >>> re.search(r"(love)\1(Python)\2", "I lovelovePythonPython")

  8. <_sre.SRE_Match object; span=(2, 22), match='lovelovePythonPython'>

并不是要求全部都要是值组,是要求 \序号 匹配的是值组:

 
  
  1. >>> re.search(r"(I )love(Python)\2", "I lovePythonPython.com")

  2. <_sre.SRE_Match object; span=(0, 18), match='I lovePythonPython'>

②如果跟着的数字是 0 或者 3位的数字,那么它是一个八进制数,表示的是这个八进制数对应的 ASCII 码对应的字符。

例如:字符 0 对应的十进制数为 48,对应的八进制数为 60,这里要三位数,就是060,所以:

 
  
  1. >>> re.search(r"\060", '0')

  2. <_sre.SRE_Match object; span=(0, 1), match='0'>

 
  
  1. >>> re.search(r"I love Python\060", 'I love Python0')

  2. <_sre.SRE_Match object; span=(0, 14), match='I love Python0'>

接下来要介绍的元字符是 中括号([ ]),这是生成一个字符类,事实上,字符类就是一个字符集合的意思,另外,值的注意的是:被中括号包含在里面的元字符都会失去特殊功能,就像 反斜杠加上一个元字符是一样的,举例:

 
  
  1. >>> re.search(r"[.]", 'I love Python.com')

  2. <_sre.SRE_Match object; span=(13, 14), match='.'>

字符类的意思就是将它里面的内容都当做普通的字符看待,除了几个特殊的字符:

①小横杠(-),我们用它表示范围,我们上节课讲过,这节课我们讲一个其他的方法:re.findall()

re.findall

在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个列表返回。

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

参数:

参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
 
  
  1. >>> re.findall(r"[a-z]","12a32bc43jf3")

  2. ['a', 'b', 'c', 'j', 'f']

findall 和 search 相比,似乎更符合我们的需求,但是当遇到值组时,findall 也会有陷阱,我们后面会讲解。

②反斜杠(\),把反斜杠放在字符类[ ]中,它也不是表示本身,这样会报错,

 
  
  1. >>> re.findall(r"[\]","12a32bc43jf3")

  2. Traceback (most recent call last):

  3. File "", line 1, in

  4. re.findall(r"[\]","12a32bc43jf3")

  5. File "D:\ProgramFiles\Anaconda3\lib\re.py", line 213, in findall

  6. return _compile(pattern, flags).findall(string)

  7. File "D:\ProgramFiles\Anaconda3\lib\re.py", line 293, in _compile

  8. p = sre_compile.compile(pattern, flags)

  9. File "D:\ProgramFiles\Anaconda3\lib\sre_compile.py", line 536, in compile

  10. p = sre_parse.parse(p, flags)

  11. File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 829, in parse

  12. p = _parse_sub(source, pattern, 0)

  13. File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 437, in _parse_sub

  14. itemsappend(_parse(source, state))

  15. File "D:\ProgramFiles\Anaconda3\lib\sre_parse.py", line 545, in _parse

  16. source.tell() - here)

  17. sre_constants.error: unterminated character set at position 0

反斜杠在字符类里,表示Python 字符串的转义符。

在字符串里,我们都知道 \n 表示回车的意思,所以:

 
  
  1. >>> re.findall(r"[\n]","12a32\nbc43jf3")

  2. ['\n']

③托字符 ^,在字符类[ ]里,表示‘除了’(取反)的意思,但是要注意的是,这个托字符 ^ 必须放在最前面:

 
  
  1. >>> re.findall(r"[^a-z]","12a32bc43jf3")

  2. ['1', '2', '3', '2', '4', '3', '3']

如果放在后面,就是表示匹配它本身:

 
  
  1. >>> re.findall(r"[a-z^]","12a32bc^^43jf3")

  2. ['a', 'b', 'c', '^', '^', 'j', 'f']

最后要介绍的元字符 是用于做重复的事情,例如我们上节课讲到的 大括号{ },如{M,N}(要求M,N均为非负整数,且M<=N)表示前面的内容匹配 M~N次。

 
  
  1. >>> re.search(r'Python{3}', 'I love Pythonnn')

  2. <_sre.SRE_Match object; span=(7, 15), match='Pythonnn'>

 
  
  1. >>> re.search(r'(Python){3}', 'I love PythonPythonPython')

  2. <_sre.SRE_Match object; span=(7, 25), match='PythonPythonPython'>

在正则表达式中,需要注意的是,大家在写编程的时候,可能会注意美观,可能会多加一些空格,但是在正则表达式里面,你千万不能加空格,例如:

 
  
  1. >>> re.search(r'(Python){1,5}', 'I love PythonPythonPython')

  2. <_sre.SRE_Match object; span=(7, 25), match='PythonPythonPython'>

 
  
  1. >>> re.search(r'(Python){1, 5}', 'I love PythonPythonPython')

  2. >>>

因为空格会被解析为一个正则表达式。

最后,我们来谈一下几个特殊的:

①星号(*):匹配前面的子表达式零次或多次,等价于 {0,}

②加号(+):匹配前面的子表达式一次或多次,等价于 {1,}

③问号(?):匹配前面的子表达式零次或一次,等价于 {0,1}

在正则表达式中,如果实现条件一样,推荐大家使用左边的 *  +  ?这三个,不要使用 大括号{ },因为:首先,星号、加号、问号更加简洁;其次,正则表达式内部会对这三个符号进行优化,效率会比使用大括号要高一些。

最后,我们来谈一下 贪婪 和 非贪婪。

关于我们这个 重复 的操作,有一点需要注意的就是:正则表达式默认是启用 贪婪 的模式来进行匹配的,那什么是 贪婪 呢?贪婪就是贪心,也就是说,只要在符合的条件下,它会尽可能多的去匹配,例如前面的 re.search(r'(Python){1,5}', 'I love PythonPythonPython') 就会直接匹配到3个 Python。

我们来看一个现实中的案例。假设我们想 匹配  

 
  
  1. >>> s = " I love Python.com"

  2. >>> re.search(r"<.+>", s)

  3. <_sre.SRE_Match object; span=(0, 46), match=' I love Python.com'>

<.+> 表示以 < 开头,以 > 结尾,重复 . 号 1次或多次。最后匹配了字符串全部。很明显,这不是我们想要的结果。

因为贪婪会在条件符合的情况下尽可能多的去匹配,既然是这样,我们就必须启用 非贪婪模式才可以,那么非贪婪模式怎么样启用呢?

很简单,在表示重复的元字符后面再加上一个问号,这时候,问号就不代表0次或1次了,而是表示启用非贪婪模式:

 
  
  1. >>> re.search(r"<.+?>", s)

  2. <_sre.SRE_Match object; span=(0, 6), match=''>

好了,到这里,正则表达式的所有元字符我么就讲解完毕了,大家课后一定要勤加练习。

你可能感兴趣的:(python零基础,python)