Python spider入门_1 正则表达式学习笔记
Python spider入门_1 正则表达式学习笔记
0 引言
一些不错的学习网站(也是本文的参考网站):
(主要参考)CSDN博主笔记:正则表达式学习笔记(超级详细!!!)| 有用的小知识-CSDN博客
(IMPORTANT)正则表达式练习网站:regex101: build, test, and debug regex
菜鸟教程:正则表达式 – 语法 | 菜鸟教程 (runoob.com)
python re模块运用:Python 正则表达式 | 菜鸟教程 (runoob.com)
1 什么是正则表达式
总的来说,正则表达式就是一组由字母和符号组成的特殊文本,这个特殊文本规定了找到的文本是什么样的,它会去给定的一个大的文本里去找出符合规定的文本。
一般无特殊要求,都会对待匹配的文本从左到右匹配。
2 正则表达式语法
2.1 基本匹配
正则表达式是执行搜索的格式,由一些符号,字母和数字组成,例如下面这个,最简单的:
2.2 元字符
正则表达式主要依赖于元字符,元字符不代表它们自身意思,有特殊含义,一些元字符在方括号内还有特殊意思。
大概的元字符有这些:
元字符 | 描述 |
---|---|
. | 匹配任意单个字符(除了换行符)。 |
[ ] | 字符种类。匹配方括号内的任意字符。 |
[^ ] | 否定的字符种类。匹配除了方括号内的任意字符。 |
* | 匹配>=0个重复的在*号之前的字符。 |
+ | 匹配>=1个重复的在+号前的字符。 |
? | 标记?之前的字符为可选。 |
{n,m} | 匹配num个大括号之前的字符或字符集(n <= num <= m) |
(xyz) | 字符集,匹配与xyz完全相等的字符串 |
| | 或运算符 |
\ | 转义字符,用于匹配一些保留的字符 |
^ | 从开始行开始匹配 |
$ | 从末端开始匹配 |
2.2.1 点运算符 .
. 可以匹配单个字符,可以看成通配符,但它不匹配换行符。
2.2.2 字集符 [ ]
也叫字符类。方括号指定一个字符集。方括号内使用连字符来指定字符集的范围。在方括号中的字符集不关心顺序。例如,表达式[Tt]he匹配The和the(即此方括号是一个域,这里可取T或t)。
小tip:如果只搜索 . 就得用转义字符 \ ,在[ ]中,不论是直接使用点还是通过转义字符都可以直接表示。
2.2.3 否定字符集
相当于集合中的补集,即全集去掉了某一部分的集合
2.2.4 * 号
匹配 * 前面的那一个字符 0 次或多次
例如,匹配 / zooo*
符合的字符串有 zoo,zooo,zoooo,即只看最后一个o,它可以是0次的,也可以是多次的
再例如,以下的表达式匹配了所有以小写字母开头的字符串
变式:
查找特定字符且前后可带空格:
.* 搭配:
2.2.5 + 号
与 * 号类似,但要求规定字符(就是 + 前面的那一个字符)出现大于等于 1 次。
例如,
与 * 号对比,
2.2.6 ? 号
在正则表达式中,元字符 ?表示规定的那一个字符(即在 ?前面的那一个字符为可选),即出现 0 次或 1 次。如 c?at 匹配 cat 或者 at
2.2.7 { } 号
在正则表达式中 { } 是一个量词,常用来限定一个或一组字符可重复出现的次数。如,[a-z]{3,4} 匹配最少 3 个最多 4 个小写字母。
倘若省略第二个参数,则匹配大于等于 3 个的小写字母
倘若把逗号也省略,则重复匹配固定的次数,即匹配 3 个小写字母
2.2.8 (…) 特征标群
你可以把这个当成小学二年级学过的小括号,即括号中的东西是个整体。例如,(ab)* 就是匹配空或者多个 ab,如果没有使用括号,则匹配连续出现空或者多个 b
在比如说 { } 中的表示前面那一个字符重复几次的,如果加上 ( ) ,就表示括号内的要重复几次
未加括号时:
加括号后:
2.2.9 | 或运算符
这个太简单了,就是 or
2.2.10 转码特殊字符 \
用于指定这些特殊字符,在一个句子中想要匹配一些特殊字符时需要通过转码符来消除这些字符的特殊性。
例如,你只是想要匹配句子中的句号 “.”,但它是有特殊用法的特殊字符,贸然匹配只会出现你不会想要得到的结果。
此时,你需要转义字符:
当然,他的用法是多样的:
2.2.11 锚点 ^ 号和 $ 号
在正则表达式中,想要匹配指定开头或结尾的字符串就要用到锚点。^ 指定开头,$ 指定结尾。(每段话的)
不指定开头:
指定开头:
不指定结尾:
指定结尾:
2.3 简写字符集
一些常用的字符集有简写形式,就像数学中的N,表示全体自然数,不用写成 (0,1,2,3,…)
简写 | 描述 |
---|---|
. | 除换行符外所有字符 |
\w | [a-zA-Z0-9],匹配所有字母数字 |
\W | [^\w],匹配所有非字母数字 |
\d | [0-9],匹配数字 |
\D | [^\d],匹配非数字 |
\s | [\t\n\f\r\p{Z}],匹配所有空格字符 |
\S | [^\s],匹配所有非空格字符 |
\f | 匹配一个换页符 |
\n | 匹配一个换行符 |
\r | 匹配一个回车符 |
\t | 匹配一个制表符 |
\v | 匹配一个垂直制表符 |
\p | 匹配CR/LF(等同于\r\n),用来匹配 DOS 行终止符 |
2.4 零宽度断言(前后预查)
先行断言和后发断言都是用来做判断的,就是一个条件,也可以说是一个约束吧。
先行断言:目标是前面的东西,找到的东西后面要符合要求
后发断言:目标是后面的东西,找到的东西前面要符合要求
例如,我们要获得所有跟在 $ 后面的数字,注意理解,我们现在要的是数字,而要求是数字前面有 $ 符号,此时,我们就应该用后发断言,在学完这四种断言后,你就会知道,这里要用正后发断言 (?<=$)[0-9]*
一共有如下四种零断言:
符号 | 描述 |
---|---|
?= | 正先行断言 - 存在 |
?! | 负先行断言 - 排除 |
?<= | 正后发断言 - 存在 |
? | 负后发断言 - 排除 |
2.4.1 正先行断言
?=… 为正先行断言,表示第一部分表达式之后必须跟着 ?=… 定义的表达式。
2.4.2 负先行断言
?!… 为负先行断言,筛选所有匹配结果,筛选条件为其后不跟随着断言中定义的格式。
2.4.3 正后发断言
?<=… 为正后发断言,筛选所有匹配结果,筛选条件为其前跟随着断言中定义的格式。
思考:这里被选中的 fat 是怎么回事
理所当然的认为了这里的 mat|fat 是一个整体,但这种想法是错误的,这里 (?<=[T|t]he\s)mat 是一个整体,fat 是另一个整体
而正确的写法应该是 (?<=[T|t]he\s)(mat|fat)
2.4.4 负后发断言
?<!… 为负后发断言,筛选所有匹配结果,筛选条件为其前不跟随着断言中定义的格式。
2.5 标志
标志也叫模式修正符,也是正则表达式的一部分。
你可以理解为模式的打开与关闭,也可以理解成不同的搜索模式吧。
标志 | 描述 |
---|---|
i | insensitive,大小写不敏感,即忽略大小写 |
g | global,全局搜索 |
m | 多行修饰符:锚点 ^ , $ 工作在每行的起始 |
标志符在学习网站的右边可以选择:
2.5.1 忽略大小写 insensitive
顾名思义,大小写将不影响搜索结果
2.5.2 全局搜索 global
如果关闭全局搜索,那么在匹配到第一个正确结果后它就不会再继续搜索,并只返回第一个正确匹配,如
(注意:此时标志 i 已经关闭)
但打开全局搜索,它就不止匹配第一个,在搜索到第一个后,它会继续往下搜索,知道查完全文
2.5.3 多行修饰符 multiline
执行多行匹配,像之前学到的 ^ , $ 如果想在每行的开头和结尾生效,则需要用的多行修饰符 m
例如,表达式 at.$
打开 m 和 g 时:
打开 m,关闭 g 时:
打开 g,关闭 m 时:
关闭 m 和 g 时:
2.6 贪婪匹配与懒惰匹配
正则表达式默认时贪婪模式,此模式匹配尽可能长的字符串。用 ? 将贪婪模式转为懒惰模式。
贪婪匹配:
- 当前模式下找到 5 个匹配结果(长匹配)
懒惰匹配:
- 当前模式下找到的 14 个匹配结果(短匹配)
2.7 正则表达式格式
通过上述学习,相信你也发现了正则表达式是有格式要求的,而使用练习网站时,它帮助我们省去了一部分,而当我们写代码时,常常要写出完整格式。在此,我们将整个正则表达式的格式再次声明:
- 正则表达式的完整格式应该是:/… …/… …
- 第一个省略号代表的部分为我们所要写的匹配语句,也就是我们之前一直在学的那些字符集,断言之类的
- 第二个省略号的部分为标志,就是 g,m,i 之类的那些(标志可不止这三种,只是我们只学了这三种)
- 完整正则表达式的例子:
- /(.*?at)/gm 来自懒惰匹配的部分
- /(?<=[T|t]he\s)(mat|fat)/gm 来自断言的部分
3 正则表达式的应用
3.1 验证一个用户名
/^[a-z0-9_-]{3,15}$/
想想看这个正则表达式要求的用户名要满足哪些条件
3.2 验证电话号码
/^(+(\d{2}))-(\d{11})$/
正确格式:+xx-xxxxxxxxxxx
3.3 验证 IPv4 地址
/^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9]).(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9]).(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9]).(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])$/
觉得复杂又晦涩难懂吗,其实只用分析其中的一段就好,因为 IPv4 地址都是 xxx.xxx.xxx.xxx,其中 xxx 的取值为 0~255,所以,只是四段相同的 (1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9]) 中间用“ . ”来隔开,不要忘了给“ . ”加上转义符
接下来,对于 (1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9]) 分析:(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])
\d 为 0~9 中的数,等同于 [0-9]
那么 1\d{2} 代表 100~199
2[0-4]\d 代表 200~249
25[0-5] 代表 250~255
[1-9]\d 代表 10~99
[1-9] 代表 1~9
自此,1~255的取值便被限定
接下来,把四个这玩意用 \. 隔开,加上“ ^ ”与“ $ ”便做出了 IPv4 地址验证。