作者:落寞红颜玉玫瑰
本文使用软件及版本:python 3.11
更多 re 信息请查看官方文档:https://docs.python.org/3/library/re.html
本文使用素材:王者荣耀英雄信息获取,网址https://pvp.qq.com/web201605/herolist.shtml
正则表达式:是用来简洁表达一组字符串的表达式
操作符
操作符 | 说明 | 示例 |
. | 除换行符外任意单个字符 | 任意单个字符 |
[ ] | 单字符取值集合 | [abc] 表示a或b或c |
[^ ] | 排除所列字符集合 | [^abc] 表示取值不能是a,b,c |
^ | 匹配字符串开头 | ^a 字符串·需要以a开头 |
$ | 匹配字符串结尾 | a$ 字符串·需要以a结尾 |
* | 重复前面字符0次或n次 | abd* 重复"d" 0次或者n次 |
+ | 重复前面字符1次或n次 | abd+ 重复"d" 1次或者n次 |
? | 重复前面字符0次或1次 | abd? 重复"d" 0次或者1次,结果为 ”ab“ 或 "abd" |
| | 匹配左边字符集或者右边字符集 | abc | def 表示 abc 或者 def |
{m,n} | 重复前面字符n次到m次 | abc{1,3} 表示abc或abcc或abccc |
\d | 表示数字0-9任意一个,等价于[0-9] | \d{1,3} 可以是1,12,123,155等 |
\w | 单词字符(包含下划线),等价于[a-zA-Z0-9_] | \w* 可以是”“,”A“,"a3"等 |
匹配中文字符:[\u4e00-\u9fa5],上表有些操作符没有列出来,后面用到会介绍。
贪婪匹配与非贪婪匹配
re 中默认匹配模式是贪婪匹配,即尽可能多的匹配字符。
贪婪 | 非贪婪 | 举例 | 说明 |
* | *? | a[c]*? | 贪婪匹配:匹配尽可能多的c; 非贪婪匹配:尽可能匹配少的c |
+ | +? | a[c]+? | |
? | ?? | a[c]?? | |
{m,n} | {m,n}? | a[c]{1,3}? |
贪婪匹配
match_str="acccccccc"
result=re.search("a[c]*",match_str)
if result:
print(result.group(0))
#贪婪匹配下输出结果为:acccccccc
非贪婪匹配
match_str="acccccccc"
result=re.search("a[c]*?",match_str)
if result:
print(result.group(0))
#非贪婪匹配下输出结果为:a
下面来提取王者英雄名称,网页链接,及图片
因为只需要获取一次网站数据,加上需要熟悉正则表达式,所以我们选择用 requests 库和 re 库。导入需要用到的模块
import os
import re
import requests
import openpyxl
url="https://pvp.qq.com/web201605/herolist.shtml"
r=requests.get(url)
r.encoding=r.apparent_encoding #gbk
if r.status_code == 200:
print(r.text[:2000])
else:
print("未获取到网页")
获取到的网页文本如下:
<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_str=r.text
#英雄链接提取正则表达式
hero_str=r'<li><a href="(herodetail/(\d{3}).shtml)"'
hero_match=re.compile(hero_str)
#找到所有英雄链接网址
result=hero_match.finditer(match_str)
if result:
for match in result:
print(match.group(1))
注:观察到英雄图片和英雄链接有同一个id,可以直接拼接英雄图片链接。
image_url="https://game.gtimg.cn/images/yxzj/img201606/heroimg/{0}/{0}.jpg".format(match.group(2))
用同样方法,写出抓取英雄名字的正则表达式:
这里用到了非贪婪匹配 “.*?” ,我们需要得到每一条<li>,所以要尽可能少的匹配中间的字符,如果是贪婪匹配,则得到是整个<ul>,因为其对应关系如下:
<ul>
<li></li>
<li></li>
<li></li>
</ul>
一个<ul>中包含多个<li>,贪婪匹配就会选中整个<ul>。
“[\u4e00-\u9fa5]{1,5}” 用来定位英雄名字,英雄名字为汉字,长度在1-5个字之间。
match_str=r.text
#英雄名字提取正则表达式,
hero_name_str=r'<li><a .*?([\u4e00-\u9fa5]{1,5})</a></li>'
#因为字符串中有换行符“\n”,所以加入re.S
hero_name_match=re.compile(hero_name_str,re.S)
#找到所有英雄链接网址
result=hero_match.finditer(match_str)
if result:
for match in result:
print(match.group(1))
整合两个正则表达式,最后的结果如下:
url="https://pvp.qq.com/web201605/herolist.shtml"
r=requests.get(url)
r.encoding=r.apparent_encoding #gbk
if r.status_code == 200:
match_str="".join(r.text)
print(match_str)
hero_extract=r'<li><a href="(herodetail/(\d{3}).shtml)".*?([\u4e00-\u9fa5]{1,5})</a></li>'
hero_extract_pat=re.compile(hero_extract,re.S)
result=hero_extract_pat.finditer(match_str)
if result:
wb=gen_wb()
ws=wb["hero_info"]
start_row=2
ws.cell(1,1).value="hero_name"
ws.cell(1,2).value="hero_url"
ws.cell(1,3).value="hero_image_url"
#re.finditer 返回的是迭代的 match 对象,可以遍历输出,关于 match 对象,下篇文章会介绍。
for match in result:
ws.cell(start_row,2).value="https://pvp.qq.com/web201605/"+match.group(1)
ws.cell(start_row,3).value="https://game.gtimg.cn/images/yxzj/img201606/heroimg/{0}/{0}.jpg".format(match.group(2))
ws.cell(start_row,1).value=match.group(3)
print(start_row)
start_row += 1
wb.save(save_path)
print("英雄信息抓取下载完毕,下载路径:{}".format(save_path))
else:
print("未获取到网页")
创建写入Excel函数:
def gen_wb():
os.chdir("../Desktop")
global save_path
save_path=os.getcwd()+"\hero_info.xlsx"
if os.path.exists(save_path):
os.remove(save_path)
wb=openpyxl.Workbook()
ws=wb.create_sheet("hero_info",0)
wb.save(save_path)
return openpyxl.load_workbook(save_path)
成功抓取到信息:
验证结果:文件对应正确。