看完这个系列所有爬虫都easy!(三)超神器:Re正则模块深度学习

一、什么是正则表达式?

正则表达式,就是使用字符,转义字符和特殊字符组成一个规则,使用这个规则对文本内容完成一个搜索,或者匹配或者替换的功能。

正则表达式的组成:

  • 普通字符:大小写字母,数字,符号
  • 转义字符 \n,\t,\d,\s
  • 特殊字符:* . ? + $ [] () ^ {} |
  • 匹配模式:I,U,A等

基本使用

#定义字符串
vars = 'iloveyou521verymuch'
#定义正则表达式
reg = '\d'
#调用正则函数方法
res = re.findall(reg, vars)# ['5','2','1']

当然上述只是很简单的对某种类型的搜索,一般用的还是特殊字符+转义字符的组合。


二、re模块的重要函数

以下我们的参数

  • pattern:匹配的正则表达式规则
  • string:等待匹配的字符串

matchsearch是面试常考函数,比较重要。

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, 'bianchen@wy.163.com'))
  • 第一部分 \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"
    }
]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值