爬虫教程-第三章:信息提取之正则表达式

3.3 正则表达式

前面两篇博客介绍了lxmlBeautifulSoup 对信息的提取,这节来介绍最后一种提取信息的方式----正则表达式。

正则表达式相信大家都听过,那什么是正则表达式呢?通俗的讲,正则表达式就是按照一定的规则,从某个字符串中匹配出满足规则的字符串

先来简单的看一下正则表达式的使用:

import re

text = 'hello re'
reg = re.match('he',text)  
print(reg.group())  # 输出he

match(pattern,string) 作用:在string中匹配以pattern 开头的字符串,若匹配到则返回pattern,否则返回None.

group() 作用:暂时知道它是返回匹配到的字符串就可以了。后面再详细讲。


接下来看看一些在正则表达式中具有特殊意义的字符。

  1. 匹配单个字符
. 匹配除换行符以外的任意字符                         
[] 匹配里面的任意一个字符                           
[^ ] 除了里面的任意一个字符                         
\d 匹配数字(大写的意思就是与小写相反)     如 \D 表示除了数字的都匹配         
\w 等价于 [0~9A~Za~z_]					   \W 表示除了[0~9A~Za~z_]都匹配
\s 匹配空白符,如[\f\n\r\t ] (空白也在里面)   \S  同理

[0~9A~Za~z_] 表示匹配0到1、A到Z、a到z、_ 中的任意一个。

\D 等价于 [^0~9][^\d]

tips:如果要匹配一些符号,比如/ 、, < > 等,可以使用 \S 来匹配

import re

text = '12A'
reg = re.match('\d',text)  
print(reg.group())  # 输出1
phone = '125421'
print(re.match('[21]',phone).group())  # 输出1
# print(re.match('[245]',phone).group())  # 报错,说明没找到以2或4或5开头的字符串

tips:若string 中拥有特殊意义的字符时需转义r''

pattern 中拥有特殊意义的字符时需要使用 \ 转义。因为python 本身要转义一次,放到正则还要在转义一次。所以 匹配\ 需写成 '\\\\' ,匹配+ 需写成 '\+'

  1. 锚字符:

    ^ 以…开头(与[]里面的^不是一个意思)
    $ 以…结尾

    import re
    
    text1 = 'hello re'
    # 以he开头 等价于match
    reg = re.search('^he', text1)  # search用法后面有详解
    print(reg.group())
    
    # 以re结尾
    r = re.search('re$', text1)
    print(r.group())
    
  2. 匹配多个字符:

    特殊符号作用例子
    *匹配任意多个字符x* 匹配任意个x
    ?匹配0个或1个字符(解除贪婪模式)x? 匹配0个或1个x
    +匹配1个或多个字符(最少都要匹配到1个)x+ 匹配1个或多个x
    ()将括号里面的字符看成一个整体(xyz) 匹配xyz
    {n}匹配n个字符x{4} 匹配4个x
    {n,m}匹配n~m个字符x{2,5} 匹配2到5个x
    |x|y 匹配x或者y

小练习

import re

# 1.验证手机号码
phone = '14512545888'
reg = re.match(r'1[345678]\d{9}', phone)
print(reg.group())

1[345678]\d{9} 表示 第一位为1,第二位可以是345678的任意一个,接下来的9位是任意数字。


# 2.验证邮箱(邮箱可以包含小数点)
email = '524615@qq.com'
em = re.match(r'[\w.]+@\w+\.\w+', email)
print(em.group())

email2 = r'bill.gates@microsoft.com'
em2 = re.match(r'[\w.]+@\w+\.com$', email2)
print(em2.group())

[\w.]+ 表示 至少要匹配到\w.

@ 表示 匹配@

\w+\.com$ 表示 . 前面至少有一个字符,. 后面必须以com结尾


#3.验证URL
URL = 'https://baidu.com/item/python'

u = re.match(r'(http|https|ftp)://\w+\.\S+', URL)
print(u.group())

(http|https|ftp) 表示匹配http、https、ftp的其中一个。注意这里不要使用 [http|https|ftp] 因为此时匹配的是[]里面的任意一个字符。


贪婪模式:尽可能多的匹配字符。

何为贪婪模式?比如 有一个字符串 <h1>段落</h1> 若你使用<\w+> 去匹配,则会匹配到整个字符串,这就叫贪婪模式。而如果你此时只想匹配到<h1> ,可以<\w+?> 加个问号解除贪婪匹配。


group分组

用 () 表示要提取的分组 组的排序:从外到内,从左到右

import re

text = '154-555'
m = re.search(r"(?P<name1>\d{3})-(?P<name2>\d{3})", text)

print(m.group('name1'))  # 154
print(m.group(0), '***', m.group())  # 154-555 *** 154-555
print(m.group(1))  # 154
print(m.group(2))  # 555
print(m.groups())  # ('154', '555')

注意:

  1. ?P<name1> 是给\d{3} 所匹配到的该组字符串取名为name1,而不是匹配的意思。
  2. 需要注意 groups()m.group() 的区别

re模块常用函数:

函数作用
match(pattern,string[,flags=0])从string开头匹配pattern,有则返回,不是开头或者没有返回none
search(pattern,string[,flags=0])返回第一个匹配成功的pattern
findall(pattern,string[,flags=0])返回一个所有成功匹配数据的列表
finditer(pattern,string[,flags=0])返回一个所有成功匹配数据的迭代器,可以用next()迭代
sub(pattern,repl,string[,count=0,flags=0])返回在string中利用repl替换满足pattern的字符串,count表示替换多少个满足的字符串
split(pattern, string[, maxsplit])按照满足pattern的字符串将string分割后返回列表

flags

编译标志位,用于修改正则表达式的匹配方式,如:是否区分大小写,多行匹配等。

  • re.I 忽略大小写
  • re.M 多行匹配,影响^和$
  • re.S 或者**re.DOTALL** 使. 可以匹配包括换行符在内的所有字符
import re

s= '12 34\n56 78\n90'
re_f = re.findall(r'^\d+', s, re.M)  # 匹配每行的开头
print(re_f)  # ['12', '56', '90']
print(re.search(r'.+', s, flags=re.S).group())  # 12 34\n56 78\n90

compile

在上表的前四个函数执行之前,都会执行一个compile函数,即正则表达式会先编译模式(即前面的pattern)是否正确然后再进行匹配。

reg = re.search(r'.+', s, re.S)
等价于
re_com = re.compile(r'.+', re.S)
reg = re_com.search(s)

看到这里你也许会想,直接search就完事了,compile 还需要两步这么麻烦,还不如直接用search 呢。我一开始也是这么觉得的,知道后来,我发现事情并不那么简单。

如果你需要对许多的字符串进行相同模式的匹配,此时compile就派上了大用处,你只需要编译一次就可以一直使用。不信你看我下面:

re_com = re.compile(r'.+')
reg1 = re_com.search(s1)
reg2 = re_com.search(s2)
reg3 = re_com.search(s3)
......

而如果你直接search的话,就要编译好多次,如果你要匹配几百万条数据,每条都要编译,那得花多少时间啊。


sub

string = 'I must be 1个 shuaige.'
string = re.sub(r'\d个', '', string)  # 把 1个 删除
print(string)  # I must be  shuaige.

split:

text = 'hello$$world'
reg = re.split('\W', text)
print(reg)  # ['hello', '', 'world']

综合案例爬取哔哩哔哩热门视频

使用正则表达式进行匹配时,不会像前两章的那两种提取方式有一种结构在里面,可以直接通过方法来筛选,正则能匹配的只有字符串,也就是说即使你匹配的整个网页,在正则看来就是一串很长的字符串而已。所以我们来转变下思路,不要先一个一个的爬取,而是把全部的标题,链接、观看数等一次性爬取下来,分别放到数组里,然后再对数组进行整合即可得到相同效果。

在这里插入图片描述

import requests
import re

# 输出视频的详情 如链接、标题、播放量、评论数、作者
def print_detail(msg_lists):
    for i in msg_lists:
        print(i)

# 解析页面
def html_parse(url):
    msg_lists = []  # 存放所有的视频信息

    res = requests.get(url)
    text = res.text
    # 获取链接跟标题
    msg = re.findall(r'<a href="(.*?)".*?class="title">(.*?)</a>', text)
    # 获取播放量、评论数以及作者名称
    detail = re.findall(r'<span class="data-box">.*?</i>(.*?)</span>', text)

    # 获取前20个视频信息
    for i in range(20):
        detail_dist = {}  # 每次存放单个视频的详细信息

        detail_dist['视频链接'] = msg[i][0]  # 存储视频链接
        detail_dist['标题'] = msg[i][1]  # 存储标题
        detail_dist['播放量'] = detail[i * 3]  # 存储播放量
        detail_dist['评论数'] = detail[i * 3 + 1]  # 存储评论数
        detail_dist['作者'] = detail[i * 3 + 2]  # 存储作者
        msg_lists.append(detail_dist)  # 把每个视频信息存放到列表中

    # 输出视频信息
    print_detail(msg_lists)


if __name__ == '__main__':
    url = 'https://www.bilibili.com/ranking'
    html_parse(url)

分析正则表达式

<a href="(.*?)".*?class="title">(.*?)</a>

对需要获取的信息加上 () 匹配成功时就会返回该括号内匹配到的内容。

.*? 表示提取任意多的除换行外的字符,问号的作用是接触贪婪模式。此时"(.*?)" 模式就会告诉正则说,“ ” 里面的内容随便你匹配,匹配到了就返回给我。

.*?class 表示前面的内容随便你匹配,反正到了class这里你就给我停止。

所以,msg[i] 存放了第i个视频的链接跟标题。

<span class="data-box">.*?</i>(.*?)</span>

你分析了页面之后,你会发现,这个正则表达式第一次匹配到了播放量、第二次匹配到了评论数、第三次匹配到了作者名称,第四次才再次匹配到下一条视频的播放量,第五次才再次匹配到下一条视频的评论数….

所以才会有 detail[i * 3] detail[i * 3 + 1] detail[i * 3 + 2]


小结

  1. 正则表达式没有那种层次结构,在它的世界里只有字符串。
  2. 在需要匹配的地方加()

好啦,正则表达式就介绍到这里,小伙伴们要多多练习,有问题可以在评论区一起讨论。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值