• Python爬虫(五)—正则表达式 re 的深入学习


    前言

    以下关于正则表达式 re 学习记录,强烈推荐深入了解的查看官方文档。

    re:https://docs.python.org/zh-cn/3/library/re.html

    本文借鉴官方文档及博文:https://www.cnblogs.com/zhaof/p/6925674.html

    正则表达式

    • 介绍
      正则表达式是一组由字母和符号组成的特殊文本, 它可以用来从文本中找出满足你想要的格式的句子。

      • 大小写敏感
      • 使用反斜杠(’’)来表示特殊字符,或者把特殊字符转义成普通字符。
      • 带有 ‘r’ 前缀的字符串字面值中,反斜杠不必做任何特殊处理。 最简单的表达式,也要推荐原始字符串:r’regex’。
      • 正则表达式可以拼接; 如果 A 和 B 都是正则表达式, 那么 AB 也是正则表达式。 通常, 如果字符串 p 匹配 A 并且另一个字符串 q 匹配 B, 那么 pq 可以匹配 AB。
      • 重复修饰符 (, +, ?, {m,n}, 等) 不能直接嵌套。这样避免了非贪婪后缀 ? 修饰符,和其他实现中的修饰符产生的多义性。要应用一个内层重复嵌套,可以使用括号。 比如,表达式 (?:a{6}) 匹配6个 ‘a’ 字符重复任意次数。
      • 绝大部分正则表达式操作都提供为模块函数和方法,在 编译正则表达式. 这些函数是一个捷径,不需要先编译一个正则对象,但是损失了一些优化参数。
    • 特殊字符

    w      匹配字母数字及下划线
    W      匹配f非字母数字下划线
    s      匹配任意空白字符,等价于[	
    
    f]
    S      匹配任意非空字符
    d      匹配任意数字
    D      匹配任意非数字
    A      匹配字符串开始
    Z      匹配字符串结束,如果存在换行,只匹配换行前的结束字符串
    z      匹配字符串结束
    G      匹配最后匹配完成的位置
    
          匹配一个换行符
    	      匹配一个制表符
    ^       匹配字符串的开头
    $       匹配字符串的末尾
    .       匹配任意字符,除了换行符,re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符
    [....]  用来表示一组字符,单独列出:[amk]匹配a,m或k
    [^...]  不在[]中的字符:[^abc]匹配除了a,b,c之外的字符
    *       匹配0个或多个的表达式
    +       匹配1个或者多个的表达式
    ?       匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
    {n}     精确匹配n前面的表示
    {m,m}   匹配n到m次由前面的正则表达式定义片段,贪婪模式
    a|b     匹配a或者b
    ()      匹配括号内的表达式,也表示一个组
    
    • 控制匹配的模式
    修饰符描述
    re.I使匹配对大小写不敏感
    re.L做本地化识别(locale-aware)匹配
    re.M多行匹配,影响 ^ 和 $
    re.S使 . 匹配包括换行在内的所有字符
    re.U根据Unicode字符集解析字符。这个标志影响 w, W, , B.
    re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

    re.compile(pattern, flags=0)

    将正则表达式的样式编译为一个 正则表达式对象 (正则对象),可以用于匹配,通过这个对象的方法 match(), search() 以及其他如下描述。

    这个表达式的行为可以通过指定 标记 的值来改变。值可以是以下任意变量,可以通过位的OR操作来结合( | 操作符)。

    prog = re.compile(pattern)
    result = prog.match(string)
    

    等价于

    result = re.match(pattern, string)
    

    如果需要多次使用这个正则表达式的话,使用 re.compile() 和保存这个正则对象以便复用,可以让程序更加高效。

    re.match(pattern, string, flags=0)

    如果 string 开始的0或者多个字符匹配到了正则表达式样式,就返回一个相应的 匹配对象 。 如果没有匹配,就返回 None。
    尽量使用泛匹配,使用括号得到匹配目标,尽量使用非贪婪模式,有换行符就用re.S
    re.match是从字符串的起始位置匹配一个模式

    • 常规的匹配
      • result.group()获取匹配的结果
      • result.span()获去匹配字符串的长度范围
    import re
    
    content = "hello 123 4567 World_This is a regex Demo"
    result = re.match('^hellosdddsd{4}sw{10}.*Demo$',content)
    print(result)
    print(result.group())
    print(result.span())
    """
    <_sre.SRE_Match object; span=(0, 41), match='hello 123 4567 World_This is a regex Demo'>
    hello 123 4567 World_This is a regex Demo
    (0, 41)
    """
    
    • 泛匹配
    import re
    
    content = "hello 123 4567 World_This is a regex Demo"
    result = re.match("^hello.*Demo$",content)
    print(result)
    print(result.group())
    print(result.span())
    
    • 匹配目标
      为了匹配字符串中具体的目标,则需要通过()括起来。
      通过re.group()获得结果后,如果正则表达式中有括号,则re.group(1)获取的就是第一个括号中匹配的结果
    import re
    
    content = "extra things hello 123455 world_this is a Re Extra things"
    result = re.search("hello.*?(d+).*?Re",content)
    print(result)
    print(result.group())
    print(result.group(1))
    """
    <_sre.SRE_Match object; span=(13, 44), match='hello 123455 world_this is a Re'>
    hello 123455 world_this is a Re
    123455
    """
    
    • 贪婪匹配
    
    import re
    
    content = "hello 1234567 World_This is a regex Demo"
    result = re.match('^hello.*(d+).*Demo',content)
    print(result)
    print(result.group(1))  # 7
    

    结果中可以看出只匹配到了7,并没有匹配到1234567,出现这种情况的原因是前面的.* 给匹配掉了, .*在这里会尽可能的匹配多的内容,也就是我们所说的贪婪匹配,

    如果我们想要匹配到1234567则需要将正则表达式改为:
    result= re.match(’^he.*?(d+).*Demo’,content)
    这样结果就可以匹配到1234567

    • 匹配模式的使用
    import re
    
    content = """hello 123456 world_this
    my name is luozheng
    """
    
    result = re.match('^he.*?(d+).*?luozheng$',content,re.S)
    print(result.group())
    print(result.group(1))
    """
    hello 123456 world_this
    my name is luozheng
    123456
    """
    

    re.search(pattern, string, flags=0)

    扫描整个 字符串 找到匹配样式的第一个位置,并返回一个相应的 匹配对象。如果没有匹配,就返回一个 None。
    为了匹配方便,我们会更多的用search,不用match,match必须匹配头部,所以很多时候不是特别方便
    关于常规的匹配、泛匹配、匹配目标、贪婪匹配、匹配模式的使用 同样适用。

    import re
    
    content = "extra things hello 123455 world_this is a Re Extra things"
    
    result = re.search("hello.*?(d+).*?Re",content)
    print(result.group())
    print(result.group(1))
    """
    hello 123455 world_this is a Re
    123455
    """
    

    re.findall(pattern, string, flags=0)

    对 string 返回一个不重复的 pattern 的匹配列表, string 从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。

    在 3.7 版更改: 非空匹配现在可以在前一个空匹配之后出现了。
    
    import re
    
    html = '''<div id="songs-list">
        <h2 class="title">经典老歌</h2>
        <p class="introduction">
            经典老歌列表
        </p>
        <ul id="list" class="list-group">
            <li data-view="2">一路上有你</li>
            <li data-view="7">
                <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
            </li>
            <li data-view="4" class="active">
                <a href="/3.mp3" singer="齐秦">往事随风</a>
            </li>
            <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
            <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
            <li data-view="5">
                <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
            </li>
        </ul>
    </div>'''
    
    results = re.findall('<li.*?>s*?(<a.*?>)?(w+)(</a>)?s*?</li>',html,re.S)
    print(results)
    for result in results:
        print(result[1])
    """
    一路上有你
    沧海一声笑
    往事随风
    光辉岁月
    记事本
    但愿人长久
    """
    

    注意点

    • s*? 这种用法其实就是为了解决有的有换行,有的没有换行的问题
    • (<a.*?>)? 这种用法是因为html中有的有a标签,有的没有的,?表示匹配一个或0个,正好可以用于匹配
    • 匹配对象总是有一个布尔值 True。如果没有匹配的话 match() 和 search() 返回 None 所以你可以简单的用 if 语句来判断是否匹配
    match = re.search(pattern, string)
    if match:
        process(match)
    
    • 正则表达式对象 (正则对象),同样支持search、 match、findall、split、sub等这些方法。
    pattern = re.compile("d")
    pattern.search("dog")     # Match at index 0<re.Match object; span=(0, 1), match='d'>
    
    pattern.search("dog", 1)  # No match; search doesn't include the "d"
    

    其他函数

    • re.split(pattern, string, maxsplit=0, flags=0)
      用 pattern 分开 string 。 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素。
    import re
    
    s1 = re.split(r'W+', 'Words, words, words.')
    s2 = re.split(r'(W+)', 'Words, words, words.')
    s3 = re.split(r'W+', 'Words, words, words.', 1)
    s4 = re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
    
    print(s1)
    print(s2)
    print(s3)
    print(s4)
    """
    ['Words', 'words', 'words', '']
    ['Words', ', ', 'words', ', ', 'words', '.', '']
    ['Words', 'words, words.']
    ['0', '3', '9']
    """
    
    • re.sub(pattern, repl, string, count=0, flags=0)
      re.sub(正则表达式,替换成的字符串,原字符串)
      例如:
      content = re.sub(’d+’,’’,content)
      会将所有数字删除。
    • re.purge()
      清除正则表达式缓存

    个人博客:Loak 正 - 关注人工智能及互联网的个人博客
    文章地址:Python爬虫(五)—正则表达式 re 的深入学习

  • 相关阅读:
    nginx配置
    线程与进程的区别:
    java面试题1
    递归的定义和优缺点
    使用jedis连接redis可能会出现的问题及解决方案
    Linux上安装Redis
    Linux 权限管理
    Maven
    网址备份
    反射
  • 原文地址:https://www.cnblogs.com/l0zh/p/13739737.html
Copyright © 2020-2023  润新知