爬虫学习记录

下面会用到到网站https://regex101.com/

正则表达式的函数

python提供了一个re模块这个模块提供了很多的方法和函数

match()函数

使用方法

他的格式,他有三参数,这个函数会从起始位置进行匹配如果匹配失败就返回none

1
re.match(匹配的正则表达式, 要匹配的字符串, 可选参数标志位控制匹配的比如是否区分大小写等)
  • 正则表达式修饰符 - 可选标志

    修饰符 描述
    re.I 使匹配对大小写不敏感
    re.L 做本地化识别(locale-aware)匹配
    re.M 多行匹配,影响 ^ 和 $
    re.S 使 . 匹配包括换行在内的所有字符
    re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
    re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

实测

匹配错误返回

代码

1
2
3
4
5
6
7
8
9
import  re

a="babc"
b="abc def ghi"

# a和b进行匹配,前面多一个b不匹配
c=re.match(a,b)
# 输出结果
print(c)

结果:返回一个None

image-20210418085308715

匹配正确返回

代码

1
2
3
4
5
6
7
8
9
import  re

a="abc"
b="abc def ghi"

# a和b进行匹配
c=re.match(a,b)
# 输出结果
print(c)

结果:匹配正确,返回一个对象

image-20210418084957128

方法

他进行是返回一个对象那么我们就可以用里面的方法了

查看里面的方法可以用dir函数

可以看见有很多的方法

image-20210418090626506

下面我列出几个常用的方法

常用的方法 描述
group() 返回匹配的字符串
span() 返回匹配的范围
start() 返回从那开始匹配的
groups() 返回全部分组
  1. group()选择返回的字符串

    实测

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import  re

    a="abc"
    b="abc def ghi"

    # a和b进行匹配
    c=re.match(a,b)

    # 输出匹配的字符串
    print(c.group())

    结果

    1
    abc

    image-20210418090829379

  2. span()返回匹配的范围

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import  re

    a="abc"
    b="abc def ghi"
    # a和b进行匹配
    c=re.match(a,b)

    # 输出匹配的范围
    print(c.span())

    结果

    1
    (0, 3)

    image-20210418091042017

  3. start()返回从那开始匹配的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import  re

    a="abc"
    b="abc def ghi"
    # a和b进行匹配
    c=re.match(a,b)

    # 输出从那开始匹配的
    print(c.start())

    结果

    1
    0

    image-20210418091516259

第三个可选参数标志位

比如代码是下面这样的

1
2
3
4
5
6
7
8
import  re

a="ABc"
b="abc def ghi"
# a和b进行匹配
c=re.match(a,b)

print(c)

结果,应为他是AB和ab是不匹配的

1
None

我们就可以用到参数标志位,不区分大小写

代码

1
2
3
4
5
6
7
8
import  re

a="ABc"
b="abc def ghi"
# a和b进行匹配,添加上re.I不区分大小写
c=re.match(a,b,re.I)

print(c)

结果

1
<re.Match object; span=(0, 3), match='abc'>

image-20210418091949404

字符的使用

字符 描述
. 匹配任意一个字符(除了\n)
[] 匹配列表中的内容(点就是点而不是如何字符)
\w 匹配字母、数字及下划线比如a-z,A-Z,_
\W 匹配不是字母、数字及下划线的字符
\s 匹配任意空白字符,等价于 [\\t\\n\\r\\f]
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9]
\D 匹配任意非数字的字符
\Z 匹配字符串结尾,如果有换行会往下匹配
\A 匹配字符串开头
^ 匹配一行字符串的开头
$ 匹配一行字符串的结尾
\n 匹配一个换行符
\t 匹配一个制表符
* 匹配 0 个或多个表达式
+ 匹配 1 个或多个表达式
? 匹配 0 个或 1 个前面的正则表达式定义的片段(非贪婪方式)
{n} 精确匹配 n 个前面的表达式
a|b 匹配 ab
( ) 匹配括号内的表达式,也表示一个组
\ 转义符

全部的实测

.匹配任意一个字符

这个点.就相当于如何字符

1
2
3
4
5
6
7
8
9
10
import re

a='abc'
c=re.match('.',a)

# 输出对象
print(c)

# 输出被匹配的内容
print(c.group())

结果

1
2
<re.Match object; span=(0, 1), match='a'>
a

[]匹配列表中的内容

这个[]匹配列表中的内容,比如[15]就是只匹配15,要是[1-5]就是只匹配15,如果是[a-z]就是匹配到a到z

可以看到他只匹配15

image-20210420074431747

代码测试

