什么是正则表达式
正则表达式(Regular Expression),我一直觉得将regular译成“正则”是一件很荒唐的是,不仅体现在正则表达式,包括初接触神经网络时的概念正则化也是如此,其实就是标准化。regular expression就是标准表达式,目的就是提出一套标准规则,用来匹配字符串,以达到提取或过滤某种特定形式的字符串的作用。
正则表达式的作用
正则表达式十分强大,可以匹配任意的字符串。可以应用在各个方面,例如想提取一大段话中的邮箱地址,提取IP地址,提取电话号码,提取年月日等等,换言之,这些具有一定规律的字符串格式都可以用正则表达式来批量化提取。
- 邮箱地址
1 | import re |
运行结果
1 | <re.Match object; span=(0, 16), match='eric6465@163.com'> |
以上是我的四个邮箱,全部匹配正确。匹配的邮箱的关键在于@和顶级域名。
- IP地址
1 | import re |
运行结果
1 | <re.Match object; span=(0, 11), match='192.168.0.1'> |
前两个都是我们常见的合法的IP地址,后四个分别出现了一些错误,全部无法匹配。匹配IP地址的关键在于,IP地址由4个8位二进制码组成,其间用‘.’隔开,转换为十进制后,范围在0-255。若是1位,则0-9均可;若2位,则十位不能为0,个位任意;若3位,则百位为1-2,当百位为1时,剩余两位任意,当百位为2时,后两位最大值为55,即十位为0-4时个位任意,十位为5时个位只能为0-5。
- 电话号码
1 | import re |
运行结果
1 | <re.Match object; span=(0, 12), match='020-87114442'> |
匹配电话号码相对简单,只要注意是否有连字符‘-’,并且两边各有几位数字即可。
- 日期
这里我们尝试抓取某个网页内的日期信息
网页链接: 关于2020-2021学年度第一学期部分通选课停开的通知
网页截图:
代码:
1 | import re |
运行结果
1 | <re.Match object; span=(7049, 7059), match='2020-09-24'> |
由于是从网页中提取日期,而非判断是否是合法日期,所以我们只需要匹配“yyyy-mm-dd”即可,而不需要判断是否是闰年,是大月还是小月。
</br>
通过上述四个例子,想必我们已经知道了正则表达式的最大的作用,即可以提取我们所需的特定格式的字符串。下面我们来具体说说正则表达式的语法。
正则表达式的语法
- 普通字符
字符 | 描述 |
---|---|
[ABC] | 匹配包含ABC任意一个的单个字符 |
ABC | 匹配任意不包含ABC的单个字符 |
[A-Za-z0-9] | 匹配A-Z,a-z,0-9中任意一个的单个字符 |
\w | 匹配A-Z,a-z,0-9和下划线中任意一个的单个字符 |
</br>
- 非打印字符(常用)
字符 | 描述 |
---|---|
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\s | 匹配任意空格字符 |
</br>
- 特殊字符
字符 | 描述 |
---|---|
$ | 匹配结尾 |
() | 分部分匹配 |
. | 匹配除换行符外任意字符 |
\ | 转义符号 |
^ | 匹配开始,若放在[]中使用则代表非运算 |
</br>
- 限定符
字符 | 描述 |
---|---|
* | 匹配前一个字符0次或无穷次,等价于{0,} |
+ | 匹配前一个字符1次或无穷次,等价于{1,} |
? | 匹配前一个字符0次或1次,等价于{0,1} |
{} | {n,m}代表匹配前一个字符不少于n次,不多于m次 |
注意:*和+都是贪婪的,?是非贪婪的。通过在*和+后添加?,达到最小长度匹配的效果。*?表示匹配前面的字符任意次,但是越少越好;+?表示匹配前面的字符1次或1次以上,但是越少越好。
Python中的re模块
许多编程语言都支持用正则表达式的操作,以Python为例,其中的re模块功能十分强大,为Python语言提供了正则表达式的全部功能。下面着重介绍几个比较常用的函数。
- search()
用法:
尝试搜索整个字符串,直到找到第一个符合的匹配,停止匹配。
1 | re.search(pattern, string, flags=0) |
- match()
用法:
尝试从字符串的起始位置匹配。
1 | re.match(pattern, string, flags=0) |
- findall()
用法:
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
1 | re.findall(pattern, string, flags=0) |
- finditer()
用法:
和findall() 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
1 | re.finditer(pattern, string, flags=0) |
- group() & groups()
用法:
是匹配对象的函数,用于获取匹配到的表达式
方法 | 描述 |
---|---|
group() | 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组 |
groups() | 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号 |
- compile()
用法:
用于编译正则表达式,当我们要重复多次匹配某一固定的正则表达式的时候,我们可以在一开始就进行re.compile()的操作,然后直接对返回结果应用search(),match(),findall()等方法,这样可以省略调用这些函数时重复编译正则表达式的时间。
1 | re.compile(pattern[, flags]) |
</br>
其实掌握了最基本的正则表达式语法,就可以很轻松的应用到不同的编程语言中,具体的针对不同语言的函数可以在做的时候再去查找。
关于爬虫
众所周知,最基本的网络爬虫就是从网络页面中爬取信息,同样以Python语言为例,我们常用的解析网页的工具有BeautifulSoup模块和lxml模块中的xpath()。这些的原理都是解析网页中的html标签。我比较常用xpath去解析,所以就拿xpath来举个例子:当网页源代码和浏览器渲染出来的代码不一样的时候,我们可能无法直接在浏览器定位我们要查找的标签或者直接copy xpath,同时由于没有缩进,我们也很难直接从源代码中看出某个标签的具体索引,这时我们就可以采用简单粗暴的正则表达式,直接去匹配。另一种情况,就是动态网页加载出来的页面的内容,可能不是完全的json格式,前面可能附带着一段参数,这时我们无法采用json.loads()去编译,我们去要将其中的json格式的数据抓取出来,这时我们也可以用正则表达式直接去抓取。
用正则表达式爬虫可能不是一种很好的选择,主要是因为过于繁琐,但是遇到解决不了的问题,遇到无法解析的标签,正则表达式可以100%保证顺利爬取下来。
推荐在线编译工具
初次遇到正则表达式,可能会觉得像是火星文一样复杂,但是无非就是一些规则和语法,只要多加练习就可以掌握。下面推荐两个在线的正则表达式测试网站: