美文网首页大数据 爬虫Python AI Sql
Python爬虫-正则表达式re

Python爬虫-正则表达式re

作者: s_nash | 来源:发表于2020-04-28 17:54 被阅读0次

      随着大数据乃至人工智能的迅猛发展,数据变得越来越重要,甚至已成为很多企业赖以生存的根基。而想要获取数据,爬虫是必备工具之一。而正则表达式在爬虫抓取内容中扮演中重要角色,要想成为一个合作的爬虫工程师,就必须熟练的掌握正则表达式的知识。
      正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。re模块使 Python 语言拥有全部的正则表达式功能。本文主要是记录一下本人学习Python的正则表达式re模块常用处理函数的笔记,言归正传,下面进入正文。
    re.match函数
      re.match(pattern, string, flags=0),尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
        1.pattern 匹配的正则表达式
        2.string 要匹配的字符串
        3.flags标志位,用于控制正则表达式的匹配方式,修饰符
    其中修饰符如下:

    image.png
    如下,匹配到起始位置的:
    #!/usr/bin/python
    import re
    line = "Cats are smarter than dogs"
    matchObj = re.match( r'(.*) are (?P<value>.*?) .*', line, re.M|re.I)
    print(matchObj)
    

    打印结果为

    <_sre.SRE_Match object; span=(0, 26), match='Cats are smarter than dogs'>
    

    如上,返回一个re.MatchObject对象,该对象有如下的方法:
       1.group(num=0),0可以不传入,返回字符串,group(num=1)返回正则第一分组匹配的字符串,group(name)返回匹配指定分组的字符串
       2.groups(),以元组返回规则里面所有括号里匹配的字符串
       3.start(num=0),0可以不传入,返回匹配的起始位置,start(name)返回匹配指定分组的字符串起始位置
       4.end(num=0),0可以不传入,返回匹配的结束位置,end(name)返回匹配指定分组的字符串结束位置
       5.span(num=0),0可以不传入,返回匹配的字符串的(start, end),span(name)返回匹配指定分组的字符串的(start, end)
    如下,代码接上面:

    if matchObj:
       print("matchObj.group('value') : ", matchObj.group('value'))
       print("matchObj.group(1) : ", matchObj.group(1))
       print("matchObj.group(2) : ", matchObj.group(2))
       print("matchObj.groups() : ",matchObj.groups())
       print("matchObj.start(2) : ",matchObj.start(2))
       print("matchObj.end(2): ",matchObj.end(2))
       print("matchObj.span(): ",matchObj.span())
    else:
       print("No match!!")
    

    打印结果为

    matchObj.group('value') :  smarter  #获取正则表达式指定分组(?P<value>.*?)匹配的字符串
    matchObj.group(1) :  Cats  #获取正则表达式第一分组(.*)匹配匹配的字符串
    matchObj.group(2) :  smarter  #获取正则表达式第二分组(.*)匹配匹配的字符串
    matchObj.groups() :('Cats', 'smarter')  #获取全部分组匹配的字符串,以元组形式返回
    matchObj.start(2) :9 #返回正则表达式第二分组(.*)的起始位置
    matchObj.end(2): 16 #返回正则表达式第二分组(.*)的结束位置
    matchObj.span():(0, 26) #返回匹配到字符串的起始和结束位置
    

    如果,没有匹配到起始位置,则会返回None,如下:

    print(re.match('com', 'www.baidu.com'))   #没有匹配起始位置,打印结果为None
    

    re.search函数
      re.search(pattern, string, flags=0),扫描整个字符串并返回第一个成功的匹配;
        1.pattern 匹配的正则表达式
        2.string 要匹配的字符串
        3.flags标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
    如下:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*- 
    import re
    print(re.search('www', 'www.runoobwww.com').span()) # 在起始位置匹配
    print(re.search('com', 'www.runoobcom.com').span())    # 不在起始位置匹配
    

    打印结果如下,可以匹配字符串的任意位置,匹配到后直接返回结果,不继续进行匹配:

    (0, 3)
    (10, 13)
    

    该方法会返回一个对象格式的数据<class '_sre.SRE_Match'>,具有如下的方法:
       1.group(num=0),0可以不传入,返回字符串,group(num=1)返回正则第一分组匹配的字符串,group(name)返回匹配指定分组的字符串
       2.groups(),以元组返回规则里面所有括号里匹配的字符串
       3.start(num=0),0可以不传入,返回匹配的起始位置,start(name)返回匹配指定分组的字符串起始位置
       4.end(num=0),0可以不传入,返回匹配的结束位置,end(name)返回匹配指定分组的字符串结束位置
       5.span(num=0),0可以不传入,返回匹配的字符串的(start, end),span(name)返回匹配指定分组的字符串的(start, end)
    具体的用法如下:

    #!/usr/bin/python
    import re
    
    line = "Cats are smarter than dogs";
    searchObj = re.search( r'(.*) are (.*?) .*', line, re.M|re.I)
    if searchObj:
        print("searchObj.groups() : ", searchObj.groups()) 
        print("searchObj.group(1) : ", searchObj.group(1))
        print("searchObj.group(2) : ", searchObj.group(2))
        print("searchObj.start(2) :",searchObj.start(2))
        print("searchObj.end(2): ", searchObj.end(2))
        print("searchObj.span(): ", searchObj.span())
    else:
        print("Nothing found!!")
    

    打印结果为

    searchObj.groups() :  ('Cats', 'smarter')   #获取全部分组匹配的字符串,以元组形式返回
    searchObj.group(1) :  Cats #获取正则表达式第一分组(.*)匹配匹配的字符串
    searchObj.group(2) : smarter #获取正则表达式第二分组(.*)匹配匹配的字符串
    searchObj.start(2) : 9 #返回正则表达式第二分组(.*)的起始位置
    searchObj.end(2): 16 #返回正则表达式第二分组(.*)的结束位置
    searchObj.span(): (0, 26)  #返回匹配到字符串的起始和结束位置
    

    如果,没有匹配任何字符串,则会返回None,如下:

    print(re.match('c1om', 'www.baidu.com'))   #没有匹配起始位置,打印结果为None
    

    re.sub函数
      re.sub(pattern, repl, string, count=0, flags=0)方法用来替换字符串;
       1.pattern 匹配的正则表达式
       2.repl 替换的字符串,也可以为一个函数
       3.string 要匹配的字符串
       4.count 模式匹配后替换的最大次数,0的情况下是全部替换
       5.flags 标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
    如下:

    #!/usr/bin/python
    import re
    
    s = '2004-959-559 # 这是一个国外电话号码'
    phone = re.sub('#\D+', '', s )  #使用空字符替换后面的'# 这是一个国外电话号码'
    print(phone) 
    print(re.sub('\-', '', phone, 1)) # count =1,替换一次匹配到的字符串
    
    

    打印结果如下:

    2004-959-559  #替换#+后面的字符串后,得到前面的电话号码
    2004959-559  #替换一次'-'后的结果
    

    repl为函数的情况下,代码如下

    #!/usr/bin/python
    import re
    
    def double(matched):
      value = int(matched.group('name'))  #获取匹配组name的字符串,对其进行取int
      return str(value*2)
    
    s = 'A23G4HFD567'
    print(re.sub('(?P<name>\d+)', double, s)) 
    

    打印如下:

    A46G8HFD1134 #匹配出数字字符,对其int后进行*2操作后,替换原来的数字字符
    

       这里穿插说下,正则表达式分组命名的问题,格式为(?P<name>...),如上面(?P<name>\d+),就是将这个分组命名为name,这个就可以通过调用group('name')方法获取这个分组匹配的字符串。

    re.compile 函数
     re.compile(pattern[, flags]),用于编译正则表达式,生成一个正则表达式( Pattern )对象;
      1.pattern 匹配的正则表达式
      2.flags标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
    如下:

    #!/usr/bin/python
    import re
    
    pattern = re.compile(r'(.*) are (?P<value>.*?) .*') 
    

    如上所示,返回re.RegexObject 对象,具有如下的常用方法:
      1.findall(string[, pos[, endpos]]),参数如下:
       1.string 待匹配的字符串。
       2.pos 可选参数,指定字符串的起始位置,默认为 0
       3.endpos 可选参数,指定字符串的结束位置,默认为字符串的长度
    findall()方法,返回字符串所有非重叠匹配的列表,如果没有匹配到,则返回空列表,如下:

    #!/usr/bin/python
    import re
    
    pattern = re.compile(r'\d+')  #查找数字
    result1 = pattern.findall('runoob 123 google 456')
    result2 = pattern.findall('run88oob123google456', 0, 10)
    result3 = pattern.findall('runoobgoogle')
    print(result1)
    print(result2)
    

    打印结果如下:

    ['123', '456'] #找出这个字符串中所有的数字字符
    ['88', '12'] #字符串索引0到10之间,查找所有的数字字符
    [] #没有匹配任何内容,返回空的列表
    

    另外,对于findall()方法,如果匹配的正则表达式中有分组的话,则以列表的形式返回分组匹配到字符串,如果有多个分组,则返回元组列表,如下只有一个分组的情况:

    #!/usr/bin/python
    import re
    
    s = "Cats are smarter than dogs"
    pattern = re.compile(r'(.*) are .*', re.M|re.I)
    print(pattern.findall(s))
    

    打印结果如下:

    ['Cats'] # 找出分组(.*)匹配的字符串
    

    有多个分组的情况下,如下:

    #!/usr/bin/python
    import re
    
    s = "Cats are smarter than dogs"
    pattern = re.compile(r'(.*) are (.*?) .*', re.M|re.I)
    print(pattern.findall(s))
    

    打印结果如下:

    [('Cats', 'smarter')]  #返回一个list,元素为元组
    

    这个有个问题,如果pattern = re.compile(r'(.) are (.) .*', re.M|re.I),去掉中间分组那个?,打印出来的结果跟上面不一样,如下

    #!/usr/bin/python
    import re
    
    s = "Cats are smarter than dogs"
    pattern = re.compile(r'(.*) are (.*) .*', re.M|re.I)
    print(pattern.findall(s))
    

    打印结果如下:

    [('Cats', 'smarter than')] 
    

     这到底是为什么呢,这个问题困扰了我很久,原来在正则表达式中*是贪婪匹配,要尽可能多的匹配,所以第二分组贪婪模式下,匹配的字符串为smarter than,上面的处理是在正则表达式外面加?,使用贪婪模式转换成非贪婪模式。所以使用正则表达式进行匹配字符串的时候,尽量使用非贪婪模式,比如用".*?"代替".*",为了防止贪婪模式的情况下,导致获取的字符串缺失。但是如果".*?"出现结尾的话,就会匹配不到任何的内容,因为它会匹配尽可能少的字符,这是需要使用".*".
     另外,findall()方法匹配的字符串只是正则表达式中分组匹配的内容,有的时候跟我们想要获取的字符串不一致,那么我们想获取全部正则匹配的内容,该这么办呢,下面举例说明一下这个问题:

    #!/usr/bin/python
    import re
    
    s = "Cats are smarter than dogs"
    pattern = re.compile(r'(?:.*) are (?:.*) .*', re.M|re.I)  #在分组正则表达式前面(括号里)加'?:',使得捕获组变成非捕获组
    print(pattern.findall(s))
    

    打印结果如下:

    ['Cats are smarter than dogs'] #设置成非捕获组后,匹配出全部的内容。
    

      2.match(string[, pos[, endpos]])参数如下:
       1.string 待匹配的字符串。
       2.pos 可选参数,指定字符串的起始位置,默认为 0
       3.endpos 可选参数,指定字符串的结束位置,默认为字符串的长度
    如下:

    #!/usr/bin/python
    import re
    
    pattern = re.compile(r'\d+')
    s = pattern.match('one12twothree34four', 3, 10)  #传入pos和endpos参数,从pos位置进行匹配,endpos位置结束
    print(s)
    

    打印结果如下:

    <_sre.SRE_Match object; span=(3, 5), match='12'> #指定从字符1进行匹配,能够匹配到数据字符串。
    

    该方法返回MatchObject 对象,有group(),groups(),start(),end()和span()方法,具体的用法见上面。

    re.finditer 函数
     re.finditer(pattern, string, flags=0),与findall类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回,每个迭代都是一个MatchObject 对象;
      1.pattern 匹配的正则表达式
      2.string 要被匹配的字符串
      3.flags 标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
    如下:

    #!/usr/bin/python
    import re
    
    s = '2004-959-559 # 这是一个国外电话号码'
    pattern = re.compile(r'\d+')
    iter = pattern.finditer(s)  #返回一个迭代器
    for i in iter:
             print(i.group()) #调用group()方法获取匹配的内容
    

    打印结果如下:

    #打印出匹配的全部数字字符
    2004
    959
    559
    

    re.split 函数
     re.split(pattern, string[, maxsplit=0, flag=0]),按照能够匹配的子串将字符串分割后返回列表;
      1.pattern 匹配的正则表达式
      2.string 要被匹配的字符串
      3.maxsplit 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。
      4.flags 标志位,用于控制正则表达式的匹配方式,修饰符 (见图image.png)
    如下:

    #!/usr/bin/python
    import re
    
    s = 'runoob, runoob, runoob'
    print(re.split('\W+', s))
    print(re.split('(\W+)', s)) 
    print(re.split('\W+', s), 1) #传入maxsplit=1,分隔一次
    

    打印结果如下:

    ['runoob', 'runoob', 'runoob'] 
    ['runoob', ', ', 'runoob', ', ', 'runoob']
    ['runoob', 'runoob, runoob']  #传入maxsplit=1,分隔一次
    

    这里有个疑问,第一个print打印的内容,连','都做了分隔项了,跟上面的区别是,对正常表达式做了捕获组,希望亲爱的您能够帮忙我解答一下,谢谢
      断断续续的,终于这个总结给写完了,对我自己来说,又巩固了一下正则方面的知识点,加深了印象,也希望能够给亲爱的您带来帮助。

    本篇如果对您有帮助,请点个👍,谢谢。

    相关文章

      网友评论

        本文标题:Python爬虫-正则表达式re

        本文链接:https://www.haomeiwen.com/subject/spsxwhtx.html