1
2
3
4
5
6
7
8
9
10
11
import re

a='4'

c=re.match('[1-5]',a)

# 输出对象
print(c)

# 输出被匹配的内容
print(c.group())

结果:匹配1到5到的数字4也是这个范围的

1
2
<re.Match object; span=(0, 1), match='4'>
4

\w匹配匹配字母数字下划线

演示可以看见数字和字母都可以匹配到

image-20210420081022642

代码测试

1
2
3
4
5
6
7
8
9
10
11
12
import re

a='a1'

# 应为这个函数只能一个一个的匹配我这个就用到了两个\w
c=re.match('\w\w',a)

# 输出对象
print(c)

# 输出被匹配的内容
print(c.group())

结果

1
2
<re.Match object; span=(0, 2), match='a1'>
a1

\W匹配不是字母数字下划线

可以看见下面字符都是可以匹配的,他是和小写的\w是对立的

image-20210421081511484

\s匹配任意空白字符

\s匹配任意空白字符匹配任意空白字符,等价于 [\\t\\n\\r\\f]

image-20210421081811687

\S匹配任意非空字符

这个只要不是空就匹配上和小写的\s是对立的

image-20210421081915172

\d匹配任意数字

\d匹配任意数字,等价于 [0-9]

image-20210421082203149

\D匹配任意非数字的字符

他是和小写的\d是对立的

image-20210421082319941

\Z匹配字符串结尾

这个\Z是和$有差别的

  • \Z匹配字符串结尾,如果他匹配到换行符也会跟着换行符往下匹配
  • $只会匹配到换行符前的

image-20210421085745512

\A匹配字符串开头

这个\A^差不多

  • ^是匹配一行字符串的开头
  • \A匹配字符串开头

image-20210421082918321

代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding: utf-8 -*-
import re


a='aaabbbccc'

c=re.match('\Aaaa',a)

# 输出对象
print(c)

# 输出被匹配的内容
print(c.group())

结果

1
2
<re.Match object; span=(0, 3), match='aaa'>
aaa

^匹配一行字符串的开头

这个\A^差不多

  • ^是匹配一行字符串的开头
  • \A匹配多行字符串开头

image-20210421091657333

$匹配一行字符串的结尾

他和^是对立的

这个\Z是和$有差别的

  • \Z匹配字符串结尾,如果他匹配到换行符也会跟着换行符往下匹配
  • $只会匹配到换行符前的

image-20210421091914804

\n匹配换行符

image-20210421172308336

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
import re

pattern='蔡徐坤 135000000\n01 18岁'

# 输出pattern变量的结果
print(pattern)

# 匹配换行符
x =re.findall('\n',pattern)

# 输出结果
print(x)

结果

1
2
3
蔡徐坤 135000000  
01 18岁 # 可以看见换行了
['\n'] # 匹配到了换行符

\t匹配制表符

这个演示不了

*匹配0个或多个

*+差不多

  • *匹配0个或n个 ,如果一个没有匹配到也不会返回None
  • +匹配1个或n个,如果一个都没有匹配到他就会返还None

代码

1
2
3
4
5
6
7
import re

pattern='aaaaaaa'
# 匹配一个a
x =re.match('a',pattern)
# 输出匹配的结果
print(x.group())

结果:可以看见他就匹配一个a

1
a

如果我添加一个*那就可以叫后面的a都匹配上

1
2
3
4
5
6
7
import re

pattern='aaaaaaa'
# 匹配后面全部的a
x =re.match('a*',pattern)
# 输出匹配的结果
print(x.group())

结果:可以看见后面的a都匹配上了

1
aaaaaaa

如果我有个不匹配也不会返回None

代码

1
2
3
4
5
6
7
8
import re

pattern='aaaaaaa'

# 匹配一个没有的b
x =re.match('b*',pattern)

print(x)

结果:他结果并没有返回None

1
<re.Match object; span=(0, 0), match=''>

+匹配1个或多个

*+差不多

  • *匹配0个或n个 ,如果一个没有匹配到也不会返回None
  • +匹配1个或n个,如果一个都没有匹配到他就会返还None

代码

1
2
3
4
5
6
7
import re

pattern='aaaaaaa'
# 匹配一个a
x =re.match('a',pattern)
# 输出匹配的结果
print(x.group())

结果:可以看见他就匹配一个a

1
a

如果我添加一个*那就可以叫后面的a都匹配上

1
2
3
4
5
6
7
import re

pattern='aaaaaaa'
# 匹配后面全部的a
x =re.match('a+',pattern)
# 输出匹配的结果
print(x.group())

