广州番禺Python爬虫小班周末班培训
第四期线下Python爬虫小班周末班已经开课了,授课详情请点击:http://chipscoco.com/?id=232
7.1.1 理解正则表达式
正则表达式的英文为regular expression, regular为有规则,有规律的意思,regular expression可简单地理解为"有规则的表达式"。这里的规则是指通过预先定义好的特定字符(模式字符,限定字符,修饰字符)组成一个规则字符串。每一个规则字符串代表一种匹配规则,然后利用规则字符串对目标字符串进行匹配或过滤。
比如正则表达式中定义了模式字符\w,用来匹配字母,数字,下划线。定义了模式字符\s,用来匹配任意空白字符。同时定义了限定符,用来限定对目标字符的匹配次数,比如?字符用来限定匹配0次或1次,+字符用来限定字符串至少出现1次。
在使用正则表达式时,可以对这些模式字符和限定字符进行灵活组合,以组合成更复杂的匹配规则,比如\w+,表示目标字符串中至少有一个字母,数字,或下划线,否则匹配失败。
正则表达式也是一个字符串,在正则表达式中可以包含其它的普通字符。我们学习正则表达式,主要是学习正则表达式中的模式字符和限定符。然后根据正则表达式中的语法规则,将普通字符,模式字符,限定符组合成一个高效的正则表达式。
7.1.2 正则表达式中的模式字符
下表为正则表达式中常用的模式字符:
模式字符 | 描述 |
\w | 匹配字母,数字,下划线 |
\W | 匹配除\w以外的任意字符 |
\s | 匹配空白字符。计算机中的空白字符:\t, \n, \r, \f |
\S | 匹配除\s以外的任意字符,即非空字符 |
\A | 匹配字符串开头 |
\z | 匹配字符串结尾,会匹配到行尾的换行符 |
\Z | 匹配字符串结尾,不会匹配到行尾的换行符 |
\d | 匹配任意数字字符:1-9 |
\D | 匹配除\d以外的任意字符 |
^ | 匹配一行字符串的开头 |
$ | 匹配一行字符串的结尾 |
. | 匹配除换行符以外的任意字符 |
[] | 匹配[]内的任意一个字符,表示某一个范围可使用-进行连接,比如[a-z],表示匹配a到z之间的任意字符 |
[^] | 匹配除[]以外的任意字符 |
| | 左右两边表达式是"或"关系,表示匹配左边或者右边 |
() | (1) 表示将括号内的字符作为一个整体进行匹配 (2) 取匹配结果的时候,括号中的表达式作为一个整体被获取 |
7.1.3 正则表达式中的限定符
限定字符用来限定匹配的次数,下表为正则表达式中常用的限定符:
限定符 | 描述 |
? | 匹配表达式0次或1次 |
+ | 匹配表达式1次以上 |
* | 匹配表达式0次或1次以上 |
{n} | 重复匹配表达式n次 |
{m, n} | 至少匹配m次,最多匹配n次 |
{m,} | 至少匹配m次 |
7.1.4 贪婪匹配与非贪婪匹配
使用限定字符限定匹配次数时,在默认情况下会进行贪婪匹配。所谓贪婪匹配,是指尽可能进行多的匹配。假设目标字符串为"薯条老师的第三期Python线下就业培训班于2021年3月7号开学",正则表达式为.*(\d+),用来提取出字符串中的2021。
由于*符号使用了贪婪匹配,所以会匹配到目标字符串中的第一个字符到"2021"的最后一个数字字符1之间的所有字符,最后只留下数字字符1给\d模式进行匹配,最终匹配的结果为1,不符合预期。
为什么只留下数字字符1给\d模式进行匹配呢?因为模式符号.也可以匹配到数字字符,在贪婪模式下会尽可能多的匹配,所以最终\d模式只匹配到1。
在限定符后面加上一个?,可将贪婪匹配改为非贪婪匹配。在非贪婪匹配中,会尽可能少的匹配。那么,使用正则表达式.*?(\d+),就可以匹配出2021。
7.1.5 正则表达式中的零宽断言
在正则表达式的使用场景中,我们可能会有这样的需求:只做判断,不做捕获。例如"薯条老师","汉堡助教"为需要捕获的目标字符串,且它们前面的位置必须存在字符串"chipscoco",那么该怎么做呢?一些同学可能会这么写正则表达式:
pattern = 'chipscoco(薯条老师|汉堡助教)'
这样写就错了,因为这样的正则表达式会同时捕获到chipscoco。只判断目标字符串前后是否存在特定字符,而不保存匹配结果,可以使用正则表达式中的零宽断言。
下表为零宽断言的常用符号:
零宽断言 | 描述 |
?=exp | 正预测先行断言,目标字符串后面必需匹配表达式exp |
?!exp | 负预测先行断言,!即非,表示目标字符串后面不能出现表达式exp |
?<=exp | 正回顾后发断言,目标字符串前面必需匹配表达式exp |
?<!exp | 负回顾后发断言,表示目标字符串前面不能出现表达式exp |
所以,只捕获"薯条老师","汉堡助教",同时判断其前面的位置必须存在字符串"chipscoco",正则表达式应为:
pattern = '薯条老师|汉堡助教(?<=chipscoco)'
7.1.6 Python中的正则表达式模块
Python提供了内置模块re来处理正则表达式,下表为re模块中的常用方法:
常用方法 | 描述 |
match(pattern,string,flags=0) | 从字符串起始位置开始匹配,通过返回值的group方法来获取匹配的结果。 |
search(pattern, string, flags=0) | 扫描整个字符串,返回第一次匹配的结果 |
findall(pattern, string,flags=0) | 扫描整个字符串,返回所有匹配的结果 |
sub(pattern, repl, string, flags=0) | 将string中的repl子串使用pattern进行替换 |
compile(pattern, flags=0) | 将模式字符串编译成可复用的正则表达式对象 |
以上方法中的flags参数用来对模式匹配进行修饰,flags参数的常用取值如下表所示:
flags参数 | 描述 |
re.I | I是英文单词ignore的首字母,表示在匹配过程中不区分大小写 |
re.M | M是英文单词multilines的首字母,表示多行匹配 |
re.S | 对于模式字符.会匹配包含换行符的任意字符 |
re.U | U是unicode的首字母,表示根据unicode编码来解析字符 |
代码实例-解析所有URL中的域名:
import re login_url = 'http://chipscoco.com/zb_users/plugin/YtUser/cmd.php?act=verify' register_url = 'Http://chipscoco.cn:8090/register/' page_url = 'https://chipscoco.cn/?id=9' home_url = 'HTTP://www.chipscoco.com' official_url = 'http://薯条橙子IT教育.com:6666/' urls = [login_url, register_url, page_url, home_url, official_url] """ (1) (?<!\-)系负回顾后发断言,表示目标字符串前面不能出现字符- (2) 正则表达式([\w\.\u4e00-\u9fa5]+)用来捕获url中的域名,其中u4e00-\u9fa用来匹配中文字符 (3) :?用来匹配域名和端口号之间的分隔符:,(\d*)用来捕获url中的端口号 """ pattern = "https?://(?<!\-)([\w\.\u4e00-\u9fa5]+[\w\.\u4e00-\u9fa5]*)" \ ":?(\d*)[/\w\?\.=&]*$" for url in urls: # re模块的match方法表示从头开始匹配,只匹配一次 o = re.match(pattern, url, re.I) print(o.group(1))
代码实例-解析日志文件中的客户端IP地址及请求时间:
import re log = """ 2020-08-31 16:39:58--INFO--{"remote_addr":192.168.2.3, "request_time": "0.618"} 2020-08-31 16:57:19--INFO--{"remote_addr":192.168.2.3, "request_time": "1.018"} 2020-08-31 16:39:58--INFO--{"remote_addr":192.168.1.7, "request_time": "0.638"} 2020-08-31 16:57:19--INFO--{'remote_addr':192.168.2.3, "request_time": "2.001"} 2020-08-31 16:39:58--INFO--{"remote_addr":192.168.1.7, "request_time": "0.708"} 2020-08-31 16:57:19--INFO--{"remote_addr":192.168.2.3, 'request_time': "0.816"} 2020-08-31 16:39:58--INFO--{"remote_addr":192.168.2.5, 'request_time': "0.616"} 2020-08-31 16:57:19--INFO--{'remote_addr':192.168.2.3, "request_time": "1.091"} """ """ (1) 注意日志文件中的remote_addr,request_time,其引号为单引号或双引号 所以在正则表达式中用['"]进行了匹配。 (2) \s*表示匹配任意的空白字符,空白字符包括空格 (3) ([\d\.]*)用来捕获ip地址或请求时间 """ pattern = """ {['"]remote_addr['"]:\s*([\d\.]*), \s*['"]request_time['"]:\s*['"]([\d\.]*)['"]} """ # re模块的findall方法会查找到所有匹配的目标字符串 o = re.findall(pattern, log, re.I) for _ in o: print(_)
7.1.7 简易的HTML解析器:HTMLParser
现在通过正则表达式来编写一个简单的HTML解析器,用来解析HTML中的常用标签。
以下为源码:
# __author__ = 薯条老师 import re class HTMLParser: class Label: def __init__(self, label, text, **kwargs): self.__label = label self.__text = text self.__build_attrs(**kwargs) def __build_attrs(self, **kwargs): for name,value in kwargs.items(): self.__dict__[name] = value def get_text(self): return re.sub("<.*?>|</.*?>", "", self.__text) def __getitem__(self, name): if name == "text" and name not in self.__dict__: self.__dict__[name] = self.get_text() return self.__dict__.get(name) def __init__(self, html): self.__html = html @property def html(self): return self.__html def __build_pattern(self, label, **kwargs): pattern = "<{}.*?".format(label) for attr, value in kwargs.items(): if attr == "class_": attr = "class" pattern+='{}=["\']{}["\']'.format(attr, value) pattern += "\s*>(.*?)</{}>".format(label) return pattern def find(self, label, **kwargs): # 根据关键字参数来构建一个正则表达式 pattern = self.__build_pattern(label, **kwargs) instances = [] for _ in re.findall(pattern, self.__html): instances.append(HTMLParser.Label(label, _, **kwargs)) return instances if __name__ == "__main__": html = "<html><body><p>在线Python教程 " \ "<a href='www.chipscoco.com'>Python零基础入门指南</a></p>" \ "<div><p><a href=\"www.chipscoco.com\">与薯条老师一起学Python</a></p>" \ "</div></body></html>" html_parser = HTMLParser(html) # 查找HTML中的P标签 labels = html_parser.find("p") for label in labels: # 调用标签对象的get_text方法获取标签文本 print(label.get_text()) """ (1) 通过关键字参数class_获取class为article的标签。 (2) class是Python中的关键字,故用class_作为关键字参数 """ labels = html_parser.find("p",class_="article") print(labels[0].get_text()) labels = html_parser.find("a", href="www.chipscoco.com") for label in labels: # 读者可进行优化,比如可以输出该标签的其它标准属性 print(label["href"], label["text"])
输出结果:
在线Python教程 Python零基础入门指南
与薯条老师一起学Python
在线Python教程 Python零基础入门指南
www.chipscoco.com Python零基础入门指南
www.chipscoco.com 与薯条老师一起学Python
7.1.8 知识要点
(1) 正则表达式也是一个字符串,在正则表达式中可以包含其它的普通字符。我们学习正则表达式,主要是学习正则表达式中的模式字符和限定符。
(2) 所谓贪婪匹配,是指尽可能进行多的匹配,而非贪婪匹配指尽可能少的匹配。
(3) 如需只做判断,不做捕获,可以使用正则表达式中的零宽断言。
(4) Python一个内置的re模块用来处理正则表达式
7.1.9 高薪就业班
(1) Python后端工程师高薪就业班,月薪10K-15K,免费领取课程大纲
(2) Python爬虫工程师高薪就业班,年薪十五万,免费领取课程大纲
(3) Java后端开发工程师高薪就业班,月薪10K-20K, 免费领取课程大纲
(4) Python大数据工程师就业班,月薪12K-25K,免费领取课程大纲
扫码免费领取学习资料: