正则与re模块
一、正则表达式
1.1、什么是正则
正则表达式(英语:Regular Expression,常简写为regex、regexp或RE),又称正则表示式、正则表示法、规则表达式、常规表示法,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本,简而言之,正则就是使用特殊字符来匹配特定文本达到提取数据的目的。
1.2、正则语法
1.2.1、元字符
- \d 匹配所有数字
- \D 匹配所有非数字
- \w 匹配数字,字母下划线
- \W 匹配非数字,字母下划线
- \s 匹配空白字符(\n\r\t…)
- \S 匹配非空白字符
- . 匹配除换行符之外的所有字符
- [] 位于字符组中的字符,只匹配其中的一个。[abc],[a-zA-Z0-9]
- [^] 不匹配字符组中的字符
- ^ 匹配开头
- $ 匹配结尾
- () 分组
- | 或, a|b:匹配a或者b
1.2.2、量词
- +:前面的字符匹配一次或者多次
- *:匹配任意次
- ?:匹配0次或者一次
- {m}:匹配m次
- {m,}:匹配大于等于m次
- {m,n}:匹配大于等于m小于等于n次
1.2.3、贪婪匹配于非贪婪(量词后面加?)
贪婪匹配:在满足条件的情况下,尽可能的多匹配。(.+、.?、.* …)
非贪婪匹配:在满足条件的情况下,尽可能少匹配。 (.+?、.??、.*? …)
1.2.4、分组命名,分组引用,与取消分组
分组是为了提取已经匹配到的字符串中的特定内容。
import re
st = '<a href="/list,cjpl.html" class="balink">财经评论吧</a>' # 待匹配字符串
#这里进行了分组命名,名字为 name,而在后面通过name这个名字进行引用。
pa = r'<(?P<name>\w+) href="(.*?)" class="balink">(.*?)</(?P=name)>' # 正则表达式
print(re.findall(pa, st))
#这里是第二种分组引用方法
pa = r'<(\w+) href="(.*?)" class="balink">(.*?)</(\1)>' # 正则表达式
print(re.findall(pa, st))
#(?:)取消显示分组,(一般用于取消没有命名或者引用的组)
pa = r'<(?:\w+) href="(.*?)" class="balink">(.*?)</(\1)>' # 正则表达式
print(re.findall(pa, st))
pa = r'<(?:\w+) href="(.*?)" class="balink">(?:.*?)</(\w+)>' # 正则表达式
print(re.findall(pa, st))
#执行结果
[('a', '/list,cjpl.html', '财经评论吧')]
[('a', '/list,cjpl.html', '财经评论吧', 'a')]
[]
[('/list,cjpl.html', 'a')]
1.3、常用的几个正则表达式
- 非负整数:^[1-9]\d*|0
- 正整数:^[1-9]\d*
- 电话号码:^1[3-9]\d{9}
- 日期1998-08-8 :^[1-9]\d{3}-(1[012]|0?[1-9])-([12]\d|0?[1-9]|3[01])
- 进阶版本:^[1-9]\d{3}(?P<tu>\D+)(1[012]|0?[1-9])(?P=tu)([12]\d|0?[1-9]|3[01])
- ```
st='1998+1+1'
rs=re.search(r'^[1-9]\d{3}(?P<tu>[^\d])(1[012]|0?[1-9])(?P=tu)([12]\d|0?[1-9]|3[01])',st)
print(rs.group())
#结果
1998+1+1
```
- 邮箱:^[-\w.]+@([a-zA-Z\d.-]+\.)+[a-zA-Z\d]{2,6}$
- 中文字符:[\u4e00-\u9fa5]+
二、re模块
re模块是python中用来操作正则表达式的常用模块
2.1、常用方法
2.1.1、compile方法,用来将正则表达式,编译成pattern,使用pattern对象调用下面的方法,在一定程度上提升效率。
pattern = re.compile(
r'正则表达式',
'匹配模式',#可以不指定,默认就按正则表达式本来的含义进行匹配。
)
匹配模式:
- re.S : 让 . 可以匹配所有字符
- re.I: 忽略大小写
2.1.2、match方法,默认从头开始匹配,只匹配一次,返回一个match对象
match对象 = re.match(pattern,#正则表达式
string,#要匹配的目标字符串
start,#要匹配目标字符串的起始位置(可选)
end#结束位置(可选)
)
s = re.match('\d\d\d', 'qwe123rty')
print(s)
s = re.match('\d\d\d', '123rty')
print(s) #match对象
print(s.group()) #配置到的字符
print(s.span()) #配置到字符所在位置
#None
#<_sre.SRE_Match object; span=(0, 3), match='123'>
#123
#(0, 3)
2.1.3、search 方法,全文匹配,只匹配一次,返回一个match对象
match = re.search(
pattern,#正则表达式
string,#要匹配的目标字符串
start,#要匹配目标字符串的起始位置(可选)
end#结束位置(可选)
)
s = re.search('\d\d\d', 'qwe123rty')
print(s)
print(s.group())
print(s.span())
<_sre.SRE_Match object; span=(3, 6), match='123'>
123
(3, 6)
2.1.4、findall 方法:全文匹配,匹配多次,返回一个list
list= re.findall(
pattern,#正则表达式
string,#要匹配的目标字符串
start,#要匹配目标字符串的起始位置(可选)
end#结束位置(可选)
)
s = 'qweor98e810243h8-0u0d1023-104321'
ret = re.findall(r'10(\d)', s)
print(ret) #优先显示分组
ret = re.findall(r'10\d', s)
print(ret)
#['2', '2', '4']
#['102', '102', '104']
2.1.5、finditer 方法,全文匹配,匹配多次,返回一个迭代器,迭代器里面存储的是match对象
迭代器= re.finditer(
pattern,#正则表达式
string,#要匹配的目标字符串
start,#要匹配目标字符串的起始位置(可选)
end#结束位置(可选)
)
s = re.finditer('[a-z]+', 'qwer1234zxcv')
print(next(s).group())
print(next(s).group())
#qwer
#zxcv
2.1.6、sub 方法,替换字符串
替换后与的字符串=re.sub(
pattern,#正则表达式
repl, #替换成什么
String,#替换什么
Count#替换次数,可选,默认全部替换
)
s='qwer\n\t 123 9'
res=re.sub('\s+','',s,)
print(res)
#qwer1239
sub方法中第二个参数不仅能传入字符串,而且还能传入一个函数,并且该函数的参数是match对象,通过函数处理匹配到的match对象,有时候可以达到意想不到的效果。
#让s字符串中数字每个加1000
import re
s='zs:1000,ls:3000'
def func(m):
s=m.group()
return str(int(s)+1000)
s=re.sub(r'\d+',func,s)
print(s)
#zs:2000,ls:4000
2.1.7、split方法,分割字符串
list=re.split(
pattern,#正则表达式
String,#待分割字符串
Maxsplit#指定最大分隔次数,默认全部分隔,可选
)
s = re.split('\d\d\d', 'qwe123rty')
print(s)
s = re.split('(\d)(\d)(\d)', 'qwe123rty') # 在re.split中添加分组,通过整体分割,在结果中保留分组内容
print(s)
#['qwe', 'rty']
#['qwe', '1', '2', '3', 'rty']
三、案例
3.1、猫眼电影TOP100
from concurrent.futures import ThreadPoolExecutor
import re
import requests
from queue import PriorityQueue
from queue import Empty
import os
import json
class Maoyan():
def __init__(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
}
self.base_url = 'https://maoyan.com/board/4?offset={}'
self.Quene = PriorityQueue()
self.host = 'https://maoyan.com'
self.path = './maoyan'
def get_html(self, url):
res = requests.get(url, headers=self.headers)
res.encoding = res.apparent_encoding
self.parse_content(res.text)
def get_url(self):
return [self.base_url.format(i * 10) for i in range(10)]
def parse_content(self, content):
res = re.findall(
r'<dd>.*?board-index-.*?">(.*?)</i>.*?<a\s+href="(.*?)"\s+title="(.*?)".*?主演:(.*?)</p>.*?上映时间:(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>',
content, re.S)
for item in res:
dic = {
'rank': int(item[0]),
'url': self.host + item[1],
'title': item[2],
'star': re.sub('\s+', '', item[3]),
'releasetime': item[4],
'score': item[-2] + item[-1]
}
self.Quene.put((dic.get('rank'), dic))
def save_file(self):
if not os.path.exists(self.path):
os.mkdir(self.path)
filename = os.path.join(self.path, '猫眼.json')
with open(filename, 'w', encoding='utf-8') as file:
while True:
try:
json.dump(self.Quene.get_nowait()[1], file, ensure_ascii=False)
file.write('\n')
except Empty:
break
def run(self):
t_list = []
tp = ThreadPoolExecutor(10)
for i in self.get_url():
s = tp.submit(self.get_html, i)
t_list.append(s)
for i in t_list:
i.result()
self.save_file()
if __name__ == '__main__':
my = Maoyan()
my.run()
3.2、股吧
from threading import Thread
import re
import requests
from queue import Queue, Empty
import os
import json
class Guba(object):
def __init__(self, path='./'):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
}
self.base_url = 'http://guba.eastmoney.com/default,99_{}.html'
self.path = path
self.Quene = Queue()
def get_html(self, url):
res = requests.get(url, headers=self.headers)
res.encoding = res.apparent_encoding
self.parse_content(res.text)
def parse_content(self, content):
p = re.compile(
r'<li>\s+<cite>(.*?)</cite>\s+<cite>(.*?)</cite>.*?<span class="sub">.*?balink">(.*?)</a>.*?note">(.*?)</a></span>.*?<cite class="last">(.*?)</cite>',
re.S)
res = p.findall(content)
for item in res:
dic = {
'read': int(re.sub('\s+', '', item[0])),
'comment': int(re.sub('\s+', '', item[1])),
'name': item[2],
'title': item[3],
'time': item[-1]
}
self.Quene.put(dic)
def get_url(self):
return [self.base_url.format(i) for i in range(1, 13)]
def save_file(self):
if not os.path.exists(self.path):
os.mkdir(self.path)
filename = os.path.join(self.path, '股吧.json')
with open(filename, 'w', encoding='utf-8') as file:
while True:
try:
js = self.Quene.get_nowait()
print(js)
json.dump(js, file, ensure_ascii=False)
file.write('\n')
except Empty:
break
def run(self):
th_list = []
for url in self.get_url():
th = Thread(target=self.get_html, args=(url,))
th.start()
th_list.append(th)
for i in th_list:
i.join()
r_h = Thread(target=self.save_file,)
r_h.start()
if __name__ == '__main__':
gb = Guba()
gb.run()
3.3、淘宝电场
通过分析发现,淘宝电场商品数据都是js请求渲染,我这里只提取了其中的一个url进行商品数据提取
3.3.1、jsurl如下
3.3.2、代码
import requests
import re
import json
def get_content():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
}
url = 'https://tce.taobao.com/api/mget.htm?callback=jsonp1495&tce_sid=1870256,1870299&tce_vid=0,1&tid=,&tab=,&topic=,&count=,&env=online,online'
res = requests.get(url, headers=headers)
json_str = re.findall('window.jsonp1495&&jsonp1495\((.*)\)', res.text, re.S)[0].strip()
dic_ = json.loads(json_str, encoding='utf-8')
return dic_['result']['1870299']['result']
def write_file(content):
with open('./goods.json','w',encoding='utf-8') as file:
for item in content:
del item['_sys_point_local']
json.dump(item,file,ensure_ascii=False)
file.write('\n')
print(item)
if __name__ == '__main__':
content = get_content()
write_file(content)