结果:可以看见后面的a都匹配上了

1
aaaaaaa

如果我有个不匹配就会返还None

代码

1
2
3
4
5
6
7
8
import re

pattern='aaaaaaa'

# 匹配一个没有的b
x =re.match('b+',pattern)

print(x)

结果

1
None

?匹配 0 个或 1 个

在一个?他不是贪婪模式,他是前面的正则表达式定义的片段

?他是匹配 0 个或 1 个,如果匹配不到也不会返回None

代码

1
2
3
4
5
6
7
8
import re

pattern='abcdef12121212'

# 匹配一个存在的
x =re.match('a?',pattern)

print(x)

结果:返回他匹配到的

1
<re.Match object; span=(0, 1), match='a'>

我让他匹配一个没有的他也不会返回None

代码

1
2
3
4
5
6
7
8
import re

pattern='bcdef12121212'

# 匹配一个不存在的
x =re.match('a?',pattern)

print(x)

代码

1
<re.Match object; span=(0, 1), match=''>

如果我不添加?程序就返回None

代码

1
2
3
4
5
6
7
8
import re

pattern='bcdef12121212'

# 匹配一个不存在的
x =re.match('a',pattern)

print(x)

结果

1
None

{n}匹配自己指定次数

  1. 测试指定指定次数

    指定匹配多少

    1
    2
    3
    4
    5
    6
    7
    8
    import re

    pattern='12121212bcdef'

    # 匹配3个数字
    x =re.match('\d{3}',pattern)

    print(x)

    结果:可以看见他匹配了三个

    1
    <re.Match object; span=(0, 3), match='121'>
  2. 指定匹配范围

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    import re

    pattern='12121212bcdef'

    # 匹配2-5个数字
    x =re.match('\d{2,5}',pattern)

    print(x)

    结果:应为他都是按照最多进行匹配所以会匹配到2个

    1
    <re.Match object; span=(0, 5), match='12121'>
  3. 匹配最少多几个

    1
    2
    3
    4
    5
    6
    7
    8
    import re

    pattern='12121212bcdef'

    # 匹配最少2个数字,最多不限制
    x =re.match('\d{2,}',pattern)

    print(x)

    结果:应该,后面不添加就是无限

    1
    <re.Match object; span=(0, 8), match='12121212'>

a|b匹配 ab

比如我们想执行匹配w和m那就可以用到|符号了

1
2
3
4
5
6
7
import re

pattern='wdsadasdsa'
# 匹配第一个字符w或者m
x =re.match('w|m',pattern)

print(x)

结果

1
<re.Match object; span=(0, 1), match='w'>

( )分组

学习网址https://www.bilibili.com/video/BV1N64y1u7Ty?p=10

基本分组

什么是分组,比如我们匹配一个字符串他只能匹配输出一组,我们可以用()进行分组

演示

代码

1
2
3
4
5
6
7
8
import re

pattern='zhao:2737977997'

x =re.match('\w*:\d*',pattern)

print(x)
print(x.group())

结果

1
2
<re.Match object; span=(0, 15), match='zhao:2737977997'>
zhao:2737977997

比如我们想叫zhao2737977997进行分开我们就可以用()进行分组

代码

1
2
3
4
5
6
7
8
9
import re

pattern='zhao:2737977997'

x =re.match('(\w*):(\d*)',pattern)

print(x)
# 输出全部分组
print(x.groups())

结果

1
2
<re.Match object; span=(0, 15), match='zhao:2737977997'>
('zhao', '2737977997')

我们还可以选择要选择输出的分组用group()group(1)或者groups()[1]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re

pattern='zhao:2737977997'

x =re.match('(\w*):(\d*)',pattern)

print(x)
# 用group(1)指定输出分组
print("用group(1)指定输出分组")
print(x.group(1))
print(x.group(2))

# 用groups()[0]指定输出分组
print("用groups()[0]指定输出分组")
print(x.groups()[0])
print(x.groups()[1])

结果

1
2
3
4
5
6
7
<re.Match object; span=(0, 15), match='zhao:2737977997'>
用group(1)指定输出分组
zhao
2737977997
用groups()[0]指定输出分组
zhao
2737977997

分组引用

比如我们想匹配一个HTML代码

1
2
3
4
5
6
7
import re

pattern='<bead>匹配</h1>'

x =re.match('<.+>.+</.+>',pattern)

print(x)

结果

1
<re.Match object; span=(0, 28), match='<bead>匹配</bead>'>

