数据解析基础之正则表达式

一、爬虫网页数据解析

1.1、爬虫常用的数据解析分类

  • 正则表达式(万金油)
  • Beautiful Soup库 (bs4)
  • xpath方法(最常用)

1.2、爬虫数据解析原理概述

  • 其实聚焦爬虫中关注的局部文本内容,通常是在页面HTML源码中的标签之间或者标签对应的属性中。
    • 进行指定标签的定位
    • 标签或者标签对应的属性中存储的数据值进行提取解析

二、正则表达式

2.1、概要

  • 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。通俗理解:就是使用一种规则性的表达式来寻找文本字符串中通用的数据;

2.2、常见语法

2.2.1、点(.)

  • 点(.)可以匹配所有字符:匹配出换行符以外的任意单个字符(按行匹配)
  • 示例 :
import re

content = """苹果是绿色的
橙子是橙色的
香蕉是黄色的
乌鸦是黑色的
"""

p = re.compile(r'.色')
for one in p.findall(content):
    print(one)
  • 注意:
    • re模块是使用正则表达式的python官方库,在使用之前需要进行import,其中re.compile()表示对正则表达式进行编译,将普通的字符串编译为可通配的正则表达式。p.findall(content)用于在文本中通过表达式进行匹配数据。结果如下:

image

2.2.2、星号(*)

  • 星号(*)重复匹配任意次:表示匹配前面的子表达式任意次,包括0次
  • 示例 :
import re

content = """苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,
"""

p = re.compile(r',.*')
for i in p.findall(content):
    print(i)
  • 解析:',.*'表示匹配前面一个字符是全角逗号,后面紧接着多个字符,可以发现结果都是按行匹配的。结果如下
    image

2.2.3、加号(+)

  • 加号(+)重复匹配多次:表示匹配前面的子表达式一次或多次,不包括0次(用法同上)

2.2.4、花括号({})

  • 花括号({})匹配指定次数:表示前面的字符匹配指定的次数,其表示范围(最小次数和最大次数)
  • 示例 :
import re

content = """
红彤彤,绿油油,黑乎乎,绿油油油
"""
p = re.compile(r"绿油{3,4}")
for i in p.findall(content):
    print(i)
  • 解析:'绿油{3,4}'表示匹配绿字且紧接着的油字出现最少3次最多4次的字符串,符合的只有绿油油油和绿油油油油。结果如下:
    image

2.2.5、问号(?)

  • 问号(?)匹配0或1次:表示前面的字符匹配0或1次,等价于

2.2.6、贪婪模式和非贪婪模式

  • 假设我们需要将下面字符串中所有的html标签提取出来:
source = '<html><head><title>Title</title>'

得到这样的列表:
['<html>','<head>','<title>','</title>']

于是我们使用以下代码
import re
p = re.compile(r'<.*>')
print(p.findall(source))

出现的匹配结果如下:
['<html><head><title>Title</title>']
  • 我们代码中r'<.>'表示匹配所以包含左右尖括号,中间含任意个字符的字符串,即我们想要的html标签,但是匹配的结果与我们预想的并不一样,这是为何呢?因为我们前面讲述的、+、?都是贪婪性质的,所谓贪婪,是指其尽可能匹配更多的字符内容。而在这里我们想要的是让其一个标签一个标签的匹配,那就需要非贪婪模式,非贪婪模式其实就在匹配次数后加?即可,这里的话我们使用r'<.*?>'会发现即可将标签一个一个匹配出来。

  • 示例 :

import re

source = '<html><head><title>Title</title>'
p = re.compile(r'<.*?>')
print(p.findall(source))

image

2.2.7、反斜杠(\)

  • 反斜杠(\)对元字符转义:在正则表达式中有多种用途

1、比如说,点表示匹配任意单个字符,如果我们本身需要匹配点这个字符,则需要使用其进行转义

  • 示例:
import re

content = """
苹果. 是绿色的
橙子. 是橙色的
香蕉. 是黄色的
乌鸦. 是黑色的
"""
p = re.compile(r'.*\.')
for i in p.findall(content):
       print(i)
  • 结果
    image

2、反斜杠后面接一些字符,表示匹配某种类型的单个字符:

  • \d 匹配0-9之间任意一个数字字符,等价于表达式 [0-9]

  • \D 匹配任意一个不是0-9之间的数字字符,等价于表达式 [^0-9]

  • \s 匹配任意一个空白字符,包括 空格、tab、换行符等,等价于表达式 [\t\n\r\f\v]

  • \S 匹配任意一个非空白字符,等价于表达式 [^ \t\n\r\f\v]

  • \w 匹配任意一个文字字符,包括大小写字母、数字、下划线,等价于表达式 [a-zA-Z0-9_]。注意:缺省情况也包括 unicode文字字符,如果指定 ASCII码标记,则只包括ASCII字母。

  • 示例:

import re

source = """
tom
tony
jery
"""

p = re.compile(r'\w{2,4}',re.A) # re.A参数表示匹配ASCII字符
print(p.findall(source))

2.2.8、方括号([])

  • 方括号([])匹配单个字符:即匹配其中包含的字符,如[abc]可以匹配abc中的任意一个字符;[a-z]匹配范围内的字符;[^a-z]表示非小写字母。

2.2.9、起始位置和单行、多行模式

  • ^表示匹配文本的起始位置,正则表达式中可以设定单行模式和多行模式。如果是单行模式,表示匹配整个文本的开头位置;如果是多行模式,表示匹配文本每行的开头位置。

  • $ 表示匹配文本的结束位置,单行多行同上,如\d+$

  • 示例 :

import re

content = """
001-苹果价格-60,
002-橙子价格-70,
003-香蕉价格-80,
"""

# 欲匹配编号,如果使用'\d+'则会将价格也匹配到,所以要匹配开头位置的信息
p = re.compile(r'^\d+', re.M) # 添加开头信息标识符,M表示MULTILINE,多行模式
print(p.findall(content))
  • 结果 :
    image

2.2.10、括号(()):组选择

  • 括号(())称之为正则表达式的组选择,即从正则表达式匹配的内容里面扣取出其中的某些需要的部分。

  • 示例 :

import re

content = """
苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
"""

p = re.compile(r'^(.*),', re.M)
for i in p.findall(content):
    print(i) # 打印的是组选择
  • 结果 :
    image

  • 注:如果多行匹配的多个组,则每一行都是一个组选择的元组

  • 示例 :

import re

content = """
张三,手机号码15945678901
李四,手机号码13945677701
王二,手机号码13845666901
"""

p = re.compile(r'^(.+),.+(\d{11})', re.MULTILINE)
for one in  p.findall(content):
    print(one)
  • 结果 :
    image

2.2.11、正则表达式切割字符串

  • 示例 :
import re

names = '关羽; 张飞, 赵云,   马超, 黄忠  李逵'
namelist = re.split(r'[;,\s]\s*', names)
print(namelist)
  • 结果 :
    image

2.2.12、匹配换行符

  • 点(.)不能匹配换行符,如果我们使用时想让其匹配换行符,可以在compile()方法中添加re.DOTALL参数。

2.2.13、匹配模式替换

1、使用sub方法进行替换

  • 示例 :
import re

names = '''
下面是这学期要学习的课程:
<a href='https://www.bilibili.com/video/av66771949/?p=1' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是牛顿第2运动定律
'''
newStr = re.sub(r'/av\d+?/', '/cn345677/', names)
print(newStr)
  • 结果 :
    image

2、使用指数函数替换

  • 示例 :
import re

names = '''
下面是这学期要学习的课程:
<a href='https://www.bilibili.com/video/av66771949/?p=1' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是牛顿第2运动定律
<a href='https://www.bilibili.com/video/av46349552/?p=125' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是毕达哥拉斯公式
<a href='https://www.bilibili.com/video/av90571967/?p=33' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是切割磁力线
'''
# 替换函数,参数是 Match对象
def subFunc(match):
    # Match对象 的 group(0) 返回的是整个匹配上的字符串
    src = match.group(0)

    # Match对象 的 group(1) 返回的是第一个group分组的内容
    number = int(match.group(1)) + 6
    dest = f'/av{number}/'

    print(f'{src} 替换为 {dest}')

    # 返回值就是最终替换的字符串
    return dest

newStr = re.sub(r'/av(\d+?)/', subFunc , names)
print(newStr)
  • 结果 :
    image
posted @ 2021-05-03 14:29  SRE运维充电站  阅读(305)  评论(0编辑  收藏  举报