文章目录
一、什么是正则表达式?
正则表达式,就是使用字符,转义字符和特殊字符组成一个规则,使用这个规则对文本内容完成一个搜索,或者匹配或者替换的功能。
正则表达式的组成:
- 普通字符:大小写字母,数字,符号
- 转义字符
\n,\t,\d,\s
等 - 特殊字符:
* . ? + $ [] () ^ {} |
等 - 匹配模式:I,U,A等
基本使用
#定义字符串
vars = 'iloveyou521verymuch'
#定义正则表达式
reg = '\d'
#调用正则函数方法
res = re.findall(reg, vars)# ['5','2','1']
当然上述只是很简单的对某种类型的搜索,一般用的还是特殊字符+转义字符的组合。
二、re模块的重要函数
以下我们的参数
pattern
:匹配的正则表达式规则string
:等待匹配的字符串
match
和search
是面试常考函数,比较重要。
1:re.match(pattern, string)函数
- 从头开始匹配,要么第一个就符合要求,要么不符合。
- 匹配成功返回
match对象
,否则返回none。 - 返回的结果可以使用
group()
方法获取返回的数据。 - 可以通过
span()
方法获取匹配结果的下标区间。
#定义字符串
vars = 'iloveyou521verymuch'
#定义正则表达式
reg = 'love'
#调用正则函数方法
res = re.match(reg, vars)
print(res)
reg = 'ilove'
res = re.match(reg, vars)
print(res) #返回获取的match对象 <re.Match object; span=(0, 5), match='ilove'>
print(res.group()) #获取返回的数据结果 ilove
print(res.span()) #获取匹配结果的下标区间 (0, 5)
此时第一个字符就不符合匹配的要求,因此返回none,其他函数运行结果在图中。
2:re.search(pattern,string)函数
- 从字符串
string
开头到结尾进行搜索式匹配(与match
的唯一区别) - 匹配成功返回
match对象
,否则返回none。 - 返回的结果可以使用
group()
方法获取返回的数据。 - 可以通过
span()
方法获取匹配结果的下标区间。
#定义字符串
vars = 'iloveyou521verymuch'
#定义正则表达式
reg = 'love'
#调用正则函数方法
res = re.search(reg, vars) #<re.Match object; span=(1, 5), match='love'>
print(res)
reg = 'ilove'
res = re.search(reg, vars)
print(res) #返回获取的match对象 <re.Match object; span=(0, 5), match='ilove'>
print(res.group()) #获取返回的数据结果 ilove
print(res.span()) #获取匹配结果的下标区间 (0, 5)
3:其他常用函数
re.findall(pattern, string)函数
- 按照正则表达式的规则
pattern
,在字符串string
中所有的匹配元素,结果返回一个列表,如果没有找到,就返回一个空列表。
re.finditer(pattern, string)函数
- 按照正则表达式的规则
pattern
在字符串string
中匹配所有的符合规则的元素,返回一个迭代器。
re.sub(pattern, repl,string)函数:搜索替换功能
- 按照正则表达式的规则,在字符串中找到需要被替换的字符。
- 这里多了一个
repl
参数,我们在string
字符串中,找到所有匹配pattern
规则的字符串。将它们替换为repl
。
compile(pattern, flags=0)
将正则表达式的样式编译为一个 正则表达式对象 (正则对象),使用正则对象直接操作。
reg = re.compile('\d{3}') #['521', '511']
res = reg.findall(vars)
上面我们使用那些函数的时候,都是将正则表达式当作参数传递进去,而compile
函数允许我们直接使用创建的正则对象,去调用相应的函数。为什么需要compile
函数呢,从search
的函数原型我们可以窥见一斑
def search(pattern, string, flags=0):
"""Scan through string looking for a match to the pattern, returning
a Match object, or None if no match was found."""
return _compile(pattern, flags).search(string)
每次search
都要创建一个re
的正则化对象,因此如果数据量很大,我们需要不断的进行正则化对象的创建,这无疑是非常耗费资源的,因此我们不如将正则表达式自己定义成一个正则对象,然后由对象调用方法。
split(pattern, string, maxsplit=0, flags=0)
用 pattern
分开 string
。 如果在 pattern
中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit
非零, 最多进行 maxsplit
次分隔, 剩下的字符全部返回到列表的最后一个元素。
re.split(r'\W+', 'Words, words, words.') #['Words', 'words', 'words', '']
三、正则表达式的定义和规则
1:转义字符
\w
:代表单个 字母,数字,下划线\W
:代表单个的 非字母,数字,下划线\d
:代表单个的数字\D
:代表单个的非数字\s
:代表单个的空格符或者制表符\S
:代表单个的非空格符或者制表符
2:特殊字符
.
:代表单个的任意字符(除了换行符)*
:代表匹配次数,任意次数,\w*
表示匹配任意多的字母,数字,下划线。但是这里需要注意,如果字符串\txxxxxxx
,他会返回0,因为一开始没匹配到,所以*就代表了0次。特点:在匹配的开始处如果符合要求,则按照规则一直向后匹配,直到不符合匹配规则结束并返回。如果在匹配的开始处就不符合要求,则直接返回,匹配到的次数为0.
vars = ' ilove you 521verymuch5 11'
reg = '\w*?'
res = re.search(reg, vars).group()# null 第一个字符就不匹配
reg = '\s\w*'
res = re.search(reg, vars).group() # ilove 第一个匹配了才会继续
+
:代表匹配次数,至少要求匹配一次。特点:如果开始处不符合要求,会一直向后询问直到有符合要求的串出现。
vars = ' ilove you 521verymuch5 11'
reg = '\w+?'
res = re.search(reg, vars).group()# ilove 即使第一个没匹配也会继续
?
:拒绝贪婪:前面的匹配规则只要达成要求就返回。
vars = ' ilove you 521verymuch5 11'
reg = '\w+?'
res = re.search(reg, vars).group()# i 对+而言,匹配一个就返回
reg = '\w*?'
res = re.search(reg, vars).group()# 0 对*而言,就一个都不匹配
{}
:代表匹配次数,{4}
一个数字时,表示必须匹配的次数,{2,5}
两个数字时,表示必须匹配的次数的区间次数。
vars = ' ilove you 521verymuch5 11'
reg = '\w{3}'
res = re.search(reg, vars).group()#ilo 只返回第一次匹配到的结果
reg = '\w{2,5}'
res = re.search(reg, vars).group()#ilove 只返回第一次匹配到的结果
[]
:代表字符的范围,[a-z,A-Z,0-9,_]
就相当于一个\w
()
:代表子组,括号中的表达式首先作为整个正则的一部分,另外会把符合小括号中的内容单独提取一份。灵活的是用括号,可以在整个正则匹配对象中提取出来想要的东西,在数据解析中非常有用。
vars = ' ilove you 521verymuch1111'
reg = '\w+\d{4}'
res = re.search(reg, vars)
print(res.group()) #521verymuch1111
reg = '\w+(\d{4})'
res = re.search(reg, vars)
print(res.group()) #521verymuch1111
print(res.groups()) #('1111',) 子组的元素单独存放出来了
vars = ' ilove you 521verymuch1111tommorrow'
reg = '\w+(\d{4})(\w+)'
res = re.search(reg, vars)
print(res.group()) #521verymuch1111tommorrow
print(res.groups()) #('1111', 'tommorrow')
reg = '\w+((\d{4})(\w+))'
res = re.search(reg, vars)
print(res.groups())#('1111tommorrow', '1111', 'tommorrow')
^,$
:代表开头和代表结尾
#一个匹配手机号的实例:
reg = '^1\d{10}$'
开头必须是1,结尾没有别的元素。
3:常用实例:正则表达式匹配邮箱
匹配邮箱的正则表达式
import re
# 正则表示式
pattern = "\w+[@][a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+"
# 使用re模块mach函数进行匹配
print(re.match(pattern, '[email protected]'))
- 第一部分
\w+
匹配邮箱名称,邮箱名称由英文字母、数字、下划线组成。 - 第二部分是匹配邮箱域名,邮箱域名由英文字母、数字、下划线、“.”组成,和邮箱名称的构成基本相同。
- 由于邮箱域名可能是多级的,比如
pku.edu.com
,因此就需要“(\.[a-zA-Z0-9_]+)”
重复匹配一次或多次,
4:正则模式
四、爬虫实例:
基础的request可以看我之前的几篇~
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 3 11:48:27 2020
@author: csyfZhang
"""
import requests as req
import json
import re
class crawl():
#自己博客的地址
url = "https://blog.csdn.net/csyifanZhang/article/list/1"
#定义请求头信息
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36 FS',
}
text = ''
#存储爬取的数据
data = ''
# 存储到json文件
filePath = './text.json'
def __init__(self):
res = req.get(self.url, headers = self.headers)
if res.status_code == 200:
#写入文件
self.text = res.text
if self.pasedata():
self.writedata()
print("写入成功--------------------------------------------")
def pasedata(self):
print("正在解析-------------------------------------------")
# 解析数据
#1:定义正则,提取该属性框内的文本信息(文章title)
reg = '<span class="article-type type-1 float-none">原创</span>(.*?)</a>'
arr = re.findall(reg, self.text)
titlearr = [i.strip() for i in arr] #把所有的空格消掉
#定义阅读数的正则
reg = '<span class="read-num">阅读数 <span class="num">(.*?)</span> </span>'
read = re.findall(reg, self.text)
#定义url的正则
reg = ' <a href="(https://blog.csdn.net/csyifanZhang/article/details/\d+)" target="_blank">'
urlList = re.findall(reg, self.text)
urlList = set(urlList) # 数组去重,保留不重复的链接
#压缩数据
data = list(zip(titlearr, read, urlList))
dataList = []
#转化成json数据
for i in data:
res = {'title': i[0],'url': i[1],'readNum' :i[2] }
dataList.append(res)
print("正在写入--------------------------------------")
with open('./data.json', 'w', encoding='utf-8') as fp:
json.dump(self.data,fp)
print("操作完成--------------------------------------")
# 创建实例化对象
crawl()
对于生成的json数据,她长成了歪瓜裂枣
我们肯定不喜欢这种一行的形式,随便找一个格式化工具之一,进行格式化,然后他就变成了下面这样,多么美妙的数据~
[
{
"title":"看完这个系列所有爬虫都easy!(一)爬虫介绍与request库使用",
"readNum":"602",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105257494"
},
{
"title":"Mobile Edge Computing学习笔记(二)问题,挑战与主流研究方向",
"readNum":"214",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105300028"
},
{
"title":"Mobile Edge Computing学习笔记(一)从通信领域看移动边缘计算:现有的经典结构与模型",
"readNum":"339",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105300194"
},
{
"title":"Federated Machine Learning学习笔记(二):区块链技术",
"readNum":"202",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105186519"
},
{
"title":"网络信息检索(一)检索模型:布尔,向量,概率检索",
"readNum":"314",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105221044"
},
{
"title":"2105:IP Address:32位二进制转IP地址 0msAC",
"readNum":"1",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105214518"
},
{
"title":"1062:昂贵的聘礼:网络流技压群雄!",
"readNum":"7",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105185387"
},
{
"title":"3624 Charm Bracelet:经典背包 内存优化轻松AC",
"readNum":"10",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105252544"
},
{
"title":"2777 Count Color:为什么要进制表示?+一组数据+易错点",
"readNum":"7",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105300646"
},
{
"title":"1077 Eight:八数码问题变形:从bfs到双向bfs+hash到A*启发式搜索的进化史",
"readNum":"10",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105118127"
},
{
"title":"LuoGu:八数码难题:小白从bfs到双向bfs+hash到A*启发式搜索的进化史",
"readNum":"15",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105269356"
},
{
"title":"1552:Doubles:map迅速搞定二倍数的寻找",
"readNum":"12",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105191686"
},
{
"title":"1611 The Suspects:新冠肺炎来了!并查集简单使用+并查集基础附送",
"readNum":"34",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105262768"
},
{
"title":"1321:棋盘问题:揭开dfs的神秘面纱+一些常错的点",
"readNum":"16",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105276795"
},
{
"title":"看完这个系列所有爬虫都easy!(二)Xpath+bs4双剑合璧",
"readNum":"34",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105074521"
},
{
"title":"初识python爬虫:5分钟爬取博客的全部信息",
"readNum":"41",
"url":"https://blog.csdn.net/csyifanZhang/article/details/104691745"
},
{
"title":"深入浅出:四种常用的最短路算法+两种常用生成树算法+网络流常用算法大礼包",
"readNum":"32",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105254056"
},
{
"title":"3259 :Wormholes:spfa+bellman-ford如何判断负环",
"readNum":"39",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105256813"
},
{
"title":"LuoGu p4702 取石子:入门级博弈",
"readNum":"46",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105255537"
},
{
"title":"1067:取石子游戏:威佐夫博奕详细推导",
"readNum":"36",
"url":"https://blog.csdn.net/csyifanZhang/article/details/104656408"
},
{
"title":"1936 All in All:超喜欢的模板题:字符串t能否认s做父",
"readNum":"19",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105300516"
},
{
"title":"1503 Integer Inquiry:超长数加法 坑点分析+友情提示",
"readNum":"12",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105308083"
},
{
"title":"2823 :Sliding Window:滑动窗口问题:单调队列原理+模板+使用~",
"readNum":"15",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105312915"
},
{
"title":"1316:Self Numbers: 类埃及筛法,换种思路,生活如此简单",
"readNum":"18",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105308979"
},
{
"title":"1068 Parencodings:小数据就是可以为所欲为,暴力模拟,0msKO",
"readNum":"20",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105266973"
},
{
"title":"2017 Speed Limit:0ms AC的小水题",
"readNum":"30",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105187309"
},
{
"title":"1459 Power Network:阅读比题恶心系列:网络流怎么流+坑点分析+一组样例数据",
"readNum":"30",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105244813"
},
{
"title":"1328Radar Installation:如何贪心+坑点记录+一组样例数据",
"readNum":"26",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105239413"
},
{
"title":"网络信息检索(五)查询处理:查询方式+查询操作",
"readNum":"26",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105279632"
},
{
"title":"嵌入式系统(七):关于串行通信你不得不知的概念",
"readNum":"76",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105215833"
},
{
"title":"2524:Ubiquitous Religions:宗教信仰:并查集简单使用+并查集基础附送",
"readNum":"24",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105259174"
},
{
"title":"1979:Red and Black:经典地图遍历,dfs强行跑图+坑点分析",
"readNum":"23",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105220748"
},
{
"title":"2104:K-th Number:以题为例,一文搞懂主席树的原理+代码(我画了一天图,就是为了你能看懂!)",
"readNum":"19",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105262371"
},
{
"title":"1298:The Hardest Problem Ever:string类快速处理字符转换",
"readNum":"26",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105266469"
},
{
"title":"1013 Counterfeit Dollar:我们不要暴力:优美的确定假币(内含一大组数据+各种情况分析)",
"readNum":"37",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105313903"
},
{
"title":"2528:Mayor's posters:为什么要线段树?为什么要离散化?一文搞懂该题",
"readNum":"32",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105315516"
},
{
"title":"计算方法:三次样条插值原理",
"readNum":"23",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105288856"
},
{
"title":"这可能是我见过最详细的线段树教程(基础+进阶)",
"readNum":"54",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105214974"
},
{
"title":"1028:Web Navigation:堆栈操作+小坑点分析",
"readNum":"63",
"url":"https://blog.csdn.net/csyifanZhang/article/details/105255775"
}
]