上面的代码如果我匹配不同的比如<bead>匹配</h1>上面的代码他也会被匹配到

代码

1
2
3
4
5
6
7
import re

pattern='<bead>匹配</h1>'

x =re.match('<.+>.+</.+>',pattern)

print(x)

结果

1
<re.Match object; span=(0, 13), match='<bead>匹配</h1>'>

如果我们只匹配对应的HTML比如<bead></bead>这样对应如果不对应就不匹配我们就可以用分组引用

代码

1
2
3
4
5
6
7
8
import re

pattern='<bead>匹配</h1>'

# \1就是分组1,我又添加一个防止转义r不让\转义
x =re.match(r'<(.+)>.+</\1>',pattern)

print(x)

结果

1
<re.Match object; span=(0, 15), match='<bead>匹配</bead>'>

如果我改成

1
2
3
4
5
6
7
8
import re

pattern='<bead>匹配</bead>'

# \1就是分组1,我又添加一个防止转义r不让\转义
x =re.match(r'<(.+)>.+</\1>',pattern)

print(x)

结果

1
None

引用起别名

起别名格式(?p<起的名字>表达式)

引用(?p=引用的分组名字)

代码

1
2
3
4
5
6
7
8
import re

pattern='<bead>匹配</bead>'

# 我又添加一个防止转义r不让\转义
x =re.match(r'<(?P<dead>.+)>.+</*(?P=dead)>',pattern)

print(x)

结果

1
<re.Match object; span=(0, 15), match='<bead>匹配</bead>'>

如果我匹配到不一样比如<bead>匹配</h1>

代码

1
2
3
4
5
6
7
8
import re

pattern='<bead>匹配</h1>'

# 我又添加一个防止转义r不让\转义
x =re.match(r'<(?P<dead>.+)>.+</*(?P=dead)>',pattern)

print(x)

结果

1
None

\转义符

比如我们想输入\n这个,在代码里面不能直接输入\n要通过\来转义了

在python里面可以在前面添加上一个r防止字符转义的,可以省略\转义符下面有演示

看一下我直接用\n

  1. \n转义符

    代码

    1
    print('\nabc')

    结果:可以看见他是回车

    1
    2

    abc

    我们就可以用\来转义

    代码

    1
    print('\\nabc')

    结果:可以看见这样就可以了

    1
    \nabc
  2. r预防转义

    Python也可以用r预防转义

    代码

    1
    print(r'\nabc')

    结果:前面添加一个r就可以预防转义了

    1
    \nabc

