Python爬虫-正则表达式re

  随着大数据乃至人工智能的迅猛发展,数据变得越来越重要,甚至已成为很多企业赖以生存的根基。而想要获取数据,爬虫是必备工具之一。而正则表达式在爬虫抓取内容中扮演中重要角色,要想成为一个合作的爬虫工程师,就必须熟练的掌握正则表达式的知识。
  正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。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.*?) .*', 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.*?)匹配的字符串
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)

该方法会返回一个对象格式的数据,具有如下的方法:
   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\d+)', double, s)) 

打印如下:

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

   这里穿插说下,正则表达式分组命名的问题,格式为(?P...),如上面(?P\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.*?) .*') 

如上所示,返回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)