python正则表达式与re模块-02

  • 2019 年 10 月 7 日
  • 筆記

正则表达式

正则表达式与python的关系

# 正则表达式不是Python独有的,它是一门独立的技术,所有的编程语言都可以使用正则  # 但要在python中使用正则表达式,就必须依赖于python内置的re 模块

验证手机号是否合法的小案例

while True:      phone_number = input('please input your phone number : ')      if len(phone_number) == 11               and phone_number.isdigit()               and (phone_number.startswith('13')               or phone_number.startswith('14')               or phone_number.startswith('15')               or phone_number.startswith('18')               or phone_number.startswith('19')):          print('是合法的手机号码')      else:          print('不是合法的手机号码')
import re  phone_number = input('please input your phone number : ')  if re.match('^(13|14|15|18|19)[0-9]{9}$', phone_number):      print('是合法的手机号码')  else:      print('不是合法的手机号码')

从上面两段代码中很容易就可以看得出来,使用正则表达式来校验手机号明显要比纯python 代码来的精简得多。

下面就正式介绍一下正则表达式的一些基本知识

正则表达式

正则表达式: # 一种匹配字符串的规则

官方定义: # 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

常见的应用场景: # 爬虫 , # 数据分析

如果你想系统的学习,可以去了解一下《正则指引》这本书

在开始讲正则语法之前,先推荐大家一个验证正则的网站,可以在线测试你的正则表达式能否满足你的预期效果:正则表达式在线测试 ,大家可以边学习边在该网站上面验证

你改变正则表达式或者下方的待处理字符串他会自动重新匹配

字符组

# 在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示

常见的字符组(一个字符组中的数据都是 '或' 的关系)

注意: # 字符组可以用 '-' 表示范围要按照ASCII码表的顺序书写,可以 0-9 不可以 9-0,9的ASCII值比0大

字符

常见元字符(推荐按不同颜色来分组记)

注意: # ^ 与 & 会精准限制匹配的内容,两者中间写什么,待匹配的字符串就必须是什么,多一个少一个都不行 , # 运用 | (“或”)的时候一定要把长的写在前面,否则匹配到短的就匹配完成了,会有很多被截断的

分组(): # 当多个正则符号需要重复多次的时候,或者当做一个整体,进行其他操作,那么可以用分组的形式,分组在正则的语法中就是一个小括号 '(表达式)' ,里面放表达式即可

转义字符

观察上面的元字符可以发现其中有  n t 等代表特殊含义的元字符,如果就是要匹配一个字符串 'n',则会与元字符冲突,故需要在 '' 前面再加上一个 '',来防止转义,即要表示字符串 'n' 正则中需要写成 '\n'

量词

只能跟在元字符/字符组/正则组后面,限制其左边紧挨着的那个正则表达式(元字符/字符组/正则组),不可两个量词连在一起(除了 '?',解除正则的贪婪模式)

*、+、? 推荐如图所示的画坐标系的方式记忆

贪婪匹配与非贪婪匹配(惰性匹配)

贪婪匹配: # 在满足匹配时,匹配尽可能长的字符串

非贪婪匹配: # 在满足匹配时,匹配尽可能短的字符串

python的匹配模式默认为贪婪匹配,在量词后面加上 ? 可以将其匹配模式改为非贪婪模式,会匹配尽量少的字符串(仅量词作用的那个对象会受影响)

贪婪匹配原理个人理解: # 先匹配到目标字符串 '<',然后直接读取到后面所有的字符串,从倒数第一个字符开始往回找,找到 '>',则将前面的那个位置至这个位置之间的数据返回 ,注意这里是 .* 任意字符 无限多次!!!

非贪婪原理个人理解: # 先匹配到目标字符串 '<',从该位置开始往后寻找字符 '>',找到则这对数据为一个匹配返回结果 ,注意这里是 .* 任意字符 无限多次!!!

案例练习