在正则表达式里面又如果匹配字符串里面的正则字符那

  1. 正则符号匹配

    1
    2
    3
    4
    5
    6
    7
    8
    import re

    pattern='*das'

    # 在正则里面*是匹配0次或多次,
    x =re.match('*',pattern)

    print(x)

    结果:就报错了,应为在正则里面*是匹配0次或多次的意思

    1
    2
    3
    4
    5
      File "/usr/lib/python3.9/sre_parse.py", line 443, in _parse_sub
    itemsappend(_parse(source, state, verbose, nested + 1,
    File "/usr/lib/python3.9/sre_parse.py", line 668, in _parse
    raise source.error("nothing to repeat",
    re.error: nothing to repeat at position 0

    就可以用\进行转义

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    import re

    pattern='*das'

    # 在正则里面*是匹配0次或多次,我们用\进行转义
    x =re.match('\*',pattern)

    print(x)

    结果:就可以匹配出来了

    1
    <re.Match object; span=(0, 1), match='*'>
  1. 特殊字符串匹配

    特殊字符就是\n\t了等

    1
    2
    3
    4
    5
    6
    7
    import re

    pattern='\\t'

    x =re.match('\\t',pattern)

    print(x)

    结果:下面的结果为什么是None这是应为在,正则里面\也是转义符

    1
    None

    我们需要添加双倍的\\进行转义

    代码

    1
    2
    3
    4
    5
    6
    7
    import re

    pattern='\\t'
    # 就是\转义\
    x =re.match('\\\\t',pattern)

    print(x)

    结果

    1
    <re.Match object; span=(0, 2), match='\\t'>
  2. 也可以在匹配到添加r预防转义

    代码

    1
    2
    3
    4
    5
    6
    7
    import re

    pattern='\\t'
    # 防止字符转义
    x =re.match(r'\\t',pattern)

    print(x)

    结果

    1
    <re.Match object; span=(0, 2), match='\\t'>

    ?非贪婪模式

在下面

search()函数

search()函数,在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果,如果搜索完了还没有找到,就返回 None

格式

1
re.search(pattern, string, flags=0)
参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

match()测试

1
2
3
4
5
6
7
8
9
import re


content = 'Extra stings Hello 1234567'

# 匹配content变量里面的Hello
result = re.match('Hello',content)
# 输出结果
print(result)

结果:应为match()函数只能从开头匹配第一个不是H而是E,所以直接就返回了None

1
None

我们就可以用search()函数来匹配了

1
2
3
4
5
6
7
8
9
import re


content = 'Extra stings Hello 1234567'

# 匹配content变量里面的Hello
result = re.search('Hello',content)
# 输出结果
print(result)

结果:可以看见成功,应为在匹配时会扫描整个字符串

1
<re.Match object; span=(13, 18), match='Hello'>

findall()和finditer()函数

findall()函数

findall()他会获得表达式里面的全部内容,返回形式为数组

他的语法

1
re.findall(pattern, string, flags=0)
字符 描述
pattern 匹配的正则表达式
string 要匹配到字符串
flags 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解如 re.IGNORECASE 等

代码

1
2
3
4
5
6
7
import re

pattern='蔡徐坤 13500000001 18岁'

x =re.findall('\d{11}',pattern)

print(x)

结果:他和这个https://regex101.com/r/Ljaf9U/1差不多的他会返回表达式所匹配的全部内容

1
['13500000001']

finditer()函数

findall()和finditer()函数是一样的,就是finditer()函数他返回的是迭代器,什么是迭代器可以看我这个笔记https://www.zssnp.top/2021/06/09/pythonddq/

1
2
3
4
5
6
7
import re

pattern='蔡徐坤 13500000001 18岁'

x =re.finditer('\d{11}',pattern)

print(x)

结果

1
<callable_iterator object at 0x7faec31272b0>

image-20210816122520768

我们可以通过for进行迭代输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re

pattern='蔡徐坤 13500000001 18岁'

x =re.finditer('\d',pattern)

for i in x:

# 他返回的是一个对象
# | group() | 返回匹配的字符串 |
# | span() | 返回匹配的范围 |
# | start() | 返回从那开始匹配的 |
# | groups() | 返回全部分组 |
print(i)

# group()返回匹配的字符串
print(i.group())

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<re.Match object; span=(4, 5), match='1'>
1
<re.Match object; span=(5, 6), match='3'>
3
<re.Match object; span=(6, 7), match='5'>
5
<re.Match object; span=(7, 8), match='0'>
0
<re.Match object; span=(8, 9), match='0'>
0
<re.Match object; span=(9, 10), match='0'>
0
<re.Match object; span=(10, 11), match='0'>
0
<re.Match object; span=(11, 12), match='0'>
0
<re.Match object; span=(12, 13), match='0'>
0
<re.Match object; span=(13, 14), match='0'>
0
<re.Match object; span=(14, 15), match='1'>
1
<re.Match object; span=(16, 17), match='1'>
1
<re.Match object; span=(17, 18), match='8'>
8

image-20210816144034906

sub()和subn()函数

sub()这个函数是替换用的,可以叫文本的指定的字符替换掉

他俩的区别就是

  • sub():他返回替换后的结果
  • subn():他返回的是一个元组

sub()

代码

1
2
3
4
5
6
7
8
import re

content = '54aK54yr5oiR54ix5L2g'

# 叫数字的字符都替换成没有
content = re.sub('\d+', '', content)
# 输出结果
print(content)

结果

1
aKyroiRixLg

subn()

1
2
3
4
5
6
7
8
import re

content = '54aK54yr5oiR54ix5L2g'

# 叫数字的字符都替换成没有
content = re.subn('\d+', '', content)
# 输出结果
print(content)

结果

1
('aKyroiRixLg', 6)

compile()函数

compile()函数将指定的源作为代码对象返回,并准备执行,他里面也是包含sub(),findall(),search(),match()等方法

下面代码演示一下

1
2
3
4
5
6
7
8
import  re

s="ds21212adsa212dsa"

rea=re.compile('\d') # 只匹配数字

# 用compile()的findall方法来匹配s变量里面的内容
print(rea.findall(s))

结果

image-20210524225156472

视频参考

贪婪模式和非贪婪模式

正则表达式默认就是贪婪模式

  • 贪婪模式就是尽可能的匹配多的

  • 非贪婪模式尽可能匹配少的

看一下什么是贪婪模式,他匹配的是|<|h1>>>>>>|>|最外面的

image-20210816170828451

看一下什么是非贪婪模式,他匹配的是|<|h1|>|>>>>>>最少的

image-20210816171216664