官方文档请查看:https://docs.python.org/3/library/re.html
使用软件及版本:Jupyter Notebook (Anaconda3)
使用素材链接:https://pvp.qq.com/web201605/herolist.shtml
几种匹配模式简介
re.Match object 为其基本单元。
方法 | 说明 |
re.search() | 检索整个文档,匹配第一个满足表达式的字符串。 |
re.match() | 从头开始匹配,匹配第一个满足表达式的字符串。 |
re.findall() | 匹配所有满足表达式的字符串,返回一个列表。 |
re.finditer() | 匹配所有满足表达式的字符串,返回一个迭代对象。 |
re.split() | 按表达式分割字符,参数mxsplit控制最大分割数。 |
re.sub() | 替换正则表达式所匹配到的字符,参数count控制最大替换次数。 |
提前下载网页到本地,后面直接从本地加载。
os.chdir("..\Desktop")
text_path=os.getcwd()+"\download_text.json"
url="https://pvp.qq.com/web201605/herolist.shtml"
r=requests.get(url)
r.encoding=r.apparent_encoding #gbk
if r.status_code == 200:
with open(text_path,"w+") as f:
json.dump(r.text,f,ensure_ascii=False)
加载本地文件
text_path=os.getcwd()+"\download_text.json"
with open(text_path,"r") as f:
for line in f:
match_str = json.loads(line)
正则表达式设计
#正则表达式内部以组来区分,每个括号内部便是一个组,想要匹配括号,可以使用 "\(" , "\)" 或者 [(] , [)] 。
#可以用(?P<key>表达式)对组命名;等价于字典,前面是键,后面是值:{key:value};下篇文章会介绍。
hero_extract=r'<li><a href="(herodetail/(\d{3}).shtml)".*?([\u4e00-\u9fa5]{1,5})</a></li>'
#由于字符串中有换行符 "\n" ,故加入flags:re.DOTALL 使得 " . "可以匹配包括换行符在内的所有字符。
hero_extract_pat=re.compile(hero_extract,re.S)
1. re.search() 方法:
if match_str:
result=hero_extract_pat.search(match_str)
if result:
print(result)
输出结果如下:
<re.Match object; span=(12602, 12849), match='<li><a href="herodetail/506.shtml" target="_blank>
上式即是其匹配到的对象,可以看到 re.search 只匹配且返回一个对象。可以用下面代码来验证。
print(match_str[result.span()[0]-1000:result.span()[0]])
print(match_str[result.span()[1]:result.span()[1]+1000])
结果如下:
可以看到,后面同样的 li 并没有匹配到。
匹配到完整的字符串为:
string=result.group()
print(result.group())
#输出结果如下:
'''
<li><a href="herodetail/506.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/506/506.jpg" width="91"
height="91" alt="云中君">云中君</a></li>
'''
看看 match 有哪些属性
print(type(result)) #<class 're.Match'>
print(dir(result))
'''
输出结果如下:
['__class__', '__class_getitem__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
'''
前面的为私有属性,我们不作了解,下面这几个属性或方法应该是我们需要掌握的。
['end', 'endpos', 'expand', 'group', \
'groupdict', 'groups', 'lastgroup', \
'lastindex', 'pos', 're', 'regs',\
'span', 'start', 'string']
1.1 位置属性:
["start","end","span","pos","endpos"]
#输出字符串长度,作为对比。
print(len(match_str)) #output: 46490
#输出匹配到的MATCH对象在原字符串中的位置,等价于 ("result.start()","result.end()").
print(result.span()) #output: (12602, 12849)
#输出匹配目标头部在原字符串中的位置。
print(result.start()) #output: 12602
#输出匹配目标结尾在原字符串中的位置。
print(result.end()) #output: 12849
#输出匹配目标在字符串中开始匹配的位置序列。
print(result.pos) #output: 0
#输出匹配目标在字符串中结束匹配的位置序列。
print(result.endpos) #output: 46490
'''
关于本例:表达式是 hero_extract_pat ,匹配字符串是 match_str,其长度为 46490 ,\
表达式开始匹配的位置是字符串的第 0 个字符,结束匹配的位置是字符串的最后一位,\
即 46490;匹配到的结果在字符串中的位置是 (12602, 12849),匹配结果的开始位置是字符串的第 12602 个字符,匹配结果的结尾是字符串的第 12849 个。
'''
1.2 条件属性:
["re","string"]
#返回用于匹配的正则表达式,本例为 hero_extract_pat。
print(result.re) #output:re.compile('<li><a href="(herodetail/(\\d{3}).shtml)".*?([\\u4e00-\\u9fa5]{1,5})</a></li>', re.DOTALL)
#输出用于匹配的字符串,本例中为 match_str。
print(result.string) #output: <!DOCTYPE HTML>......</html>
#上式中,result.re 下面也有属于自己的属性,例:result.re.pattern 等,可以自行了解学习。
1.3 组 group 属性:
1. 建立组的概念,在正则表达式中,用“( )”来表示一个组,其序号按从左往右按左括号出现顺序,从1开始计数【0为默认参数,表示匹配到的整个字符串】。
在 hero_extract 中,共有三个组,分别是:
- (herodetail/(\\d{3}).shtml)
- (\\d{3})
- ([\\u4e00-\\u9fa5]{1,5})
当然,对于组而言,又有属于它的性质,如:命名,引用 等,这里不作介绍。
["groups","group","lastgroup","groupdict","lastindex"]
#输出一个元组,元组元素为匹配到的值,本例中只有三个组,故元组有三个元素。
print(result.groups()) #output:('herodetail/506.shtml', '506', '云中君')
#这个方法有两种用法,分别是:
#1. group()不加参数【默认参数为0】,返回字符串,值为匹配到的整个字符
print(result.group()) #or print(result.group(0))
"""
output:
<li><a href="herodetail/506.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/506/506.jpg" width="91"
height="91" alt="云中君">云中君</a></li>
"""
#2. group()加不为0的参数,输入组的索引,则返回对应的匹配到的组。
print(result.group(1)) #output: herodetail/506.shtml
print(result.group(2)) #output: 506
#以字典形式返回匹配到的结果,前提是在正则表达式中定义了名称,本例中,输出为 {} 。
print(result.groupdict()) #output: {}
#改写一下正则表达式,则输出结果就会改变。
#改变后 :hero_extract=r'<li><a href="(?P<hero_url>herodetail/(\d{3}).shtml)".*?([\u4e00-\u9fa5]{1,5})</a></li>'
print(result.groupdict()) #output: {'hero_url': 'herodetail/506.shtml'}
#改变后 :hero_extract=r'<li><a href="(?P<hero_url>herodetail/(?P<hero_info_num>\d{3}).shtml)".*?([\u4e00-\u9fa5]{1,5})</a></li>'
print(result.groupdict()) #output: {'hero_url': 'herodetail/506.shtml', 'hero_info_nim': '506'}
#属性,不加括号调动,返回字符:若最后一个组有名字,则返回 “key” ,否则返回 None.只于最后一组是否有名称有关。
print(result.lastgroup) #output: None
#改变后:hero_extract=r'<li><a href="(herodetail/(\d{3}).shtml)".*?(?P<hero_name>[\u4e00-\u9fa5]{1,5})</a></li>'
print(result.lastgroup) #output: hero_name
#同上,为属性,不加括号调动,返回值为匹配结果group的数目。
print(result.lastindex) #output:3
1.4 不常用属性
["expand","regs"]
#目前我也不太搞懂,后面搞懂补上。
print(result.expand()) #output:
#返回一个元组,其元素为各个组字符串在源字符串中的位置索引。相当于:
"""
temp_list=[result.span(),]
for item in result.groups():
temp_list.append((match_str.find(item),match_str.find(item)+len(item)))
regs=tuple(temp_list)
print(regs)
结果略有不同,这是网页页面结构所导致的,主要的是理解其含义。
"""
print(result.regs) #output:((12602, 12849), (12615, 12635), (12626, 12629), (12837, 12840))
2. 特殊 re.match() 方法:
1.此方法是 从头(head) 第一个字符开始匹配,
#此方法来匹配match_str结果为空,因为第一行不是<li...开始。
result=hero_extract_pat.match(match_str)
#等价于
if result: #return :False/because result=None
print(result)
#我们来匹配前面得到的字符串string吧。
result_t=hero_extract_pat.match(string)
#因为match是从头开始匹配
if result:
print(result)
#output: <re.Match object; span=(0, 247), match='<li><a href="herodetail/506.shtml" target="_blank>
3. re.findall() 方法:
匹配整个字符串,提取符合表达式的片段,返回一个列表,列表单个元素为一个 groups 格式的元组,
result=hero_extract_pat.findall(match_str)
if result:
print(result)
#output: [('herodetail/506.shtml', '506', '云中君'),...]
#想要查看元素,按列表处理即可。
for item in result:
print(item)
#output: ('herodetail/506.shtml', '506', '云中君')
4. re.finditer() 方法:
匹配整个字符串,提取符合表达式的片段,返回一个 match 迭代对象,列表单个元素为 re.Match 对象,具有 span() 等属性和方法
result=hero_extract_pat.finditer(match_str)
if result:
print(result)
#output: 返回元素属性及储存地址- <callable_iterator object at 0x0000023E0863A460>
#进行遍历,查看元素
for item in result:
print(item)
#output:<re.Match object; span=(12602, 12849), match='<li><a href="herodetail/506.shtml" target="_blank>
#查看其groups 和 span()
print(item..groups(),item.span(),item.regs)
#output: ('erodetail/506.shtml', '506', '云中君') (12602, 12849) ((12602, 12849), (12615, 12635), (12626, 12629), (12837, 12840))
5. re.split() 方法:
分割字符串,返回列表,可以通过 maxsplit 参数来控制最大分割次数,
#因为源文件太长,这里我们取中间一部分,看看split方法。
#先看看源字符串相同位置:
print(match_str[12000,14000]
"""
output:
li>
</ul>
<div class="herosearch">
<input type="text" id="search" name="search" class="herosearch-input" value="请输入你想要搜索的英雄名">
<a href="javascript:void(0);" class="herosearch-icon" title="点击搜索" id="searchBtn"></a>
</div>
</div>
<div class="herolist-content">
<p id="JErroTips" style="display:none;">输入英雄不存在,请重新输入</p>
<ul class="herolist clearfix">
<li><a href="herodetail/506.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/506/506.jpg" width="91"
height="91" alt="云中君">云中君</a></li>
<li><a href="herodetail/505.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/505/505.jpg" width="91"
height="91" alt="瑶">瑶</a></li>
<li><a href="herodetail/529.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/529/529.jpg" width="91"
height="91" alt="盘古">盘古</a></li>
<li><a href="herodetail/511.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/511/511.jpg" width="91"
height="91" alt="猪八戒">猪八戒</a></li>
<li><a href="herodetail/515.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/515/515.jpg" width="91"
height="91" alt="嫦娥">嫦娥</a></li>
<li><a href="herodeta
"""
result=hero_extract_pat.split(match_str[12000:14000])
if result:
print(result)
"""
output:
['li>\r\n </ul>\r\n <div class="herosearch">\r\n <input type="text" id="search" name="search" class="herosearch-input" value="请输入你想要搜索的英雄名">\r\n <a href="javascript:void(0);" class="herosearch-icon" title="点击搜索" id="searchBtn"></a>\r\n </div>\r\n </div>\r\n <div class="herolist-content">\r\n <p id="JErroTips" style="display:none;">输入英雄不存在,请重新输入</p>\r\n <ul class="herolist clearfix">\r\n ', 'herodetail/506.shtml', '506', '云中君', '\r\n ', 'herodetail/505.shtml', '505', '瑶', '\r\n ', 'herodetail/529.shtml', '529', '盘古', '\r\n ', 'herodetail/511.shtml', '511', '猪八戒', '\r\n ', 'herodetail/515.shtml', '515', '嫦娥', '\r\n <li><a href="herodeta']
"""
加入分割次数限制:
if match_str:
result=hero_extract_pat.split(match_str[12000:14000],maxsplit=1)
if result:
print(result)
"""
output:
['li>\r\n </ul>\r\n <div class="herosearch">\r\n <input type="text" id="search" name="search" class="herosearch-input" value="请输入你想要搜索的英雄名">\r\n <a href="javascript:void(0);" class="herosearch-icon" title="点击搜索" id="searchBtn"></a>\r\n </div>\r\n </div>\r\n <div class="herolist-content">\r\n <p id="JErroTips" style="display:none;">输入英雄不存在,请重新输入</p>\r\n <ul class="herolist clearfix">\r\n ', 'herodetail/506.shtml', '506', '云中君', '\r\n <li><a href="herodetail/505.shtml" target="_blank"><img\r\n src="//game.gtimg.cn/images/yxzj/img201606/heroimg/505/505.jpg" width="91"\r\n height="91" alt="瑶">瑶</a></li>\r\n <li><a href="herodetail/529.shtml" target="_blank"><img\r\n src="//game.gtimg.cn/images/yxzj/img201606/heroimg/529/529.jpg" width="91"\r\n height="91" alt="盘古">盘古</a></li>\r\n <li><a href="herodetail/511.shtml" target="_blank"><img\r\n src="//game.gtimg.cn/images/yxzj/img201606/heroimg/511/511.jpg" width="91"\r\n height="91" alt="猪八戒">猪八戒</a></li>\r\n <li><a href="herodetail/515.shtml" target="_blank"><img\r\n src="//game.gtimg.cn/images/yxzj/img201606/heroimg/515/515.jpg" width="91"\r\n height="91" alt="嫦娥">嫦娥</a></li>\r\n <li><a href="herodeta']
"""
可以看到,在加了 maxsplit 之后,与上面没有限制最大分割次数的做对比,发现其在第一个英雄名称为“云中君”之后,便没有继续分割字符串,而上面一个则分割可以匹配到的所有片段。用 len 方法查看其列表长度:结果如下:
#未限制maxsplit:
print(len(result)) #output: 21
#限制了maxsplit:
print(len(result)) #output: 5
6. re.sub() 方法:
替换表达式所匹配到的参数,并返回替换后的字符串。
#源文件还是太长,不方便在本例中查看,我们截取片段。
result=hero_extract_pat.sub("正则表达式学习",match_str[12000:14000])
if result:
print(result)
"""
output:
li>
</ul>
<div class="herosearch">
<input type="text" id="search" name="search" class="herosearch-input" value="请输入你想要搜索的英雄名">
<a href="javascript:void(0);" class="herosearch-icon" title="点击搜索" id="searchBtn"></a>
</div>
</div>
<div class="herolist-content">
<p id="JErroTips" style="display:none;">输入英雄不存在,请重新输入</p>
<ul class="herolist clearfix">
正则表达式学习
正则表达式学习
正则表达式学习
正则表达式学习
正则表达式学习
<li><a href="herodeta
"""
#加入count参数,控制最大替换次数,则结果如下:
if match_str:
#控制最多替换三个满足表达式的片段,超过则不替换
result=hero_extract_pat.sub("正则表达式学习",match_str[12000:14000],count=3
if result:
print(result)
"""
output:
li>
</ul>
<div class="herosearch">
<input type="text" id="search" name="search" class="herosearch-input" value="请输入你想要搜索的英雄名">
<a href="javascript:void(0);" class="herosearch-icon" title="点击搜索" id="searchBtn"></a>
</div>
</div>
<div class="herolist-content">
<p id="JErroTips" style="display:none;">输入英雄不存在,请重新输入</p>
<ul class="herolist clearfix">
正则表达式学习
正则表达式学习
正则表达式学习
<li><a href="herodetail/511.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/511/511.jpg" width="91"
height="91" alt="猪八戒">猪八戒</a></li>
<li><a href="herodetail/515.shtml" target="_blank"><img
src="//game.gtimg.cn/images/yxzj/img201606/heroimg/515/515.jpg" width="91"
height="91" alt="嫦娥">嫦娥</a></li>
<li><a href="herodeta
"""
以上便是本次的所有内容,关于正则表达式中的 flags ,下期再见。
如有错误,请及时指正,提前感谢。