《零基础入门学习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 "<pyshell#47>", line 1, in <module>

  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。

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

复制代码
  1. >>> s = "<html><title> I love Python.com</title></html>"

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

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

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

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

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

复制代码
  1. >>> re.search(r"<.+?>", s)

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

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

相关推荐
chao_78926 分钟前
二分查找篇——寻找旋转排序数组中的最小值【LeetCode】
python·线性代数·算法·leetcode·矩阵
金玉满堂@bj43 分钟前
PyCharm 中 Python 解释器的添加选项及作用
ide·python·pycharm
程序员三藏1 小时前
如何使用Pytest进行测试?
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
随心点儿1 小时前
使用python 将多个docx文件合并为一个word
开发语言·python·多个word合并为一个
不学无术の码农1 小时前
《Effective Python》第十三章 测试与调试——使用 Mock 测试具有复杂依赖的代码
开发语言·python
sleepybear11132 小时前
在Ubuntu上从零开始编译并运行Home Assistant源码并集成HACS与小米开源的Ha Xiaomi Home
python·智能家居·小米·home assistant·米家·ha xiaomi home
纪伊路上盛名在2 小时前
(鱼书)深度学习入门1:python入门
人工智能·python·深度学习
夏末蝉未鸣012 小时前
python transformers笔记(TrainingArguments类)
python·自然语言处理·transformer
德育处主任Pro2 小时前
「py数据分析」04如何将 Python 爬取的数据保存为 CSV 文件
数据库·python·数据分析
咸鱼鲸2 小时前
【PyTorch】PyTorch中数据准备工作(AI生成)
人工智能·pytorch·python