zoukankan      html  css  js  c++  java
  • Python爬虫从入门到精通——基本库re的使用:正则表达式【华为云技术分享】

    置顶:华为云618大促火热进行中,全场1折起,免费抽主机,消费满额送P30 Pro,点此抢购

    正则表达式是处理字符串的强大工具,它有自己特定的语法结构,有了它,实现字符串的检索、替换、匹配验证都不在话下。对于爬虫,基于正则表达式,从HTML里提取想要的信息就非常方便了。

    正则表达式有特定的语法规则的。写好正则表达式后,就可以拿它去一个长字符串里匹配查找了。不论这个字符串里面有什么,只要符合我们写的规则,统统可以找出来。对于网页来说,如果想找出网页源代码里有多少URL,用匹配URL的正则表达式去匹配即可。下图就列出了正则表达式常用的匹配规则。

    正则表达式不是Python独有的,它可以用在其他编程语言中。在Python中,re库提供了整个正则表达式的实现,利用这个库,可以在Python中使用正则表达式。在Python中写正则表达式几乎都用这个库,下面就来了解它的一些常用方法。

    match()

    match()传入要匹配的字符串以及正则表达式,就可以检测这个正则表达式是否匹配字符串。match()方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回匹配成功的结果,如果不匹配,就返回None。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.match('Blog:w{11}', content)
    5 print(result.group())

    运行结果:

    Blog:hy592070616

    这里首先声明了一个字符串 'Blog:hy592070616 Corporation:HUAWEI',其中包含英文字母、空格、数字、冒号等。接下来,我们写一个正则表达式'Blog:w{11}'来匹配这个字符串。正则表达式中Blog:就是待匹配字符串的开头,w通过查上表可知是匹配字母、数字及下划线,w后面跟着{11}表示匹配11个w。根据这个规则就可以从字符串 'Blog:hy592070616 Corporation:HUAWEI'中匹配出字符串Blog:hy592070616。同样,我们可以加入s来匹配空格,d来匹配数字以匹配整个字符串。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.match('w{4}:w{2}d{9}sw{11}:w{6}', content)
    5 print(result.group())

    运行结果:

    Blog:hy592070616 Corporation:HUAWEI

    当然,做这种简单的任务我们可以用S来匹配任意非空字符达到相同的效果。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.match('S{16}sS{18}', content)
    5 print(result.group())

    上面几种正则表达式比较复杂,出现空白字符我们就写s匹配,出现数字我们就用d匹配,这样的工作量非常大。其实完全没必要这么做,在上表中有一个万能匹配.*。其中,.可以匹配任意字符(除换行符),*代表字符无限次,所以它们组合在一起就可以匹配任意字符了。有了.*,我们就不用挨个字符地匹配了。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.match('B.*I', content)
    5 print(result.group())

    正则表达式B.*I中的BI代表了字符串Blog:hy592070616 Corporation:HUAWEI首位两个字符,其它的字符均用.*来进行匹配。我们就可以得到相同的结果:

    Blog:hy592070616 Corporation:HUAWEI

    如果想从字符串中提取一部分内容,可以使用()将想提取的子字符串括起来。()实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式会依次对应每一个分组,调用group()方法传入分组的索引即可获取提取的结果。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.match('w{4}:ww(d+)', content)
    5 print(result.group(1))

    运行结果:

    592070616

    可以看到,我们成功得到了592070616。这里用的是group(1),它与group()有所不同。group()会输出完整的匹配结果,而group(1)会输出第一个被()包围的匹配结果。假如正则表达式后面还有()包括的内容,那么可以依次用group(2)、group(3)等来获取。

    若使用.*匹配时,有时候匹配到的并不是我们想要的结果。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.match('B.*(d+)', content)
    5 print(result.group(1))

    运行结果为:

    6 

    很明显,我们希望提取的是592070616,而此处由于我们用了万能匹配符.*在语句中,正则表达式就会匹配59207061,所以在括号中我们就只能提取到一个数字6。这里就涉及一个贪婪匹配与非贪婪匹配的问题了。在贪婪匹配下,.*会匹配尽可能多的字符。正则表达式中.*后面是d+,也就是至少一个数字,并没有指定具体多少个数字,因此,.*就尽可能匹配多的字符,这里就把59207061给匹配了,给d+留下一个可满足条件的数字6。这很明显会给我们带来很大的不便。有时候,匹配结果会莫名其妙少了一部分内容。其实,这里只需要使用非贪婪匹配就好了。非贪婪匹配的写法是.*?,我们需要将将第一个.*改成了.*?就可以转变为非贪婪匹配。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.match('B.*?(d+)', content)
    5 print(result.group(1))

    结果如下:

    592070616

    match()还有个可选参数修饰符,修饰符可以扩展正则表达式的匹配范围。

    比如,在字符串Blog:hy592070616 Corporation:HUAWEI中加入换行,将其变为:

    1 Blog:hy592070616
    2 Corporation:HUAWEI

    用之前的方法就没有办法对其进行匹配:

    1 import re
    2 
    3 content = '''Blog:hy592070616
    4 Corporation:HUAWEI
    5 '''
    6 result = re.match('B.*I', content)
    7 print(result.group())

    这里的result就会返回一个None,因为.*无法匹配换行符。我们只需在match()中的第三个参数加一个修饰符re.S,即可修正这个错误。

    1 import re
    2 
    3 content = '''Blog:hy592070616
    4 Corporation:HUAWEI
    5 '''
    6 result = re.match('B.*I', content, re.S)
    7 print(result.group())

    这样就可以正常返回结果:

    1 Blog:hy592070616
    2 Corporation:HUAWEI

    search()

    match()方法是从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败了。而search()方法在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果。也就是说,正则表达式可以是字符串的一部分,在匹配时,search()方法会依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,如果搜索完了还没有找到,就返回None。比如我们要匹配字符串Blog:hy592070616 Corporation:HUAWEI中的HUAWEI:

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.search('Corporation:(w+)', content)
    5 print(result.group(1))

    就可以得到HUAWEI结果,而不需要将字符串前部全部匹配。

    findall()

    search()方法可以返回匹配正则表达式的第一个内容,但是如果想要获取匹配正则表达式的所有内容,就需要findall()方法。该方法会搜索整个字符串,然后返回匹配正则表达式的所有内容并返回列表。假设我们有HTML文件:

     1 <div id="songs-list">
     2     <h2 class="title">经典老歌</h2>
     3     <p class="introduction">
     4         经典老歌列表
     5     </p>
     6     <ul id="list" class="list-group">
     7         <li data-view="2">一路上有你</li>
     8         <li data-view="7">
     9             <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
    10         </li>
    11         <li data-view="4" class="active">
    12             <a href="/3.mp3" singer="齐秦">往事随风</a>
    13         </li>
    14         <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
    15         <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
    16         <li data-view="5">
    17             <a href="/6.mp3" singer="邓丽君"><i class="fa fa-user"></i>但愿人长久</a>
    18         </li>
    19     </ul>
    20 </div>

    我们希望提取歌手和歌名的信息,则可以:

     1 import re
     2 
     3 html = '''<div id="songs-list">
     4     <h2 class="title">经典老歌</h2>
     5     <p class="introduction">
     6         经典老歌列表
     7     </p>
     8     <ul id="list" class="list-group">
     9         <li data-view="2">一路上有你</li>
    10         <li data-view="7">
    11             <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
    12         </li>
    13         <li data-view="4" class="active">
    14             <a href="/3.mp3" singer="齐秦">往事随风</a>
    15         </li>
    16         <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
    17         <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
    18         <li data-view="5">
    19             <a href="/6.mp3" singer="邓丽君"><i class="fa fa-user"></i>但愿人长久</a>
    20         </li>
    21     </ul>
    22 </div>'''
    23 
    24 result = re.findall('singer="(w+).*>(w+)</a>', html)
    25 print(result)

    我们可以看到,大部分的歌手和歌名是singer="歌手">歌名</a>的形式,则我们可以写正则表达式singer="(w+)">(w+)</a>。但是我们发现有一条 singer="邓丽君"><i class="fa fa-user"></i>但愿人长久</a>中有其他信息,使用上述的正则表达式这无法匹配出(邓丽君,但愿人长久)这个信息。基于这个问题,我们可以将正则表达式修改为singer="(w+).*>(w+)</a>(上述代码中的正则表达式),就可以顺利匹配出所有信息了。

    1 [('任贤齐', '沧海一声笑'), ('齐秦', '往事随风'), ('beyond', '光辉岁月'), ('陈慧琳', '记事本'), ('邓丽君', '但愿人长久')]

    sub()

    除了使用正则表达式提取信息外,有时候还需要借助它来修改文本。比如,想要把一串文本中的所有数字都去掉,这时就可以借助sub()方法。比如,我想将字符串Blog:hy592070616 Corporation:HUAWEI中的Corporation:HUAWEI去掉,就可以匹配Corporation:HUAWEI并用空的字符串替换。

    1 import re
    2 
    3 content = 'Blog:hy592070616 Corporation:HUAWEI'
    4 result = re.sub('Corporation:.*', '',content)
    5 print(result)

    结果如下:

    Blog:hy592070616

    compile()

    除了前面提到的处理字符串的方法以外,最后再介绍一下compile()方法,这个方法可以将正则字符串编译成正则表达式对象,以便在后面的匹配中复用。

     1 import re
     2 
     3 content1 = '2019-01-01 12:00'
     4 content2 = '2019-01-02 12:30'
     5 content3 = '2019-01-03 13:00'
     6 pattern = re.compile('(d+)-(d+)-(d+)s.*')
     7 result_1 = re.match(pattern, content1)
     8 result_2 = re.match(pattern, content2)
     9 result_3 = re.match(pattern, content3)
    10 print('Year:' + result_1.group(1) + '  Month:' + result_1.group(2) + '  Day:' + result_1.group(3))
    11 print('Year:' + result_2.group(1) + '  Month:' + result_2.group(2) + '  Day:' + result_2.group(3))
    12 print('Year:' + result_3.group(1) + '  Month:' + result_3.group(2) + '  Day:' + result_3.group(3))

    这里为了找到各个字符串中的年月日信息构建了正则表达式对象(d+)-(d+)-(d+)s.*以复用,输出结果如下:

     1 import re
     2 
     3 content1 = '2019-01-01 12:00'
     4 content2 = '2019-01-02 12:30'
     5 content3 = '2019-01-03 13:00'
     6 pattern = re.compile('(d+)-(d+)-(d+)s.*')
     7 result_1 = re.match(pattern, content1)
     8 result_2 = re.match(pattern, content2)
     9 result_3 = re.match(pattern, content3)
    10 print('Year:' + result_1.group(1) + '  Month:' + result_1.group(2) + '  Day:' + result_1.group(3))
    11 print('Year:' + result_2.group(1) + '  Month:' + result_2.group(2) + '  Day:' + result_2.group(3))
    12 print('Year:' + result_3.group(1) + '  Month:' + result_3.group(2) + '  Day:' + result_3.group(3))

    私货时间:华为云618大促火热进行中,全场1折起,免费抽主机,消费满额送P30 Pro,点此抢购


    来源:CSDN 作者:洪远

    HDC.Cloud 华为开发者大会2020 即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。

    欢迎报名参会

  • 相关阅读:
    布局重用 include merge ViewStub
    AS 常用插件 MD
    AS 2.0新功能 Instant Run
    AS .ignore插件 忽略文件
    AS Gradle构建工具与Android plugin插件【大全】
    如何开通www国际域名个人网站
    倒计时实现方案总结 Timer Handler
    AS 进行单元测试
    RxJava 设计理念 观察者模式 Observable lambdas MD
    retrofit okhttp RxJava bk Gson Lambda 综合示例【配置】
  • 原文地址:https://www.cnblogs.com/huaweicloud/p/11866459.html
Copyright © 2011-2022 走看看