python正则表达式re模块入门,贪婪匹配和非贪婪匹配,案例:猫眼电影TOP100信息提取

目录

正则表达式:re模块

元字符

正则表达式如何匹配任意字符:re.S

贪婪匹配和非贪婪匹配

1.贪婪匹配

2.非贪婪匹配

正则表达式的分组

猫眼电影TOP100信息提取

1.需求分析

2.代码分析 

3.编写程序


正则表达式:re模块

re模块有两种方式实现正则匹配

方式一:

lists=re.findall("he","hello world")

方式二:

pattern=re.complie("he")

lists=pattern.findall("hello world")

这两种方法都可以正则匹配字符,只是第二种方式提前定义了正则表达式,可以复用,所以推荐使用第二种方式

 

元字符

正则表达式常用的元字符如下:

.匹配任意一个字符,除了\n
*匹配0个或多个的字符串
+匹配1个或多个的字符串
匹配0个或1个,为非贪婪方式
[a, b , c]匹配 ‘a’ 或 ‘b’ 或 ‘c’
\s匹配 任何 空白字符, 相当于[\t\n\r\f]
\S匹配 任何 非空白字符, 相当于[^\t\n\r\f]

 

正则表达式如何匹配任意字符:re.S

通常来说,如果我们要匹配任意字符,可以这样写(方式一):

pattern=re.complie("[\s\S]")

lists=pattern.findall("hello\nworld")

这样写可以匹配到\n,看下面这种写法,这个写法并不会匹配\n这个换行符。一个页面有非常多的换行符,所以这么写时不合理的。

pattern=re.complie(".*")

lists=pattern.findall("hello\nworld")

那么我们可以使用re.S,如下,re.S代表允许'. '匹配'\n'(方式二,推荐):

pattern=re.complie(".*",re.S)

lists=pattern.findall("hello\nworld")

 

贪婪匹配和非贪婪匹配

1.贪婪匹配

在整个表达式匹配成功的情况下,尽可能的多匹配*或 + 或 ?。

表达方式:.* 或 .+ 或 .?

2.非贪婪匹配

在整个表达式匹配成功的情况下,尽可能的少匹配* 或 + 或 ?。

表达方式:.*? 或 .?? 或 .+?

 

贪婪匹配和非贪婪匹配到底有什么含义能,看下面的例子:

编写这么一段代码,我们预期的结果是得到几个集合,集合里有两个对象,<div><p>今天天气不错</p></div>和<div><p>太阳很舒服</p></div>

import re

str="""
<div><p>今天天气不错</p></div>
<div><p>太阳很舒服</p></div>
"""

pattern=re.compile("<div><p>.*</p></div>",re.S)
lists=pattern.findall(str)

print(lists)

但是结果却成了一个对象,这是因为在贪婪匹配模式下,由于‘.’会匹配任意字符(它会认为</p></div>也是任意字符),所以它会匹配到最后一个以‘</p></div>’结尾的字符串。

再看下面的一段代码,稍一看似乎没什么区别,眼睛尖的是能发现区别的,在.*后面加了一个?号,刚才说了这是非贪婪表达式方式,而非贪婪匹配的匹配模式是匹配最近的以</p></div>结尾的字符串(仅在这个案例中),这样的匹配模式正好符合我们的预期结果。

import re

str="""
<div><p>今天天气不错</p></div>
<div><p>太阳很舒服</p></div>
"""

pattern=re.compile("<div><p>.*?</p></div>",re.S)
lists=pattern.findall(str)

print(lists)

结果:

 

总结:在正则表达式中,绝大数情况会使用非贪婪匹配,非常好理解,我们需要的内容是一个装满了成功匹配的对象集合,而不是一个连在一起的对象集合(而且在多数情况下返回的结果总是会与你预想的结果有区别)。

 

正则表达式的分组

还是上面那个案例,我们刚才得到的结果如下图,我们仅需要<div></div>中间的内容该如何处理,这就需要用的正则表达式分组。

正则表达式分组是指在完整的的模式中定义子模式,将每个用圆括号中的子模式作为结果提取出来

实际运用非常简单,还是上面的代码,我们只需要在.*?包在括号里即可,如下: 

import re

str="""
<div><p>今天天气不错</p></div>
<div><p>太阳很舒服</p></div>
"""

pattern=re.compile('<div><p>(.*?)</p></div>',re.S)
lists=pattern.findall(str)

做完上面的案例后,我们在具体来聊一下正则表达式的分组,请看下面3个案例:

import re


str='A B C D'
pattern2=re.compile("\w+\s+\w+")
lists2=pattern2.findall(str)

pattern3=re.compile("(\w+)\s+\w+")
lists3=pattern3.findall(str)

pattern4=re.compile("(\w+)\s+(\w+)")
lists4=pattern4.findall(str)

print(lists2)
print(lists3)
print(lists4)