推荐案例 正则表达式练习题 ,可以根据案例巩固知识

如果你看了本文觉得描述不清,可以参考 re模块 这篇博客,亦或是下图的出处的博客  python正则表达式指南 (前者的博客页面排版看着美观一些,但大部分数据均来源于后者)

python 中的re模块

上面只是介绍了正则表达式的一些基础知识,它是一门独立的技术,要想在python中使用正则表达式,自然就需要通过学习python内置的re模块了(也可以通过其他的一些函数方法等方式使用正则)

要使用 re模块,请不要忘记先导模块(import re)

本文此部分的参考性可能不高,内容也不是很全面,仅作为个人小结,如果有兴趣可以参考 re模块 里面有更详细的讲解以及丰富的案例(另:本模块知识很少用到,所以仅做笔记翻阅)

常见重点函数 findall 找出字符串所有符合内容成一个列表   search 查找匹配结果  match 从开头开始比对

import re    '''  findall  所有符合正则表达式的所有内容  search  有没有符合正则表达式的内容  match   是不是正则表达式对应的开头  '''  # findall 找出字符串中符合正则表达式的所有内容,并且返回一个列表,列表中的元素就是正则表达式匹配到的结果  res = re.findall('[a-z]+', 'nice toZmeet you')  print(res)  # ['nice', 'to', 'meet', 'you']    # search  不会直接返回匹配到的结果,而是给你返回一个对象,这个对象需要调用,通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。  # 注意:1.search 只会依据正则查找一次,只要查到了结果,就不会往后查找了  #       2.当查找的结果不存在的情况下,调用group会直接报错  res = re.search('ou', 'nice toZmeet you')  res2 = res.group()  # 必须调用group 才能看到匹配到的结果  print(res, res2)  # <_sre.SRE_Match object; span=(14, 16), match='ou'> ou  res1 = re.search(r'.', 'nice toZmeet you')  print(res1)  # None  # print(res1.group())  # 直接报错,if空避免      # match  # 注意:1.match 只会匹配字符串的开头部分  #       2.当字符串的开头与正则表达式不符合匹配规则的情况下,返回的也是None,调用 .group 也会报错  res = re.match('s', 'sda e rf a f')  print(res)  # <_sre.SRE_Match object; span=(0, 1), match='s'>  print(res.group())  # s    '''  正则表达式,返回类型为表达式对象的,如:<_sre.SRE_Match object; span=(6, 7), match='a'>,返回对象时,需要用正则方法取字符串,方法有:      group() # 获取匹配到的所有结果,不管有没有分组将匹配到的全部拿出来,有参取匹配到的第几个如2      groups() # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分的结果      groupdict() # 获取模型中匹配到的分组结果,只拿出匹配到的字符串中分组部分定义了key的组结果  '''

不常用函数 split 切割   sub 替换  compile 将正则编译成一个对象,可对象. 调用上述方法  finditer 返回一个放返回结果的迭代器

import re    # split 类似于字符串的切割split,返回一个列表(他会把被替换掉的字符变成空格)  res = re.split('[ab]', 'sabcasbdsafafabfas')  print(res)  # ['s', '', 'c', 's', 'ds', 'f', 'f', '', 'f', 's']    # sub 类似于字符串的replace 方法,返回替换完成的字符串,可指定替换次数  # sub('正则表达式', '新的内容', '带匹配的字符串', [要替换的次数])  #   先按正则表达式查找所有符合该表达式的内容,统一替换成'新的内容',还可以通过n来控制替换的个数  ret = re.sub('d', 'H', 'eva3egon4yuan4', 1)  # 将数字替换成'H',参数1表示只替换1个  print(ret)  # evaHegon4yuan4    # subn() 字符串replace的加强版,返回替换完成的字符串与总共替换的次数(封成了一个元组)  #   返回的是一个元组,元组的第一个元素是替换完成后的结果,第二个元素代表的是替换的个数  ret = re.subn('d', 'H', 'eva3egon4yuan4')  # 将数字替换成'H',返回元组(替换的结果,替换了多少次)  print(ret)  # ('evaHegonHyuanH', 3)      # compile  将正则编译成一个对象,后期可以直接用它来调用 findall、search 等方法  obj = re.compile('d{3}')  # 将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字  ret = obj.search('abc123eeee')  # 正则表达式对象调用search,参数为待匹配的字符串  print(ret.group())  # 123      # finditer 返回一个存放匹配结果的迭代器,可以使用前面学习到的 迭代器对象.__next__() 方法调用 ---> 简写 next(迭代器对象)  ret = re.finditer('d', 'ds3sy4784a')  print(ret)  # <callable_iterator object at 0x000001AE62617160>  print(next(ret).group())  # 查看第一个结果  # 3  print(next(ret).group())  # 查看第二个结果  # 4  print([i.group() for i in ret])  # 查看剩余的左右结果  # ['7', '8', '4']

