了解正则表达式,为成为一条爬虫?做准备
众所周知,python是世界上入门最简单的语言!?——周某人
前言
这一章将是python入门系列的最后一章,写python入门系列的初衷就是为了后面开新坑——python爬虫入门系列做准备。正则表达式,将是打开python爬虫学习之路的第一把钥匙。
应用场景
- 特定规律字符串的查找、切割、替换等
- 特定格式(邮箱、手机号、IP、URL等)的校验
- 爬虫项目中,提取特定内容
使用原则
- 只要使用字符串函数能解决的问题就不要用正则表达式
- 正则表达式的效率低,可读性差
- 正则是写来用的,不是写来读的,在不清楚功能的情况下,不要阅读别人的正则表达式
基本使用
- 说明:正则表达式的解析需要借助
re
模块 - 相关函数:
-
match:只从开头进行匹配,匹配到就返回结果对象,没有找到返回None
import re m = re.match('abc', 'hasdfhasdfabcdsfd') n = re.match('abc', 'abcdfjlaskdfjlk') if m: # 返回匹配的内容 print("m: ", m.group()) # 返回匹配内容的位置 print("m: ", m.span()) print("n: ", n.group()) print("n: ", n.span())
-
search:从任意位置匹配,功能同上,都是单次匹配(找到就停)
import re m = re.search('abc', 'hasdfhasdfabcdsfd') n = re.search('abc', 'abcdfjlaskdfjlk') print("m: ", m.group()) print("m: ", m.span()) print("n: ", n.group()) print("n: ", n.span())
-
findall:全部匹配,返回所有匹配的到结果列表,没有找到返回空列表
f = re.findall('abcd', 'abcasjdlaabcaksjd;abcasdjla') print(f)
-
compile:创建正则表达式对象,可以让创建正则对象和内容匹配分开操作
import re # 可以先生成正则表达式对象,然后再进行匹配 c = re.compile('cba') # print(type(c)) # 从开头匹配 # m = c.match('cbasdhaj;acbaalsdk') # 从任意位置匹配 m = c.search('casdhaj;acbaalsdk') if m: print(m.group()) # 匹配所有 f = c.findall('ahkjdcbasdkjalcbasakldjacba') print(f)
-
还有一种是我比较惯用的格式,推荐大家使用这种,既简单又逻辑明了。先预编译要搜寻的的字符串对象pat,然后再从数据源string中匹配对pat进行匹配。
import re pat = 'cba' string = 'cccdsasfawerewcxvcbadcascba' result = re.compile(pat).findall(string) print(result)
-
正则语法
正则表达式的语法比较多但是大多数功能是相似的,所以只需要记住其中之一就行了。在此首先将所有语法列出,最后再对每个语法进行解释。
-
匹配单个字符的语法
普通字符:简单理解就是一对一的完全匹配 []:中间的任意一个字符 [abc]:abc的任意一个字符 [0-9]:任意的数字字符 [a-zA-Z]:任意的字母 [^0-9]:非数字字符 . :除'\n'以外的任意字符 \d:数字字符,等价于[0-9] \D:非数字字符,等价于[^0-9] \w:匹配字(数字、字母、下划线) \W:匹配非字(\w相反的内容) \s:空白字符(\n、\r、\t、空格) \S:非空白字符(\s相反的内容) \b:词边界(开头、结尾、空格、标点) \B:非词边界(\b相反的内容)
-
次数限定:修饰前面的单个字符出现的次数
'*':任意次 +:至少一次 ?:至多一次 {m}:指定m次 {m,n}:m <= 次数 <=n {m,}:至少m次 {,m}:至多m次
-
边界限定:
- ^:以指定的内容开头
- $:以指定的内容结尾
综合实例
-
从开头、结尾和同时限制开头和结尾的匹配
import re # 只从开头匹配 a = re.findall('^hello', 'asjdhelloaskd') # 只从结尾匹配 b = re.findall('world$', 'asjdhelloaskworld') # 同时限制开头和结尾 c = re.findall('^\w*$', 'asjdhelloaskworld') print(a, '\n', b, '\n', c)
-
优先级以及整体(重点):
import re # |:表示或,拥有最低的优先级 # ():可以表示一个整体 s = re.search('hell(o|w)orld', 'akdhahellworld') if s: print(s.group())
-
分组匹配-1-一般字符串
import re # (\d+):'\d'表示匹配数字,跟在后面的'+'表示匹配至少一次前面的表达式出现的次数 # ([a-z]+):'[a-z]表示匹配所有的小写字母,后面的'+'同理 c = re.compile('(\d+)([a-z]+)(\d+)') s = c.search('agaj2635sdhasda237adjsd') if s: # 0:表示完整匹配内容,之后的数字表示第几组,也就是第几个()匹配的内容 print(s.group(0), s.span(0)) print(s.group(1), s.span(1)) print(s.group(2), s.span(2)) print(s.group(3), s.span(3))
-
分组匹配-2-html标签(爬虫常用)
import re string = '<div><a>百度一下</a></div>' # 固定匹配 pat_a = r'<div><a>\w+</a></div>' result_a = re.compile(pat_a).search(string) print(result_a.group()) # 动态匹配:匹配两次嵌套的标签 pat_b = r'<[a-z]+><[a-z]+>\w+</[a-z]+></[a-z]+>' result_b = re.compile(pat_b).search(string) print(result_b.group()) # 无名分组:\1、\2分别表示前面的第一组、第二组匹配的内容 pat_c = r'<([a-z]+)><([a-z]+)>\w+</\2></\1>' result_c = re.compile(pat_c).search(string) print(result_c.group()) # 命名分组:给分组()起名字 pat_d = r'<(?P<one>[a-z]+)><(?P<two>[a-z]+)>\w+</(?P=two)></(?P=one)>' result_d = re.compile(pat_d).search(string) print(result_d.group())
-
findall(爬虫常用):
import re # 按照正则进行匹配,但是添加()后,结果只显示()匹配的内容 f = re.findall('A(abc)A', 'asdjAabcAasdjAabcAsdkabca') print(f)
-
贪婪匹配(爬虫常用)
-
贪婪:最大限度的匹配;正则的匹配默认是贪婪模式。
-
非贪婪:只要满足条件,尽量少的匹配。可以使用
?
取消贪婪匹配。 -
示例:
import re string = 'sdhaasdajasksdbsdjbsdk' # .+?:取消至少一次的贪婪匹配 pat_a = 'a.+?b' result_a = re.compile(pat_a).search(string) print(result_b.group()) # .*?:取消任意多次的贪婪匹配 pat_b = 'a.*?b' result_b = re.compile(pat_b).search(string) print(result_b.group())
-
-
匹配模式(爬虫常用)
import re # re.I:表示忽略大小写 string_a = 'Hello' pat_a = r'hello' result_a = re.search(pat_a, string_a, re.I) print(result_a.group(), '\n') # re.M:多行处理,默认会把字符当做一行处理 string_b = r'asdkasj\nhelloajsdhkas' pat_b = r'hello' result_b = re.search(pat_b, string_b, re.M) print(result_b.group(), '\n') # re.S:是.可以匹配任意,作为单行处理(忽略\n) string_c = '''<div> hello </div>''' pat_c = r'<div>.*?</div>' result_c = re.search(pat_c, string_c, re.S) print(result_c.group(), '\n')
-
字符转义(也很重要)
在定义字符串的开头添加’r’表示原始字符串,可以轻松解决很多关于转义的问题python源码 \\\d
\\\\d
r'\\d'
使用re之后 \\d
\\d
\\d
实际 \d
\d
\d
示例:
import re # # python源码 \\\d \\\\d r'\\d' # 使用re之后 \\d \\d \\d # 结果 \d \d \d # 推荐使用,否则需要写很多\ pat_a = r'\\d' string_a = '\d' result_a = re.compile(pat_a).search(string_a) if result_a: print(result_a.group()) # 匹配'\b'字符串 pat_b = r'\b' string_b = r'\b' result_b = re.compile(pat_b).search(string_b) if result_b: print('成功', result_b.group())
-
正则替换:这里举了一个不是很重要的小例子,但是需要稍作思考才能看懂代码写的是什么
import re s = 'how1are2you' # 正则替换 # s2 = re.sub(r'\d', ' ', s) # 替换时可以传递一个函数,使用函数的返回值进行替换 def double(s): return str(int(s.group()) * 2) # return 'xxx' # 使用专门的处理函数,可以认为的干预替换过程 s2 = re.sub(r'\d', double, s) print(s2)
总结
本篇是《python入门超easy系列》的最后一篇文章。内容是相当重要的,是一座衔接python入门和python爬虫之间的桥梁。正则表达式对python爬虫的入门起着相当重要的作用,后期要学习python爬虫的小伙伴,一定要学好正则表达式!
下期预告:下一周开始更新《python爬虫入门超easy系列》
Tips:如有疑问欢迎随时打扰 ???