先看第一组正则表达式:re.compile("\w+\s+\w+"),我们知道\w匹配任意字母和数字,+号匹配一个或多个,我们定义的str='A B C D',按照匹配规则,\w+将匹配一串连续的字符,而这里的字符用空格隔开了,所以只会匹配一个字符。\s+匹配任何空白字符,最后的\w+匹配一个字符。那么总结下来就是这样的匹配规则:字符 空格 字符。结果也是如此。

再看第二组正则表达式:re.compile("(\w+)\s+\w+"),与第一组的区别在与第一个\w+加了括号,从结果上区别就是只匹配到了一个字符。我们在上面的案例中讲了正则表达式的分组,这里的括号就是给\w+做分组,然它成为子模式。在子模式下只取出子模式的匹配内容作为结果,这里的\w+的匹配结果是A,所以输出A。

第三组:re.compile("(\w+)\s+(\w+)")。第三组有两个括号,代表有两个子模式。两个子模式会以元组的方式输出。

 

总结:

1.正则表达式的分组是通过加()来实现的

2.如果只想取匹配结果的某一段内容,为这一段内容的匹配模式加上()

3.有两个()将会以元组的方式进行输出

 

猫眼电影TOP100信息提取

 

1.需求分析

猫眼电影TOP100榜单URL:https://maoyan.com/board/4?offset=0

页面详情如下,我们要提取的信息有电影名称、主演、上移时间和评分。

因为是top100,每页显示10个,总计10页。分析URL得到第一页为https://maoyan.com/board/4?offset=0,第二页为https://maoyan.com/board/4?offset=10。

所以页码格式为offset=(page-1)*10

 

2.代码分析 

提取信息的关键在与写对正则表达式,如图所示,我们要提取的信息有划红线的部分。

 

经过整理后得出的信息如下,我们要以这一串内容写一段正则表达式。首先我们将需要提取的字符打上分组符号()。要注意的是电影名有两处显示,我们选择title里的;评分是分为两段显示的,我们都要打上标记。切忌不要把他写成一行,目前这一段格式包含了\n换行符,并且每一个位置都是精确的,不易改动。

<div class="movie-item-info">
        <p class="name"><a href="/films/1203" title="霸王别姬" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p>
        <p class="star">
                主演:张国荣,张丰毅,巩俐
        </p>
<p class="releasetime">上映时间:1993-07-26</p>    </div>
    <div class="movie-item-number score-num">
<p class="score"><i class="integer">9.</i><i class="fraction">5</i>

打完之后如下,现在这一段正则表达式仅可以匹配霸王别姬这一段内容,我们要想办法让他匹配所有。 

<div class="movie-item-info">
        <p class="name"><a href="/films/1203" title="(.*?)" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p>
        <p class="star">
                (.*?)
        </p>
<p class="releasetime">(.*?)</p>    </div>
    <div class="movie-item-number score-num">
<p class="score"><i class="integer">(.*?)</i><i class="fraction">(.*?)</i>

我们将多余的部分用.*?代替。得到如下格式。刚才说了,因为有换行符的存在,所以在换行出也要打上.*?

<div class="movie-item-info">
        .*?title="(.*?)".*?
        <p class="star">
                (.*?)
        </p>.*?
<p class="releasetime">(.*?)</p>.*?
<i class="integer">(.*?)</i><i class="fraction">(.*?)</i>

最终经过整理后,得到下面内容。这里不要留空行,挤压成一段字符串。

<div class="board-item-main">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>.*?<i class="integer">(.*?)</i><i class="fraction">(.*?)</i>

 

3.编写程序

1.定义一个类

2.初始化类,定义url,headers,正则匹配表达式

3.定义方法:get_html()用于获取页面,注意转码

4.定义方法:run(),作为类的入口函数,提示输出页面。显示结果需要进一步处理,去除空格符

from urllib import request
import re

class Maoyan_spider(object):

    def __init__(self):
        self.url="https://maoyan.com/board/4?offset={}"
        self.headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"}
        self.pattern=re.compile('<div class="board-item-main">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)'
                   '</p>.*?<i class="integer">(.*?)</i><i class="fraction">(.*?)</i>',re.S)

    #获取页面
    def get_html(self,url):
        req = request.Request(url=url, headers=self.headers)
        rep = request.urlopen(req)
        html = rep.read().decode("utf-8")
        return html

    #入口函数
    def run(self):
        page=(int)(input("请输入页码数:"))
        #计算页面
        offset=(page-1)*10
        #拼接url
        url=self.url.format(offset)
        html=self.get_html(url)
        lists=self.pattern.findall(html)
        for i in lists:
            print("电影名:" + i[0].strip())
            print(i[1].strip())
            print(i[2].strip())
            print("评分:" + i[3] + i[4])


if __name__ == "__main__":
    maoyan=Maoyan_spider()
    maoyan.run()

 最终效果如下:

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值