正则表达式
当写一个脚本来自动获取最新的代理IP地址,目标URL:http://cn-proxy.com/
很难定位到IP及对应端口的位置,只有class = "sortable"是两个IP表格唯一的特性
产生需求,即通过IP地址特征(四段数字组成,每段数字范围0-255,分别由三个点号隔开)来寻找IP地址,正则表达式应运而生,其是描述这些复杂规则的工具
re模块
Python通过re模块来实现正则表达式
>>>import re
>>>re.search(r'Fish','I love Fish')
<_sre.SER_Match object;span=(7,12),match = 'Fish'>
分析:
search()方法用于在字符串中搜索正则表达式模式第一次出现的位置,结果为(7,12)
第一个参数是正则表达式模式,即描述的搜索规则,使用原始字符串来写
找到后返回的范围下标以0开始,找不到,则返回None
通配符
通配符即可表示任何字符,如所有Word类型的文件为*.docx
正则表达式的通配符用点号(.)来表示,可匹配除了换行符的任何字符
>>>re.search(r'.','I love Fish')
<_sre.SER_Match object;span=(0,1),match = 'I'>
>>>re.search(r'Fish','I love FishC')
<_sre.SER_Match object;span=(7,12),match = 'FishC'>
反斜杠
若想单单匹配点号,则加上反斜杠,表示消除元字符的特殊功能
>>>re.search(r'\.','I love Fish.com')
反斜杠也可以使普通字符拥有特殊能力,如(\d)表示匹配数字
>>>re.search(r'\d','123 I love Fish')
<_sre.SER_Match object;span=(0,1),match = '1'>
粗糙的匹配一个IP地址可以如下写法
>>>re.search(r'\d\d\d.\d\d\d.\d\d\d.\d\d\d','other 192.168.011.111other')
但上述不规范,因为IP数字范围是0-255,而不是000-999
字符类
字符类用来表述一个字符的范围,使用中括号将内容包起来就是字符类,含义是只要匹配到这个字符类的任何字符,结果算作匹配
如想要匹配元音字母,则
>>>>>>re.search(r'[aeiou]','I love Fish')
<_sre.SER_Match object;span=(3,4),match = 'o'>
因为正则表达式区分大小写,所以I虽然是元音字母,但是没有匹配
解决方法:
法一:关闭大小写敏感模式
法二:修改字符类
>>>>>>re.search(r'[aeiouAEIOU]','I love Fish')
在中括号内可使用小横杆来表示范围,包括数字和字母
>>>>>>re.search(r'[a-z]','I love Fish')
>>>>>>re.search(r'[0-2][0-5][0-5]','I love Fish')
[01]与[0-1]等价
重复匹配
用大括号对元字符实现重复匹配的功能
>>>re.search(r'ab{3}c','abbbc')
<_sre.SER_Match object;span=(0,5),match = 'abbbc'>
>>>re.search(r'ab{3}c','abbbbbc') #匹配不了
重复的次数取一个范围
>>>re.search(r'ab{3,5}c','abbbbbc')
<_sre.SER_Match object;span=(0,7),match = 'abbbbbc'>
>>>re.search(r'ab{3,5}c','abbbc')
>>>re.search(r'[0-255]','188')
>>>re.search(r'[0-2][0-5][0-5]','188')
正则表达式匹配的是字符串,所以数字对于字符来说只有0-9,123由‘1’,‘2’,‘3’三个字符构成,[0-255]字符类表示0-2还有两个5,所以匹配的是0125四个数字中任何一个
匹配0-255范围的数字,正确写法是
>>>re.search(r'[0-1]\d\d\|2[0-4]\d|25[0-5]','188') #000-199,200-249,250-255
<_sre.SER_Match object;span=(0,3),match = '188'>
匹配IP地址
>>>re.search(r'([0-1]\d\d|2[0-4]\d|25[0-5]\.){3}([01]\d\d|2[0-4]\d|25[0-5])',''other192.168.1.other')
{3}表示需要重复匹配三次才成功
上例忽视了1不会写出001这一点,忽略位数的影响,所以最终版为
re.search(r'(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])',''other192.168.1.other')
[01]{0,1}\d{0,1}\d表示最后一位一定要有(没有的话还叫数字吗),但百位和十位可有可无
特殊符号及用法
正则表达式以字符串的形式描述,其强大之处在于特殊符号,特殊符号定义了字符集合,子组匹配,模式重复次数。
元字符
包括 . ^ $ * + ? {} [] \ | ()
(.)点号表示匹配除换行符外的任何字符
(|)管道符类似逻辑或操作
>>>re.search(r"Fish(C|D)","FishC")
<_sre.SER_Match object;span=(0,5),match = 'FishC'>
(^)表示匹配字符串的开始位置,即目标字符串出现在开头才会匹配
>>>re.search(r"^Fish","Fish.com")
<_sre.SER_Match object;span=(0,4),match = 'Fish'>
>>>re.search(r"^Fish","I love Fish.com") #None
($)表示匹配字符串的结束位置,即目标字符串出现在末尾才会匹配
>>>re.search(r"FishC$","love FishC")
<_sre.SER_Match object;span=(5,10),match = 'FishC'>
小括号本身是一对元字符,括号内的正则表达式称为一个子组,子组可当做一个整体,例如在后面对其引用
反斜杠
反斜杠若后面数字数1-99,则表示引用序号对应的子组的字符串,若后面数字是0开头或者是三位数字,则表示八进制数,表示的是八进制数所对应的ASCII码字符
>>>re.search(r"(FishC)\1","FishCFishC.com")
\1表示引用前边序号为1的子组(第一个子组),所以r"(FishC)\1"相当于r"FishCFishC",所以匹配的是要有连续两个FishC的内容,才能成功匹配
若\后面添加的是数字0开头或者三位数,则把这三位数当做一个八进制数
>>>re.search(r"\141FishC","aFishC.com")
八进制141对应的ASCII码为小写字母a
>>>re.search(r"(FishC)\060","FishC0.com")
八进制060对应的ASCII码为数字0
[]这对元字符
表明是生成字符类,实际是一个字符集合,其内部的元字符都失去特殊功能,如同反斜杠加上元字符一样
>>>re.search(r"[.]","FishC.com")
<_sre.SER_Match object;span=(5,6),match = '.'>
字符类内部的元字符都失去特殊功能,但也有几个特殊情况
1)小横杠表示范围
>>>re.findall(r"[a-z]","FishC.com")
['i','s','h','c','o','m']
>>>findall表示找出所有匹配内容,并将结果返回为一个列表
2)反斜杠,例如\n表示匹配换行符
3)脱字符(^)表示取反
>>>re.findall(r"[^a-z]","FishC.com")
['F','C','.']
大括号,表示重复次数
"FishC{3}"等价于"FishCCC"
(FishC){3}"等价于"FishCFishCFishC"
(FishC){1,3}表示重复的次数为一次或三次,注意{1,3}间不能有空格
正则表达式不能随便用空格,表示重复的元字符还有:*,+和?
*表示{0,},+表示{1,},问号表示{0,1},同等情况推荐使用这些符号,简洁且效率比使用大括号高些