扩展

各方法分组的区别

import re    res = re.search('^[1-9]d{14}(d{2}[0-9x])?$', '110105199812067023')  print(res.group())  # 110105199812067023  print(res.group(1))  # 获取正则表达式括号阔起来分组的内容  # 023  # print(res.group(2))  # 报错,取不到     search与match均支持获取分组内容的操作  跟正则无关是python机制    # 而针对findall它没有group取值的方法,所以它默认就是分组优先获取的结果  ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')  print(ret)  # 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可  # ['oldboy']    ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')  # ?:取消分组优先  print(ret)  # ['www.oldboy.com']
import re    ret = re.search("<(?P<tag_name>w+)>w+</(?P=tag_name)>", "<h1>hello</h1>")  # 还可以在分组中利用?<name>的形式给分组起名字  # 获取的匹配结果可以直接用group('名字')拿到对应的值  print(ret.group('tag_name'))  # h1  print(ret.group())  # <h1>hello</h1>  """  注意 ?P=tag_name 相当于引用之前正则表达式,并且匹配到的值必须和前面的正则表达式一模一样  """    # 匹配整数  ret = re.findall(r"d+", "1-2*(60+(-40.35/5)-(-4*3))")  print(ret)  # ['1', '2', '60', '40', '35', '5', '4', '3']    ret = re.findall(r"d+.d*|(d+)", "1-2*(60+(-40.35/5)-(-4*3))")  print(ret)  # ['1', '2', '60', '', '5', '4', '3']  ret.remove("")  print(ret)  # ['1', '2', '60', '5', '4', '3']

爬虫小案例

'''  本爬虫案例思路:      分析 https://movie.douban.com/top250?start=%0&filter= 页面得知,每页有25条数据      通过分次请求该地址,将返回的HTML代码通过正则匹配,解析成想要的字符串格式,      分页分条存入文件中去(用到了 分组 和 取别名 的知识点)  '''  import re  from urllib.request import urlopen      def getPage(url):      response = urlopen(url)      return response.read().decode('utf-8')      def parsePage(s):      com = re.compile(          '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>d+).*?<span class="title">(?P<title>.*?)</span>'          '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S)        ret = com.finditer(s)      for i in ret:          yield {              "id": i.group("id"),              "title": i.group("title"),              "rating_num": i.group("rating_num"),              "comment_num": i.group("comment_num"),          }      def main(num):      url = 'https://movie.douban.com/top250?start=%s&filter=' % num      response_html = getPage(url)      ret = parsePage(response_html)      print(ret)      f = open("move_info7.txt", "a", encoding="utf8")        for obj in ret:          print(obj)          data = str(obj)          f.write(data + "n")      count = 0  for i in range(10):      main(count)      count += 25

数据小样

上面仅仅只是一个简单的爬虫案例,如果你想成为一名爬虫工程师,那么你必须的能够熟练地写出正则表达式,言下之意就是除了本文的内容,你还需要好好去学学正则表达式的内容