网络爬虫实战
入门Re(正则表达式库)
Re正则表达式详解——提取页面关键信息
(1)正则表达式的概念
正则表达式:regular expression, regex, RE,是用来简洁表达一组字符串的表达式,例如:
- 一组字符串:
‘PN’ ‘PYN’ ‘PYTN’ ‘PYTHN’ ‘PYTHON’
正则表达式为:
P(Y|YT|YTH|YTHO)?N - 一组字符串(无穷个):
‘PY’ ‘PYY’ ‘PYYY’ ‘PYYYY’ …… ‘PYYYY……’
正则表达式为(无穷字符串组的简洁表达):
PY+ - 一组字符串(后续存在不多于10个字符 后续字符不能是’P’或’Y’ )
'PYABC’√ 'PYKXYZ’×
正则表达式为(某种特征字符串组的简洁表达):
PY[^PY]{0,10}
Re优势:一行就是特征(模式)——一行胜千言
正则表达式是一种通用的字符串表达框架,进一步,正则表达式是一种针对字符串表达“简洁”和“特征”思想的工具,正则表达式可以用来判断某字符串的特征归属
正则表达式在文本处理中十分常用:
- 表达文本类型的特征(病毒、入侵等)
- 同时查找或替换一组字符串
- 匹配字符串的全部或部分,最主要应用在字符串匹配中
- ……
如何使用?
还是看之前的这个例子:
- 一组字符串:
‘PN’ ‘PYN’ ‘PYTN’ ‘PYTHN’ ‘PYTHON’
正则表达式:
P(Y|YT|YTH|YTHO)?N
即regex=‘P(Y|YT|YTH|YTHO)?N’
编译一下:p=re.compile(regex)
可以发现原来字符串的特征就是:特征(p)
其中,编译——将符合正则表达式语法的字符串转换成正则表达式特征
(2)正则表达式的语法
正则表达式例如:P(Y|YT|YTH|YTHO)?N,可见正则表达式语法由字符和操作符构成
常用操作符如下所示:
正则表达式语法实例:
- P(Y|YT|YTH|YTHO)?N→’PN’、‘PYN’、‘PYTN’、‘PYTHN’、‘PYTHON’
- PYTHON+→’PYTHON’、‘PYTHONN’、‘PYTHONNN’…
- PY[TH]ON→’PYTON’、‘PYHON’
- PY[^TH]?ON→’PYON’、‘PYaON’、‘PYbON’、‘PYcON’…
- PY{:3}N→’PN’、‘PYN’、‘PYYN’、‘PYYYN’…
经典正则表达式实例:
- ^[A‐Za‐z]+$→由26个字母组成的字符串
- ^[A‐Za‐z0‐9]+$ →由26个字母和数字组成的字符串
- ^‐?\d+$ →整数形式的字符串
- ^[0‐9]*[1‐9][0‐9]*$ →正整数形式的字符串
- [1‐9]\d{5} →中国境内邮政编码,6位
- [\u4e00‐\u9fa5] →匹配中文字符
- \d{3}‐\d{8}|\d{4}‐\d{7}→国内电话号码,010‐68913536
匹配IP地址的正则表达式
IP地址字符串形式的正则表达式(IP地址分4段,每段0‐255)
\d+.\d+.\d+.\d+或 \d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}
精确写法:
0‐99→[1‐9]?\d
100‐199→1\d{2}
200‐249→2[0‐4]\d
250‐255→25[0‐5]
所以应写为:
(([1‐9]?\d|1\d{2}|2[0‐4]\d|25[0‐5]).){3}([1‐9]?\d|1\d{2}|2[0‐4]\d|25[0‐5])
(3)Re库的基本使用
Re库是Python的标准库,主要用于字符串匹配,调用方式:
import re
正则表达式的表示类型
raw string类型(原生字符串类型),re库采用raw string类型表示正则表达式,表示为:r’text’,例如:
- r’[1‐9]\d{5}’
- r’\d{3}‐\d{8}|\d{4}‐\d{7}’
raw string是不包含对转义符再次转义的字符串
re库也可以采用string类型表示正则表达式,但更繁琐,例如:
- ‘[1‐9]\d{5}’
- ‘\d{3}‐\d{8}|\d{4}‐\d{7}’
建议:当正则表达式包含转义符时,使用raw string
Re库主要功能函数如下所示:
re.search
返回match对象
re.search(pattern, string, flags=0)
- pattern : 正则表达式的字符串或原生字符串表示
- string : 待匹配字符串
- flags : 正则表达式使用时的控制标记
import re
regex = r'[1-9]\d{5}'
string = "BIT 100081, HUBEI JINGMEN 448000."
match = re.search(regex, string)
if match:
print(match.group(0))
print(match.group())
结果如下:
100081
100081
re.match
从一个字符串的开始位置起匹配正则表达式,返回match对象
re.match(pattern, string, flags=0)
- pattern : 正则表达式的字符串或原生字符串表示
- string : 待匹配字符串
- flags : 正则表达式使用时的控制标记
import re
regex = r'[1-9]\d{5}'
string = "BIT 100081, HUBEI JINGMEN 448000."
string2 = "100081 BIT, HUBEI JINGMEN 448000."
match = re.match(regex, string)
match2 = re.match(regex, string2)
if match:
print('1:'+match.group(0))
if match2:
print('2:'+match2.group())
结果如下:
2:100081
re.findall
搜索字符串,以列表类型返回全部能匹配的子串
re.findall(pattern, string, flags=0)
- pattern : 正则表达式的字符串或原生字符串表示
- string : 待匹配字符串
- flags : 正则表达式使用时的控制标记
import re
regex = r'[1-9]\d{5}'
string = "BIT 100081, HUBEI JINGMEN 448000, TSINGHUA100084,0100084,01000840,1234567890123456789."
match = re.findall(regex, string)
if match:
print(match)
结果如下:
['100081', '448000', '100084', '100084', '100084', '123456', '789012', '345678']
re.split
将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
re.split(pattern, string, maxsplit=0, flags=0)
- pattern : 正则表达式的字符串或原生字符串表示
- string : 待匹配字符串
- maxsplit: 最大分割数,剩余部分作为最后一个元素输出
- flags : 正则表达式使用时的控制标记
import re
regex = r'[1-9]\d{5}'
string = "BIT 100081, HUBEI JINGMEN 448000, TSINGHUA100084,0100084,01000840,1234567890123456789."
match = re.split(regex, string)
match2 = re.split(regex, string, maxsplit=2)
print(match)
print(match2)
['BIT ', ', HUBEI JINGMEN ', ', TSINGHUA', ',0', ',0', '0,', '', '', '9.']
['BIT ', ', HUBEI JINGMEN ', ', TSINGHUA100084,0100084,01000840,1234567890123456789.']
re.finditer
搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象
re.finditer(pattern, string, flags=0)
- pattern : 正则表达式的字符串或原生字符串表示
- string : 待匹配字符串
- flags : 正则表达式使用时的控制标记
import re
regex = r'[1-9]\d{5}'
string = "BIT 100081, HUBEI JINGMEN 448000, TSINGHUA100084,0100084,01000840,1234567890123456789."
for match in re.finditer(regex, string):
if match:
print(match.group())
结果为:
100081
448000
100084
100084
100084
123456
789012
345678
re.sub
在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
re.sub(pattern, repl, string, count=0, flags=0)
- pattern : 正则表达式的字符串或原生字符串表示
- repl: 替换匹配字符串的字符串
- string : 待匹配字符串
- count : 匹配的最大替换次数
- flags : 正则表达式使用时的控制标记
import re
regex = r'[1-9]\d{5}'
string = "BIT 100081, HUBEI JINGMEN 448000, TSINGHUA100084,0100084,01000840,1234567890123456789."
match = re.sub(regex, 'zipcode', string)
print(match)
结果为:
BIT zipcode, HUBEI JINGMEN zipcode, TSINGHUAzipcode,0zipcode,0zipcode0,zipcodezipcodezipcode9.
Re库的另一种等价用法
函数式用法:一次性操作:
rst = re.search(r'[1‐9]\d{5}', 'BIT 100081')
等价于面向对象用法:编译后的多次操作
pat = re.compile(r'[1‐9]\d{5}')
rst = pat.search('BIT 100081')
re.compile——将正则表达式的字符串形式编译成正则表达式对象
regex = re.compile(pattern, flags=0)
- pattern : 正则表达式的字符串或原生字符串表示
- flags : 正则表达式使用时的控制标记
如:
regex = re.compile(r'[1‐9]\d{5}')
(4)Re库的Match对象
Match对象是一次匹配的结果,包含匹配的很多信息,例如:
>>>match = re.search(r'[1‐9]\d{5}', 'BIT 100081')
>>>if match:
print(match.group(0))
>>>type(match)
<class '_sre.SRE_Match'>
import re
regex = r'[1-9]\d{5}'
string = "BIT 100081, HUBEI JINGMEN 448000, TSINGHUA100084,0100084,01000840,1234567890123456789."
m = re.search(regex, string)
print(m.string)
print(m.re)
print(m.pos)
print(m.endpos)
print(m.group(0))
print(m.start())
print(m.end())
print(m.span())
结果如下:
BIT 100081, HUBEI JINGMEN 448000, TSINGHUA100084,0100084,01000840,1234567890123456789.
re.compile('[1-9]\\d{5}')
0
86
100081
4
10
(4, 10)
这个个人感觉很有用,可以配合finditer来对整个HTML进行遍历
(5)Re库的贪婪匹配和最小匹配
同时匹配长短不同的多项,返回哪一个呢?
>>>match = re.search(r'PY.*N', 'PYANBNCNDN')
>>>match.group(0)
'PYANBNCNDN'
Re库默认采用贪婪匹配,即输出匹配最长的子串
如何输出最短的子串呢?
>>>match = re.search(r'PY.*?N', 'PYANBNCNDN')
>>>match.group(0)
'PYAN'
只要长度输出可能不同的,都可以通过在操作符后增加?变成最小匹配
小结
正则表达式是用来简洁表达一组字符串的表达式
r'\d{3}‐\d{8}|\d{4}‐\d{7}'
re.search() = regex=re.compile() + regex.search()