1. 什么是正则表达式
正则表达式(Regular Expressions),也称为 “regex” 或 “regexp” 是使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,这样程序就可以将该模式与任意文本字符串相匹配。
使用正则表达式,可以为要匹配的可能字符串集指定规则;此集可能包含英语句子,电子邮件地址,TeX命令或你喜欢的任何内容
正则表达式引擎
采用不同算法,检查处理正则表达式的软件模块 PCRE (perl compatible regular expressions)
正则表达式的元字符分类: 字符匹配,匹配次数,位置锚定,分组
Python的正则表达式是PCRE标准的。
2. 正则表达式基础
# 字符匹配
. 匹配任意单个字符(换行符除外)
[]匹配指定范围内的任意单个字符: [0-9] [a-z]
^[xxx]以[]内的任意字符开头
[^xxx]: 除了[]内的字符,相当于取反
# 匹配次数
用于指定前面的字符要出现几次
* 匹配前面的字符的任意次,包括0次, 贪婪匹配:尽可能长的匹配
.* 任意长度的任意字符
? 匹配其前面的字符0或1次
+ 匹配前面的字符至少1次
{n} 匹配前面的字符n次
{m,n} 匹配前面的字符至少m次,最多n次如{1,3} 匹配1到3次
{n,} 匹配前面的字符至少n次# 位置锚定
# 用于定位出现的位置^ 行首
$ 行尾
^$ 空行
# 分组
() 分组,用()将多个字符捆绑在一起,当作一个整体处理
后向引用: \1,\2# | 或者
a|b a 或 b
(A|a)bc Abc或abc# \ 转义
\ 反斜杠后面可以跟各种字符,以指示各种特殊序列。它也用于转义所有元字符.因此您仍然可以在模式中匹配它们,如果你需要匹配 [ 或 \,你可以在它们前面加一个反斜杠来移除它们的特殊含义:\[ 或 \\。
在python中取消转义推荐使用r,如果要匹配'\n',因为'\n'有特殊含义:回车。要匹配'\n'可以以写'\\n',在Python中可以使用r'\n'。\d 匹配任何十进制数,等价于类 [0-9]
\w 匹配任何字母与数字字符包括下划线;这相当于类 [a-zA-Z0-9_]。
\s 匹配任何空白字符;这等价于类 [ \t\n\r\f\v]。\D 匹配任何非数字字符;这等价于类 [^0-9]。
\W 匹配任何非字母与数字字符;这相当于类 [^a-zA-Z0-9_]。
\S 匹配任何非空白字符;这相当于类 [^ \t\n\r\f\v]。
3. Python中使用正则表达式
在python中使用正则表达式常用的模块为:re。
3.1 re常用的方法
# re.findall('正则表达式','待匹配的文本') 根据正则匹配出所有符合条件的数据,返回列表 >>> re.findall('[0-9]', "Hello world 123") ['1', '2', '3'] # 如果匹配不到findall返回一个空列表 # re.finditer('正则表达式','待匹配的文本') 根据正则匹配出所有符合条件的数据,返回一个对象 >>> re.finditer('[0-9]', "Hello world 123")# 取值: >>> res = re.finditer('[0-9]', "Hello world 123") >>> for i in res: ... print(i.group()) ... 1 2 3 # 如果匹配不到finditer返回空。 # findall 和finditer功能一样,但是finditer更节省内存。 # re.search('正则表达式','待匹配的文本') 根据正则匹配到一个符合条件的就结束 >>> res = re.search('l', "Hello world 123") >>> res <_sre.SRE_Match object; span=(2, 3), match='l'> >>> res.group() 'l' # 匹配到一个就返回,group()返回具体的元素,如果匹配不到search返回None,使用group取值就会报错: AttributeError: 'NoneType' object has no attribute 'goup' # re.match('正则表达式','待匹配的文本') 根据正则从头开始匹配(文本内容必须在开头匹配上) >>> res = re.match('e', "Hello world 123") # e 不在开头,所以匹配不上,返回None >>> print(res) None >>> res = re.match('H', "Hello world 123") # H 为开头能匹配上 >>> print(res) <_sre.SRE_Match object; span=(0, 1), match='H'> >>> print(res.group()) H #如果没有匹配到,match会返回None 使用group取值的时候也会直接报错 AttributeError: 'NoneType' object has no attribute 'group' # search和match匹配不到都会报错,可以处理一下: if res: print(res.group()) else: print('没有匹配到') # re.split 分割 >>> re.split('[0-9]', "Hello 123 world") # 按匹配到的分割,返回一个列表 ['Hello ', '', '', ' world'] # re.sub('要匹配的', '要替换的', '文本') 替换 >>> re.sub('[0-9]','A', "Hello 123 world") # 默认替换全部 'Hello AAA world' >>> re.sub('[0-9]','A', "Hello 123 world",1) # 替换一次 'Hello A23 world' >>> re.sub('[0-9]','A', "Hello 123 world",2) # 替换两次 'Hello AA3 world' # re.subn() 和re.sub 功能一样,但是返回元组 并提示替换了几处 >>> re.subn('[0-9]','A', "Hello 123 world") ('Hello AAA world', 3) >>> re.subn('[0-9]','A', "Hello 123 world", 1) ('Hello A23 world', 1) # 如果一个正则表达式在程序中经常用到,这样每次都写太麻烦,可以使用compile将正则表达式的样式编译为一个正则表达式对象 (正则对象) # 例如 正则表达式'^\d{3,6}'经常使用 >>> obj = re.compile('^\d{3,6}') >>> obj.findall("123 Hello") ['123'] >>> res = obj.search("123 Hello") >>> res.group() '123' # obj还可以使用 finditer,match,split,subn,sub # 分组 >>> res = re.search('[1-9]\d{16}([0-9x])','37152119841105155x') >>> res.group() '37152119841105155x' >>> res.group(1) # 只打印分组1里的内容,1为第一个分组,这里面只有([0-9x])所以打印x 'x' >>> res = re.search('[1-9](\d{16})([0-9x])','37152119841105155x') >>> res.group() '37152119841105155x' >>> res.group(1) # (\d{16}) 为第1个分组 '7152119841105155' >>> res.group(2) # ([0-9x]) 为第2个分组 'x' # 这种取分组里的值也为索引取值 # findall优先打印出分组里的内容 >>> re.findall('[1-9]\d{16}([0-9x])','37152119841105155x') ['x'] # 取消分组: ?: >>> res = re.findall('[1-9]\d{16}(?:[0-9x])','37152119841105155x') >>> res # (?:[0-9x]) ['37152119841105155x'] # 上面的分组为无名分组,分组也可以有名字。 # 有名分组 ?P<分组名称> >>> res = re.search('[1-9](?P \d{16})(?P [0-9x])','37152119841105155x') >>> res.group() '37152119841105155x' >>> res.group('first') '7152119841105155' >>> res.group('second') 'x' # 分组后也可以使用索引到值: >>> res.group(2) 'x'
3.2 re模块练习
爬链家二手房前十页
import re
import requests
for i in range(1,11):
url = 'https://bj.lianjia.com/ershoufang/pg{}srs%E6%97%A7%E5%AE%AB/'.format(i)
r = requests.get(url)
title = re.findall('data-is_focus="" data-sl="">(.*?)',r.text)
price = re.findall('([0-9]{3})',r.text)
address = re.findall('data-log_index="\d" data-el="region">(.*?)', r.text)
houseIcon = re.findall('(.*?)