最近在做日志分析,在提取日志时,正则表达式是最好的选择,而经过进一步地学习,想将自己对于正则表达式地理解总结一下。正则表达式,是一个特殊地符号序列,能帮助我们检查一个自渡川是否与某种模式匹配。在爬虫中可用于非异步加载和非使用Ajax技术的网页(也就是页面上所有内容都在网页源代码上),也可用在一般的匹配查找算法中及其他。

0x01 正则表达式字符


一般字符

字符 含义
. 匹配任意单个字符(不包括\n)
\ 转移字符(把有特殊含义地字符转换成字面意思)
[…] 字符集。对应字符集中的任意字符
1
2
3
4
5
6
7
import re
string = 'aaa aba aca'

# re.findall(reg,text)是字符匹配text中所有符合reg(正则表达式)的子字符串,后面会说
print(re.findall('a.a',string)) #result: ['aaa', 'aba', 'aca']
print(re.findall('\ ',a)) #result: [' ', ' '] 因为string中有两个空格
print(re.findall('a[ac]a',string)) #result: ['aaa', 'aca'] 它和第一个的区别就是少了一个aba,因为.是匹配任意字符,而[]是字符集

预定义字符集

预定义字符集 含义
\d 匹配一个数字字符。等价于[0-9]
\D 匹配一个非数字字符。等价于[^0-9]
\s 匹配任何空白字符,包括空格,制表符,换页符等。等价于[\f\n\r\t\v]
\S 匹配任意非空白字符。等价于[^\f\n\r\t\v]
\w 匹配包括下划线的任何单词字符。等价[A-Za-z0-9]
\W 匹配任何非单词字符。等价于[^A-Za-z0-9]
1
2
3
4
5
6
7
8
9
import re
string = 'www.baidu. com/?id=456&&page=66\n' #一般输出string会在最后换行,再进行后面的显示

print(re.findall('\d',string)) #result: ['4', '5', '6', '6', '6']
print(re.findall('\D',string)) #result: ['w', 'w', 'w', '.', 'b', 'a', 'i', 'd', 'u', '.', ' ','c', 'o', 'm', '/', '?', 'i', 'd', '=', '&', '&', 'p', 'a', 'g', 'e', '=', '\n']
print(re.findall('\s',string)) #result: [' ','\n']
print(re.findall('\S',string)) #结果和上面第二个运行结果差不多,只是这里少了空白字符,但是多了数字
print(re.findall('\w',string)) #result: ['w', 'w', 'w', 'b', 'a', 'i', 'd', 'u', 'c', 'o', 'm', 'i', 'd', '4', '5', '6', 'p', 'a', 'g', 'e', '6', '6']
print(re.findall('\W',string)) #result: ['.', '.', ' ', '/', '?', '=', '&', '&', '=', '\n']

数量词

数量词 含义
* 匹配前一个自读0或无限次
+ 匹配前一个字符1或无限次
匹配前一个字符0次或1次
{m} 匹配前一个字符m次
{m,n} 匹配前一个字符m至n次
1
2
3
4
5
6
7
import re
string = 'aaa aba aca abba abbba'

print(re.findall('ab*a',string)) #result: ['aa', 'aba', 'abba', 'abbba']
print(re.findall('ab+a',string)) #result: ['aba', 'abba', 'abbba'] 与上一个的解果差异就是它至少匹配一次,所以没有aa
print(re.findall('ab{2}a',string)) #result: ['abba']
print(re.findall('ab{0,2}a',string)) #resulr: ['aa', 'aba', 'abba']

边界匹配

边界匹配 含义
^ 匹配字符串开头
$ 匹配字符串结尾
\A 仅匹配字符串开头
\Z 仅匹配字符串结尾
1
2
3
4
5
6
import re
string = 'www.baidu.com&&www.google.com'

print(re.findall('www',string)) #result: ['www', 'www']
print(re.findall('^www',string)) #result: ['www']
print(re.findall('com$',string)) #result: ['com']

.*?

这是一个匹配模式,匹配任意字符无限次,且是非贪婪匹配,当其后面没有任何正则字符时,将会匹配到结尾,而.*.*?的区别在前者是贪婪匹配和非贪婪模式。

1
2
3
string = 'abbbbbbb'
print(re.findall('a.*b',string)) #result:['abbbbbbb'] 这是贪婪模式,会匹配最大长度符合条件的字符串
print(re.findall('a.*?b',string)) #result:['ab'] 这是非贪婪模式,,当第一次匹配到符合条件的字符串时便不再匹配下去

一般爬虫中提取的信息都在中间,最常使用的就是非贪婪匹配

常使用方法模式

在爬虫中,因为要过跳过开头或者中间的某些字符,将自己需要的信息的正则表达式放入到()中,括号内的内容则作为返回结果,括号外不返回,这样,我们在很多无效字符串中拿到我们需要的信息。

0x02 re模块的用法


re模块使python拥有全部的正则表达式功能,下面主要介绍re模块三个主要函数的使用

search (pattern, string, flag = 0) – 匹配并第一个符合规律的内容

pattern - 匹配的正则表达式

string - 匹配的字符串

flag - 标志位,控制匹配方式,如多行匹配等

1
2
3
string = 'helloworld'
print(re.search('\w',string)) #result:<_sre.SRE_Match object; span=(0, 1), match='h'>
print(re.search('\w',string).group()) #result: h

sub(pattern, repl, string, count = 0, flags = 0 )– 用于替换字符串中的匹配项

pattern - 正则表达式

repo - 为替换的字符串

string - 要被查找替换的原始字符串

count - 替换的最大次数,默认0表示替换所有

1
2
string = 'I am Yunee'
print(re.sub('Y.*e', 'Jane', string)) #result: I am Jane

findall(pattern, string, flag = 0) – 匹配所有符合规律的内容

pattern - 匹配的正则表达式

string - 匹配的字符串

flag - 标志位,控制匹配方式,如多行匹配

finall()的使用之前已有很多例子,便不再多说。

最后写一下re模块修饰符

re模块修饰符

修饰符 描述
re.I 使匹配对大小不敏感
re.L 做本地化识别匹配
re.M 多行匹配,影响^和$
re.S 使匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符,影响\w,\W, \b, \B
re.X 该标志通过给予更灵活的格式,以便将正则表达式写的更易解
1
2
3
4
5
string = '''HELLO
world'''
print(re.findall('l',string,re.l)) #result: ['L', 'L', 'l']
print(re.findall('.*',string)) #result: ['hello', '', 'world', '']
print(re.findall('.*',string,re.S)) #result: ['hello\nworld', '']