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

in python爬虫 with 0 comment

前言

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

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

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

正则表达式

\w      匹配字母数字及下划线
\W      匹配f非字母数字下划线
\s      匹配任意空白字符,等价于[\t\n\r\f]
\S      匹配任意非空字符
\d      匹配任意数字
\D      匹配任意非数字
\A      匹配字符串开始
\Z      匹配字符串结束,如果存在换行,只匹配换行前的结束字符串
\z      匹配字符串结束
\G      匹配最后匹配完成的位置
\n      匹配一个换行符
\t      匹配一个制表符
^       匹配字符串的开头
$       匹配字符串的末尾
.       匹配任意字符,除了换行符,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, 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是从字符串的起始位置匹配一个模式

import re

content = "hello 123 4567 World_This is a regex Demo"
result = re.match('^hello\s\d\d\d\s\d{4}\s\w{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())
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*? 这种用法其实就是为了解决有的有换行,有的没有换行的问题
  • match = re.search(pattern, string)
    if match:
        process(match)
    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"

    其他函数

    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']
    """
    Responses