本文是基于@弈心大佬(王印)的书籍《网络工程师的python之路》所整理的笔记
1.使用python
我使用的python版本为3.10
交互模式
在Windows下,有两种方法进入Python解释器来使用交互模式:一种是通过命令行输入命令py或者python进入解释器;另一种是打开Python软件包自带的集成开发环境(IDE),也就是IDLE。
脚本模式
在Windows里,有两种方法创建Python脚本,一种是将代码写进Windows记事本里,另一种是借助第三方编辑器
2.变量
所谓变量(Variable),顾名思义,指在程序运行过程中,值会发生变化的量。与变量相对应的是常量,也就是在程序运行过程中值不会发生变化的量,不同于C/C++等语言,Python并没有严格定义常量这个概念,在Python中约定俗成的方法是使用全大写字母的命名方式来指定常量,如圆周率PI=3.1415926。变量是存储在内存中的一个值,在创建一个变量后,也就意味着在内存中预留了一部分空间给它。 在Python中,我们使用等号=来连接变量名和值,进而完成变量赋值的操作。这里将10这个整数(也就是内存中的对象)赋值给变量a,因为10本身是“整数”(Integer),所以变量a此时就代表了“整数”这个数据类型的值。我们可以使用type()函数来确认a的数据类型,发现变量a的数据类型此时为int,也就是integer的缩写,代码如下。
>>> a=10
>>> type(a)
<class'int'>
Python是一门动态类型语言,和C、Java等不同,我们无须手动指明变量的数据类型,根据赋值的不同,Python可以随意更改一个变量的数据类型。也就是是说我们刚刚a赋予的值为整数10,数据类型也就为整数,现在我们仍可以再次给a赋值一个字符串类型的值,那么a的数据类型也就为字符串了
变量名可以用大小写英文字母、下画线、数字来表示,但是不能包含标点符号、空格及各类其他特殊符号,如括号、货币符号等。变量名可以以字母和下画线开头,但是不能以数字开头。变量名区分大小写。
3.注释
在Python中,我们使用#来做注释符号。和#写在同一排,并且写在#后面的代码将只做注释使用,不会被当作代码的一部分,也就不会被执行。因此有时我们还可以巧用#来“遮盖”我们不想执行的代码。比如可以选择性地在脚本的某些print()函数前加上#,不看其输出内容,想看其输出内容时再把#删除,而不至于每次都要在脚本里反复删除、重写该段print()函数。
4.方法和函数
在Python中,方法(Method)和函数(Function)大体来说是可以互换的两个词,它们之间有一个细微的区别:函数是独立的功能,无须与对象关联;方法则与对象有关,不需要传递数据或参数就可以使用。举个例子,前面讲到的type()就是一个函数。方法则需要与一个对象(变量或数据)关联,比如upper()是一个方法,它的作用是将字符串里小写的英文字母转换为大写的英文字母,代码如下。
>>> vendor='cisco'
>>> vendor.upper()
'CISCO'
5.数据类型
对网络工程师来说,常用的数据类型有字符串(String)、整数(Integer)、列表(List)、字典(Dictionary)、浮点数(Float)、布尔(Boolean)。另外,不是很常用但需要了解的数据类型包括集合(Set)、元组(Tuple)及空值(None)。
5.1字符串
字符串即文本,可以用单引号''、双引号""和三引号''' '''表示,下面分别介绍三者的区别和用法。
1. 单引号和双引号
当表示内容较短的字符串时,单引号和双引号比较常用且两者用法相同,需要注意的是单引号和双引号不可以混用。除了使用print()函数,我们还可以在解释器里直接输入变量名来获取它的值,这是编辑器交互模式下特有的,脚本模式做不到。
>>> a='wwe'
>>> a
'wwe'
需要指出的是,如果变量中存在换行符\n,则print会执行换行动作,但如果在解释器里直接输入变量名,则会报错。
在Python中,我们还可以通过加号+来拼接(Concatenate)字符串,举例如下。
>>> a='你好'
>>> b='世界'
>>> a+b
'你好世界'
>>> print(a+b)
你好世界
>>> print('你好'+'世界')
你好世界
注意,在使用加号+将变量拼接合并时,如果其中一个变量为字符串,那么其他所有要与之拼接的变量也都必须为字符串,不一致要转换成一致的,否则Python会报错。例如:
2. 三引号
三引号形式的字符串通常用来表示内容较长的文本文字,它最大的好处是如果遇到需要换行的文本,文本内容里将不再需要换行符\n。比如路由器和交换机中用来警告非授权用户非法访问设备后果的MOTD(Message of The Day)之类的旗标(Banner)配置,此类文本内容通常比较长且需要换行,这时用三引号来表示该文本内容是最好的选择。
3.与字符串相关的函数
◎ upper()
前面提到过upper()方法,它的作用是将字符串里小写的英文字母转换为大写的英文字母。upper()的返回值是字符串。
◎ lower()
顾名思义,与upper()相反,lower()方法的作用是将字符串里大写的英文字母转换为小写的英文字母。lower()的返回值是字符串。
◎ strip()
strip()用来在字符串的开头和结尾移除指定的字符(如字母、数字、空格、换行符\n、标点符号等)。如果没有指定任何参数,则默认移除字符串开头和结尾处的所有空格和换行符\n。有时在字符串的开头和结尾会夹杂一些空格,如“ 192.168.100.1 ”,要去掉这些多余的空格,可以使用strip()。strip()的返回值是字符串。有时在字符串末尾会有换行符\n(比如使用open()函数的readlines()方法来读取文本文件里的内容后所返回的列表里的元素,后面会讲到),我们也可以使用strip()来移除这些换行符,举例如下。
>>> ip=' 192.168.1.1 '
>>> ip.strip()'192.168.1.1'
>>> ip='....qqq...'
>>> ip.strip('.')
'qqq'
◎ count()
count()用来判断一个字符串里给定的字母或数字具体有多少个,比如要找出“39419591034989320”这个字符串里有多少个数字9,就可以用count(),count()的返回值是整数。
◎ len()
len()用来判断字符串的长度,比如要回答上面提到的“39419591034989320”总共是多少位数,就可以用len(),len()的返回值是整数。
◎ split()和join()
之所以把这两个方法放在一起讲,是因为它俩关系比较接近,在字符串、列表的转换中互成对应的关系,split()将字符串转换成列表,join()将列表转换成字符串。到目前为止,我们还没有讲到列表(List),这里简单讲解一下:在Python中,列表是一种有序的集合,用中括号[]表示,该集合里的数据又被叫作元素,比如[1,3,5,7,9]就是一个最简单的列表,其中的整数1、3、5、7、9都属于该列表的元素。下面我们把该列表赋值给变量list1,用type()来确认该变量的数据类型,可以发现它的数据类型为list。我们可以使用索引来访问和指定列表中的每个元素,索引的顺序是从数字0开始的。列表索引的用法举例如下。
>>> list1=[1,2,3,4]
>>> list1[0]
1
>>> list1[1]
2
>>> list1[2]
3
>>> list1[3]
4
input()函数
讲完列表后,为了配合下面的案例,需要讲一下input()函数。input()的返回值是字符串。它的作用是提示用户输入数据与Python程序互动,比如你想写一段代码询问用户的年龄,让用户自己输入年龄,可以写一段这样的脚本代码。
>>> age=input('how old are you?')
how oldare you?13
>>> print(age)
13
注意这里的13是用户自己输入的,虽然它看着像整数,但是它实际的数据类型是字符串。
应用
看网络工程师如何在网络运维中使用split()和join()。举例来说,在大中型公司里,IP地址的划分一般是有规律可循的,比如说某公司有一栋10层楼的建筑,一楼的IP子网为192.168.1.0/24,二楼的为192.168.2.0/24,三楼的为192.168.3.0/24,依此类推。现在你需要做个脚本,让用户输入任意一个属于公司内网的IP地址,然后让Python告诉用户这个IP地址属于哪一层楼。代码如下:
ip=input('请输入要查询的IP地址:')
ip_list=ip.split('.')
print('.'.join(ip_list)+'属于'+ip_list[2]+'楼')
分析:如果输入的ip地址为:192.168.2.1 那么通过split('.')函数将字符串‘192.168.2.1’转换成列表(注意split()括号里的“.”表示分隔符,该分隔符用来对字符串进行切片)[192,168,2,1]。再通过ip_list[2]将列表第三个元素即2拿出来表示楼层数,即可通过ip地址查询楼层。 如果我们要把之前的列表['192','168','2','1']转换回字符串“192.168.1.1”,可以采用如下做法。
>>>'.'.join(['192','168','2','1'])
'192.168.2.1'
◎ startswith(),endswith(),isdigit(),isalpha()
之所以把上述4个字符串的函数和方法放在一起讲,是因为它们的返回值都是布尔值(Boolean)。布尔值只有两种:True和False,且首字母必须大写,true和false都不是有效的布尔值。布尔值通常用来判断条件是否成立,如果成立,则返回True;如果不成立,则返回False。首先来看startswith(),用来判断字符串的内容是否以给定的字符串开头,举例如下。
>>>ip='192.168.1.1' >>>ip.startswith(19) True >>>ip.startswith(192.) True
endswith()与startswith()恰好相反,用来判断字符串的内容是否以给定的字符串结尾。
字符串的内容包罗万象,可以为空,可以为中文汉字或英文字母,可以为整数或小数,可以为任何标点符号,也可以为上述任意形式的组合。而isdigit()就是用来判断字符串的内容是否为整数的,举例如下。
>>> year='2019'
>>> year.isdigit()
True
isalpha()用来判断字符串的内容是否为英文字母,举例如下。注意,isalpha()很严格,只要字符串的内容出现了哪怕一个非英文字母,isalpha()就会返回False。
5.2整数和浮点数
所谓整数,即通常理解的不带小数点的正数或负数,浮点数则是带小数点的正数或负数。如1为整数,1.0则为浮点数。我们可以把Python当成一个计算器,使用+、-、*、//、**等算术运算符做加、减、乘、除、求幂等常见的数学运算,举例如下。
>>> 256+256
512
在Python中,可以通过运算符做幂运算,比如8的2次方可以表示为8**2,3的3次方表示为3**3。在做除法运算时,可以看到示例中分别使用了/、//和% 3个运算符,它们的区别如下。/ 表示正常的除法运算,注意在Python 2中,如果碰到整数除整数,结果出现小数部分的时候,比如12 /10,Python 2只会返回整数部分,即1,要想得到小数点后面的部分,必须将除数或被除数通过float()函数换成浮点数来运算。而在Python 3中,12/10则直接返回浮点数1.2。// 表示向下取整,求商数。% 则表示求余数。整数也不单单用来做数学运算,通过加号+或乘号*两种运算符,还可以与字符串互动,适合用来画分割线,举例如下。
>>> print('hcie '*8)
hcie hcie hcie hcie hcie hcie hcie hcie
5.3列表
列表(List)是一种有序的集合,用中括号[]表示,列表中的数据被叫作元素(Element),每个元素之间都用逗号隔开。列表中的元素的数据类型可以不固///定,一个列表可以由字符、整数、浮点数、空值、布尔值以及列表组成。
>>> list1=[2020,1.23,'huawei',True,None,[1,2,3]]
注:一个列表本身也可以以元素的形式存在于另一个列表中,举例来说,上面的列表list1的第6个元素为列表[1,2,3],我们可以通过使用两次索引的方法来单独调取列表[1,2,3]中的元素,也就是整数1、2、3。
>>>list1=[2020,1.23,'huawei',True,None,[1,2,3]]
>>>list1[5][0]
1
下面介绍与列表相关的方法和函数◎ range()
range()函数在Python 2和Python 3中有较大区别。在Python 2中,range()函数用来创建一个整数列表,返回值为列表。
• Python 2中的range()创建的整数列表从0开始,因此range(10)返回的是一个包含整数0~9的列表,并不包含10。
• 也可以在range()中指定起始数和结尾数,返回的整数列表的最后一个元素为指定的结尾数减1,如range(1, 15)将返回一个包含整数1~14的列表,14由结尾数15减1得来。
• range()还可以通过指定步长来得到我们想要的整数,比如你只想选取1~19中所有的单数,那么就可以使用range (1, 20, 2)来实现(这里的2即步长)。
这种返回列表的range()函数有一个缺点是会占用内存,列表所含元素数量不多时对主机的性能影响不大,但是当使用range(10000000000000)来建立诸如这样巨大的列表时所占用的内存就非常“恐怖”了。因此在Python 3中,range()函数的返回值被改成range,这是一种可以被迭代的对象,这样改的目的就是节省内存。
如果要使Python 3的range()也返回列表,则需要对其使用list()函数,举例如下。
>>> a=range(10)
>>> print(a)
range(0, 10)
>>> a=list(range(10))
>>> print(a)
[0,1,2,3,4,5,6,7,8,9]
◎ append()
append()用来向列表末尾添加元素,举例如下。
>>>interface=[] >>>interface.append('gi0/0/1') >>>print(interface) ['gi0/0/1'] >>>interface.append('gi0/0/2') >>>print(interface) ['gi0/0/1','gi0/0/2']
首先我们建立一个空列表(以[]表示),并把它赋值给interfaces变量,然后使用append()方法将端口Gi1/1加入该列表,随后调用append()将Gi1/2加入该列表,现在列表interfaces中就有Gi1/1和Gi1/2两个元素了。
◎ len()
列表的len()方法和字符串的len()方法大同小异,前者用来统计列表中有多少个元素,后者用来统计字符串内容的长度,其返回值也依然为整数。
◎ count()
与字符串一样,列表也有count()方法,列表的count()方法用来找出指定的元素在列表中有多少个,返回值为整数。
◎ insert()
列表是有序的集合,前面讲到的append()方法是将新的元素添加到列表的最后面,如果我们想自己控制新元素在列表中的位置,则要用insert()方法。举例如下。
ospf_configuration=['ospf 100\n','network 0.0.0.0 255.255.255.255\n'] ospf_configuration.insert(1,'area 0\n') print(ospf_configuration) ['ospf 100\n', 'area 0\n', 'network 0.0.0.0 255.255.255.255\n']
首先我们创建一个名为ospf_configuration的变量,将配置OSPF的命令写在一个列表中赋值给该变量。随后发现列表漏了area 0命令,该命令要写在列表的第二个元素,这时我们可以用insert(1, 'area 0\n')将该命令加在列表第二个元素(记住列表的索引号是从0开始的)。如果这时我们还想给该OSPF路由器配置一个router-id,如把router-id这条命令写在router ospf 100的后面,可以再次使用insert()。
◎ pop()
pop()用来移除列表中的元素,如果不指定索引号,则pop()默认将去掉排在列表末尾的元素;如果指定了索引号,则可以精确移除想要移除的元素。
◎ index()
看了pop()的用法后,你也许会问:在拥有很多元素的列表中,怎么知道想要移除的元素的索引号是多少呢?这时就需要用index(),如想从cisco_switch_models列表中移除元素4500,可以按照如下操作。
>>> cisco_switch_models = [ '2960','3560','3750','3850','4500','6500','7600 ' ,'9300 ']
>>> cisco_switch_models.index ( '4500')
4
>>> cisco_switch_models.pop ( 4)'4500'
>>> print (cisco_switch_models)[ '2960','3560','3750','3850 ','6500','7600','9300' ]
先通过index()找出4500的索引号为4,然后配合pop(4)将它从列表中移除。
5.4字典
在Python里,字典(Dictionary)是若干组无序的键值对(Key-Value pair)的集合,用大括号{}表 示,每一组键值对都用逗号隔开,举例如下。
>>> dict ={'Vendor' :'Cisco', 'Model ': 'WS-C3750E-48PD-S', 'Ports ':48,'IOS': '12.2(55)SE12', 'CPU':36.3}
这里我们创建了一个变量名为dict的字典,该字典有5组键值对, 分别如下。
'Vendor' : 'Cisco '
'Model' : 'wS-C3750E-48PD-S'
'Ports' : 48
'IOS': '12.2 (55)SE12'
'CPU':36.3'CPU':36.3
• 键值对里的键(Key)和值(Value)用冒号:隔开,冒号的左边为键,右边为值。
• 键的数据类型可为字符串、常数、浮点数或者元组,对网络工程师来说,最常用的肯定是字符串,如“Vendor”“Model”等。
• 值可为任意的数据类型,比如这里的“Cisco”为字符串,48为整数,36.3为浮点数。
与列表不同,字典是无序的。
这里我们创建一个内容为[1,2,3,'a','b','c']的列表a,将它打印出来后,列表中元素的位置没有发生任何变化,因为列表是有序的。但是如果我们将刚才的字典dict打印出来,你会发现字典里键值对的顺序已经彻底被打乱了,没有规律可循,正因为字典是无序的,我们自然也不能像列表那样使用索引来查找字典中某个键对应的值。在字典里,查找某个值的格式为字典名[键名],举例如下。
>>> a = [ 1,2,3, 'a', 'b ', 'c']
>>> print (a)
[ 1,2,3, 'a', 'b ','c ' ]
>>>
>>> dict = { 'Vendor' : 'Cisco','Model ' : 'WS-C3750E-48PD-S','Ports' :48,'IOS' : '12.2(55)SE12','CPU' : 36.3 }
>>> print (dict)
{ 'IOS': '12.2(55)SE12','CPU':36.3,'Model ' : 'WS-C3750E-48PD-S','Vendor':'Cisco ','Ports ': 48 }
>>> print(dict['Vendor'])
Cisco
如果要在字典里新添加一组键值对,则格式为字典名[新键名] = '新值',举例如下。
>>> dict [ 'Number of devices ' ]=100
>>> print (dict)
{'Vendor ' : 'Cisco ', 'Number of devices ': 100,'IOS': '12.2(55)SE12 ','CPU':36.3,'Model' :'wS-C3750E-48PD-S ','Ports ': 48 }
>>>
如果要更改字典里某个已有键对应的值,则格式为字典名[键名] = '新值',举例如下。
>>> dict [ 'Model' ] = 'wS-C2960X-24PS-L '
>>>dict [ 'Ports '] ='24'
>>> print (dict)
{ 'IOS': '12.2(55)SE12', 'Model ': 'WS-C2960X-24PS-L','Vendor' : 'Cisco ',' Ports' : '24','CPU' :36.3 }
>>>
如果要删除字典里的某组键值对,则格式为del 字典名[键名],举例如下。
>>>deldict [ ' Number of devices' ]
>>> print (dict)
{ 'Vendor' : 'Cisco ','IOS' : '12.2 (55)SE12','CPU' :36.3,'Model ' :'ws-C3750E-4 8PD-S','Ports' : 48}
>>>
下面介绍与字典相关的函数和方法。
◎ len()
len()用来统计字典里有多少组键值对。len()的返回值是整数。
◎ keys()
keys()用来返回一个字典里所有的键,keys()的返回值为列表。
>>> print (dict)
{ 'Vendor ' : 'Cisco ','IOS ': '12.2(55)SE12','CPU': 36.3,'Model':' wS-C3750E-48PD-S','Ports' : 48 }
>>> print (dict.keys ( ) )
[ 'vendor', 'IOS','CPU', 'Model ','Ports ']
>>>
◎ values()
values()用来返回一个字典里所有的值,values()在Python 中的返回值为列表。
>>> dict = { 'Vendor' : 'Cisco','Model ' : 'WS-C3750E-48PD-S','Ports' :48,'IOS' : '12.2(55)SE12','CPU' : 36.3 }
>>> print (dict.values ( ) )
dict_values(['Cisco', 'WS-C3750E-48PD-S', 48, '12.2(55)SE12', 36.3])
>>>
◎ pop()
前面讲到,要删除字典中某组键值对可以用命令del 字典名[键名]。另外,我们可以使用pop()来达到同样的目的。与列表的pop()不同,字典的pop()不能导入索引号,需要导入的是键名,而且字典的pop()的返回值不是列表,而是键名对应的值(比如下面的48),举例如下。
>>> dict = { 'Vendor' : 'Cisco','Model ' : 'WS-C3750E-48PD-S','Ports' :48,'IOS' : '12.2(55)SE12','CPU' : 36.3 }
>>> print (dict)
{ 'Vendor' : 'Cisco','IOS': '12.2(55)SE12','CPU': 36.3,'Model ' :'wS-C375OE-48PD-S', 'Ports': 48 }
>>> dict.pop ( 'Ports ' )
48
>>> print (dict)
{ 'Vendor ' : 'Cisco','IOS': '12.2(55)SE12','CPU':36.3,'Model' :'wS-c375OE-48PD-S'}
>>>
◎ get()
前面讲到,我们可以使用values()方法得到一个字典里所有的值。除此之外,还可以使用get()来返回字典里具体键名对应的值,get()的返回值是所导入的键名对应的值,举例如下。
>>>print (dict)
{'Vendor': 'Cisco', 'IOS': '12.2(55)SE12',CPU':36.3, 'Model':'WS-C3750E-48PD-S'}
>>>dict.get ('Vendor')
'Cisco'
>>>dict.get ( 'CPU')
36.3
>>>
5.5布尔类型
布尔类型(Boolean)用来判断条件是否成立,布尔值只有两种:True和False,如果条件成立,则返回True;如果条件不成立,则返回False。两种布尔值的首字母(T和F)必须大写,true和false都不是有效的布尔值。布尔类型在判断语句中常用,Python的判断语句将在进阶语法中详细讲解。
1. 比较运算符既然布尔类型用来判断条件是否成立,那就不得不提一下Python中的比较运算符(Comparison Operators)。比较运算符包括等于号==、不等于号!=、大于号>、小于号<、大于等于号>=、小于等于号<=。比较运算符和+、-、*、//、**这些算术运算符
最大的区别是:前者用来判断符号左右两边的变量和数据是否满足运算符本身的条件,并且返回值是布尔值,后者则单纯做加减乘除等运算,返回值是整数或浮点数。在交互模式下,使用比较运算符后可以马上看到返回的布尔值True或者False。如果是脚本模式,则需要配合print命令才能看到。
2.逻辑运算符除了比较运算符,使用逻辑运算符(Logical Operators)也能返回布尔值。逻辑运算符有3种:与(and)、或(or)、非(not)。
>>> A=True
>>> B=True
>>> A and B
True
>>> A or B
True
>>> not A
False
>>>
5.6集合、元组、空值
作为同样需要网络工程师掌握的Python数据类型,集合(Set)、元组(Tuple)、空值(None)相对来说使用频率不如字符串、整数、浮点数、列表、字典及布尔类型那么高,这里进行简单介绍。
1. 集合
• 集合是一种特殊的列表,里面没有重复的元素,因为每个元素在集合中都只有一个,所以集合没有count()方法。 集合可以通过大括号{}(与字典一样,但是集合没有键值对)或者set()函数创建。
>>> interfaces = { 'Fa0/0’, 'Fa0 /1', 'Fa0 / 2'}
>>> type (interfaces)
<class 'set'>
>>>vendors = set ( [ 'cisco ','Juniper ', 'Arista ','Cisco ' ] )
>>>type ( vendors)
<class 'set'>
>>> print (vendors)
{ 'Cisco ', 'Arista ', 'Juniper' }
• 集合是无序的,不能像列表那样使用索引号,也不具备index()函数。
下面介绍与集合有关的方法和函数。
◎ add()
add()用来向一组集合添加新元素,其返回值依然是集合,插入的位置是随机的,举例如下。
>>>vendors.add ( ' Huawei')
>>>vendors
{ ' Huawei','Cisco','Arista ','Juniper'}
◎ remove()
remove()用来删除一组集合中已有的元素,其返回值依然是集合。
2. 元组
• 与集合一样,元组也是一种特殊的列表。它与列表最大的区别是:集合可以任意对列表中的元素进行增添、删除、修改,而元组则不可以。一旦创建元组,将无法对其做任何形式的更改,所以元组没有append()、insert()、pop()、add()和remove(),只保留了index()和count()两种方法。
• 元组可以通过小括号()创建,也可以使用tuple()函数创建。
• 与列表一样,元组是有序的,可以对元素进行索引。
下面介绍与元组有关的方法和函数。
◎ index()
元组的index()与列表用法相同,都用来查询指定元素的索引号。index()的返回值为整数,举例如下。
>>> vendors=('cisco','huawei','H3C')
>>> print(vendors.index('H3C'))
2
◎ count()元组的count()与列表用法相同,都用来查询指定元素在元组中的数量。count()的返回值为整数,举例如下。
3. 空值
空值是比较特殊的数据类型,它没有自带的函数和方法,也无法做任何算术和逻辑运算,但是可以被赋值给一个变量,举例如下。
>>> type(None)
<type'NoneType'>
>>> None == 100
False
>>>a= None
>>> print(a)
None
空值(None)较常用在判断语句和正则表达式中。对于网络工程师来说,日常工作中需要经常使用显示命令(show或者display)来对网络设备进行排错或者查询网络信息,通常这类显示命令都会给出很多回显内容,而大多时候我们只需要关注其中的一两项参数即可。如果用Python来实现网络运维自动化,则需要使用正则表达式来告诉Python应该抓取哪一个“关键词”(即我们想要的参数);而空值则可以用来判断“关键词”抓取得是否成功。