01基础阶段
大纲
请求与相应
http协议相关
数据解析
代理、验证码、cookie等
相关的反爬技能
数据库
Playwright
三方抓包 工具
重要数据类型
列表数据类型(数据结构)
-
在实际开发中,经常需要将一组(不止一个)数据存储起来,以便后边的代码使用。列表就是这样的一个数据结构。且列表是Python中最基本也是最常用的数据结构 之一
-
什么是数据结构?
- 通俗来讲,可以将数据结构当作是某种容器,该容器是用来装载或者存储数据的。不同的数据结构决定了对数据不同的组织方式。
- 那么当数据被装载或者存储到了某个数据结构中后,那么就可以基于该数据结构的特性对数据进行不同形式的处理和运算
-
列表的创建方式
- 创建一个列表,只要把逗号分隔的不同的数据元素使用方括号括起来即可。列表内的元素,可以是其他任意类型的数据,可以多层嵌套列表,元素个数无限制。
alist = [1,2,3,4,5] items = [1,'bobo',12.34] #列表中可以存储任意类型的数据
-
列表元素
- 存储在列表数据结构中的每一个数据被称为列表元素,简称元素
-
列表索引(类似于c语言的数组)
- 列表中的每个元素都被分配一个数字作为索引,用来表示该元素在列表内所排在的位置。第一个元素的索引是0,第二个索引是1,以此类推。
-
访问列表内的元素
- 列表从0开始为它的每一个元素顺序创建下标索引,直到总长度减一。要访问它的某个元素,以方括号加下标值的方式即可。注意要确保索引不越界,一旦访问的索引超过范文,会抛出异常。所以,一定要记得最后一个元素的索引是len(list)-1。
alist = [1,12.3,'bobo'] print(alist[2]) #'bobo' print(alist[0:2]) #[1,12] print(alist[6]) #使用索引和切片的时候,不可以访问超出索引范围的元素
-
修改元素的值
- 直接对元素进行重新赋值
alist = [1,12.3,'bobo'] alist[1] = 100.123 print(alist)
-
删除元素
- 使用del语句或者remove(),pop()方法删除指定的元素。
alist = [1,12.3,'bobo']
-
切片
-
切片指的是对序列进行截取,选取序列中的某一段
-
切片的语法是: list[start:end]
#同字符串的切片机制一样 alist = [1,12.3,'bobo','jay','hello'] print(alist[:-1])
-
以冒号分割索引,start代表起点索引,end代表结束点索引。省略start表示以0开始,省略start表示以0开始,省略end表示到列表的结尾。注意,区间是左闭右开!也就是说[1:4]会截取列表的索引为1/2/3的3个元素,不会截取索引为4的元素。分片不会修改原有的列表,可以将结果保存到新的变量,因此切片也是一种安全操作,常被用来复制一个列表,例如newlist = lis[:]。
-
切片过程中还可以设置步长,以第二个冒号分隔,例如list[3:9:2],表示每隔多少距离取一个元素。
-
-
列表的内置方法
-
上文中我们说过,数据存储到不同的数据结构中,可以基于该数据结构的特性对数据进行指定形式的操作和处理。下图中的方法是列表专有的内置方法,请熟记于心。
-
作用 方法 append() 在列表末尾添加新的对象 count() 统计某个元素在列表中出现的次数 extend() 在列表末尾一次性追加另一个序列中的多个值 index() 从列表中找出某个值第一个匹配项的索引位置 insert() 将对象插入列表 pop() 移除列表中的一个元素(默认最后一个),并且返回该元素的值 remove() 移除列表中某个值的第一个匹配项 reverse() 反向列表中的元素 sort() 对原列表进行排序 copy() 复制列表 clear() 清空列表,等于del list[:]
alist = ['bobo',"18","99.5",'北京'] #将列表转换成字符串 ret = '-'.join(alist) #将列表中的每个列表元素根据-为间隔进行拼接,返回字符串结果 print(ret) #如何将字符串转化为列表 s = 'hello-name-bobo-age' ret = s.split('-') print(ret) alist = [3,8,5,7,6,2,1] alist.sort() #对列表元素进行排序 print(alist) a = [1,2,3] a.append('bobo') #向列表尾部添加一个元素 print(a) a1 = [1,2,3] a1.insert(1,999) #向列表下标为1的位置添加一个元素 print(a1)
-
字典数据类型
-
字典的实现机制:
- Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查询和插入速度。
-
字典特性:
- 字典包含的元素个数不限,值的类型可以是任何数据类型,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查询和插入速度
-
字典特性:
- 字典包含的元素个数不限,值的类型可以是任何数据类型!但是字典的key必须是不可变的对象,例如整数、字符串、bytes和元组,最常见的还是将字符串作为key。列表、字典、集合等就不可以作为key。同时,同一个字典内的key必须是唯一的(基于哈希),但值则不必。
- 注意:从Python3.6开始,字典是有序的!它将保持元素插入时的先后顺序,务必注意!
-
创建字典
-
字典的每个键值对用冒号(:)分割,每个键值对之间用逗号(,)分隔,整个字典包括在花括号({})中,例如:
-
d = {key1:value1,key2:value2}
#键值对: key:value #key:只能使用不可变类型的数据充当,通常使用字符串 #value: 任意数据类型的值充当 #字典中无法存储重复的键值对 dict_1 = {'name':'bobo','age':'18','score':'100','age':'18'} #注意:不要在字段中存储相同的key,value可以相同 dict_2 = {'name':'bobo','age':18,'age':20} print(dict_2)
-
-
访问字典
- 虽然现在的字典在访问时有序了,但字典依然是集合类型,部署序列类型,因此没有索引下标的概念,更没有切片的说法。但与list类似的地方是,字典采用把相应的键放入方括号内获取对应值的方式取值。
d = {'name':'bobo','age':18,'scrores':[100,220,99]} #根据key访问对应的value值 print(d['name'],d['scores']) #依次访问name和scores对应的值 print(d.get('name')) #通过get使用对应的key访问对应的value值 #注意:使用[]访问不存在的key对应的value值程序会报错 # print(d['address']) #程序报错 # 注意:使用get访问不存在的key程序不会报错,但是会返回None这个空值 print(d.get('address'))
-
添加和修改
- 增加就是往字典插入新的键值对,修改就是给原有的键赋予新的值。由于一个key只能对应一个值,所以,多次对一个key赋值,后面说的值会把前面的值覆盖掉。
d = {'name':'bobo','age':20,"scores":[100,200,99]} d['name'] = 'jay' #给存在的key修改对应的value值 d['address'] = 'BeiJing' #给一个不存在的值赋值表示新增键值对 del d['age'] #删除age键值对 print(d)
-
删除字典元素、清空字典元素、清空字典和删除字典
- 使用del关键字删除字典元素或者字典本身,使用字典的clear()方法清空字典。
d = {'name':'bobo','age':'20',"scores":[100,120,99].'name':'bobo'} del d['name'] print(d) d = {'name':'bobo','age':20,"scores":[100,120,99],'name':'bobo'} del d print(d) d = {'name':'bobo','age':20,"scores":[100,120,99],'name':'bobo'} d.clear() print(d)
-
字典的重要方法
d = {'name':'bobo','age':20,"scores":[100,120,99],'name':'bobo'} print(d.key()) #返回字典中所有的key print(d.values()) #返回字典中所有的value print(d.items()) #返回字典中所有的键值对
练习
-
写一个程序,输出100以内的所有偶数,直到累加和大于1000.
#写一个程序,输出100以内的所有偶数,直到累加和大于1000. suam = 0 for i in range(101): if i % 2 == 0: if suam >= 1000: break suam += i #print(i) print(suam)
-
已知a+b+c =1000且a^2+b^2=c^2(a,b,c都是自然数),求出符合条件的a,b,c的所有组合。
#已知a+b+c =1000且a^2+b^2=c^2(a,b,c都是自然数),求出符合条件的a,b,c的所有组合。 for a in range(1001): for b in range(1001): c = 1000 - a - b if a + b + c == 1000 and a**2 + b**2 == c**2: print(a, b, c)
-
给定一个字符串test = "Hello World!",请统计字符串中每个字符的出现次数,并将结果存储在一个字典中。
text = "hello world" tongji = {} for i in text: if i not in tongji: tongji[i] = 1 else: tongji[i] += 1 print(tongji)
-
猜数字游戏设计: 可以不间断的进行猜数字游戏环境,找到猜对了,结束程序,猜不对,可有不断的进行游戏,并且需要提示用户猜大了还是猜小了。
-
最后需要统计出,用户猜了多少次猜对。
-
每一个用户的初始分数为100,每猜错一次扣5分,最后程序结束,统计用户的得分
import random guess_num = random.randint(1,10) count = 0 score = 100 while 1: count += 1 # 让用户输入数字 num = int(input('输入一个数字:')) # 判断num和guess_num是否一样 if num == guess_num: print('猜对了') break elif num < guess_num: print('猜小了') score -= 5 else: print('猜大了') score -= 5 print('一共猜的次数:',count) print('最后得分:',score)
import random guess_num = random.randint(1,10) count = 0 score = 100 while 1: count += 1 # 让用户输入数字 num = int(input('输入一个数字:')) # 判断num和guess_num是否一样 if num == guess_num: print('猜对了') break elif num < guess_num: print('猜小了') score -= 5 else: print('猜大了') score -= 5 print('一共猜的次数:',count) print('最后得分:',score)
-
函数
什么是函数?
所谓的函数其实就是Python语言中的一种工具,基于该工具可以完成不同的具体操作。想要将函数理解透彻,大家一定要善于映射生活,也就是说基于我们熟悉的生活场景来理解函数。因为,所有的编程语言都是人设计的,只要是人设计的,必然源于生活,当然,也有可能是基于别的编程语言习惯
函数分为内置函数和自定义函数
- 内置函数:
- 顾名思义,就是编程语言内置的内容,无需额外操作就可以直接调用的函数
- 自定义函数
- 当python开发者内置的函数无法满足编程需求时,我们就会根据自己的需要,重新开发适用的函数,这就是自定义函数
函数基础
先定义函数,后调用(简单)
返回值
当一个函数被调用结束后,该函数势必已经将一组操作执行结束了,如果在操作执行结束之后,想要将一个结果返回给调用者,则就可以使用return语句实现。例如:使用温度计量完体温之后,一定将结果告诉测体温的人(这段像废话)
-
返回一个具体的字符串
def func01(): name = 'job' return name ret = func01() print(ret)
-
返回一个表达式
def myAdd(): num1 = input("enter num01") num2 = input("enter num02") num1 = int(num1) num2 = int(num2) return num1 + num2
-
返回多个结果
def func02(): a = 1 b = ['linux','windows','1970'] c = '0.0.0.0' return a,b,c ret = func02() print(ret)
-
不写return默认返回None
def func03(): print('我是没有返回值的函数') ret = func03(): print(ret)
-
return后面的代码无意义(这是个错误示范,不要试图在return后面增加任何代码)
def func04(): print('函数开始执行') print('函数开始执行') return 'over' print('我想在return后面执行') #当然是不可能的 func04()
-
返回一个函数(在函数a内部定义一个函数b,并用a返回b,当调用a的时候,会执行a函数的所有代码,结束后,会执行b的代码,看不懂可以断点多试几次)
def func05(): print('这里是五号函数开始') def func06(): print('这里是六号函数开始') print('这里是五号函数结束') return func06 ret = func05() ret()
-
返回一个函数调用
def func07(): print('七号函数开始') def func08(): print('八号函数开始') return '08 over' print('七号函数结束') return func08() ret = func07() print(ret)
函数参数
绝大多数函数在定义的时候需要接收一定数量的参数,然后根据实际调用时提供的参数的不同,输出不同的结果。函数的参数可以极大程度上增加函数的"通用性"
#设计一个加法运算的函数
def addnum01():
num1 = 100
num2 = 50
return num1 + num2
ret = addnum01()
print(ret)
#增加函数的通用性
def addnum02(num1,num2):
return num1 + num2
ret = addnum02(num1,num2)
print(ret)
- 参数的两种说法
- 形参(形式参数)
- 定义函数时,在函数名称后面括号里面的参数,如果不调用该函数,那这个参数就只是个形式
- 实参(实际参数)
- 实际调用参数时,填在括号里面的参数(实际存在的数字,字符串等等)
- 函数的不同种类
- 定义函数时,参数的名字和位置确定下来,函数的接口就固定了,python函数的参数定义灵活度非常大,常用的参数类型有:
- 位置参数
- 默认参数
- 动态参数
位置参数
也叫必传参数或者顺序参数,是最重要的,也是必须在调用函数时,明确提供的参数!参数位置必须按照先后顺序,一一对应,个数不多不少的传递!
def add(a,b,c):
return a+b+c
ret = add(1,2,3)
x = y = z = 10
ret = add(x,y,z)
print(ret)
通常我们在调用函数时,位置参数都是按照顺序先后传入。但是,如果在位置参数传递时,给实参指定位置参数的参数名,那么位置参数也可以不按照顺序调用(不过还是不建议打乱顺序,影响阅读,增加工作量)
def student(name,sex,age):
return name,sex,age
student('xiaoming','male',20)
student(sex = 'male',name = 'xiaohong',age = 20)
默认参数
在函数定义时,如果给某个参数提供一个默认值,这个参数就变成了默认参数,不再是位置参数了。在调用函数时,如果不特别指定参数值,那么函数就会以默认参数执行。
def func15(name,age=18):
print(name,age)
func16("xiaoli",30)
注意:默认参数必须在位置参数后面,如果违反了这一点,在语法层面直接无法通过(下面是错误示范)
def func16(age = 18,name):
print(name,age)
动态参数
-
顾名思义,动态参数就是传入的参数个数是动态的,可以是一个、两个、任意个,也可以是零个,在不需要的时候,可以完全忽略动态函数,不需要传递任何值。
-
python的动态参数有两种,分别是:
- *args
- **kwargs
- 这里面的关键是一个和两个星号的区别,而不是args和kwargs在名字上的区别
-
注意:
- 动态参数,必须放在所有的位置参数和默认参数后面!
-
*args
- 一个星号表示接收任意个参数。调用时,会将实际参数打包成一个元组传入形式参数。如果参数是个列表,会将整个列表当作一个参数传入。例如:
def func17(*args): #动态参数 print(args) func17(1,2,'three',[6,7])
- 如果想每一个列表元素作为单独的参数进行传递,则需要使用一个星花符号即可
def func18(*args): print(args) func18(*[1,2,3,4,5])
-
**kwargs
- 两个星号表示接受键值对的动态参数,数量任意。调用的时候会将实际参数打包成字典。例如:
def func19(**kwargs): print(kwargs) func19(k1=1,k2=2,k3='bobo')
- 同上,如果我们需要传递一个字典并希望字典内的键值对一一作为参数被逐一传入函数,则只需要传入参数前加两个星号
def func20(**kwargs): print(kwargs) dic = { 'name':'zhangsan', 'age':20, 'time':5 } func20(**dic)
变量作用域(函数进阶)
-
讲到了函数就必须介绍变量的作用域
- 作用域指的是变量的有效范围。变量并不是在哪个位置都可以访问,访问权限取决于这个变量是在哪里赋值的,也就是哪个作用域内赋的值。变量在哪个作用域内赋值,则表示该变量的作用域就是该区域,变量只可以在其作用域指定区域被访问。(感觉这是一段废话)
-
局部变量
- 定义在函数内部的变量拥有一个局部作用域
-
全局变量
- 定义在函数外拥有全局作用域的变量
num = 520 #全局变量 def func21(): email = '10001@163.com' #局部变量 print(email) #函数内部可以访问局部变量 print(num) #print(email) #局部变量无法在函数外部被使用 print(num) func21()
global关键字
num = 0
def func22(a,b):
num = a + b
print('内部变量num:',num)
func22(13,14)
print('全局变量num:',num)
很明显,函数内部有一个num局部变量,外部有一个num全局变量,想要在函数内部修改全局变量,该如何操作,答案是使用global关键字。
-
global:
- 指定当前变量使用外部的全局变量
num = 0 def func23(a,b): global num num = a + b print('函数内部num:',num) func23(13,14) print('函数外部num:',num)
匿名函数-初步介绍
- 匿名函数,顾名思义,就是没有函数名称的函数,本质上匿名函数是一个表达式
- 匿名函数的使用可以省去为函数命名的麻烦,节省精力,很多编程语言都提供这一特性。匿名函数用好了,会有画龙点睛的效果,没用好,就容易四不像,初期不建议过多使用
- 匿名函数的使用
- python使用lambda关键字来创建匿名函数
- 所谓匿名,即不再使用def语句这样标准的形式定义一个函数
- lambda只是一个表达式,而不是一个代码块,函数体比def简单很多
- 仅仅能在lambda表达式中封装有限的逻辑
- 定义语法:
- ٌ其形式通常是这样的:lambda 参数 : 表达式
- 匿名函数只能有⼀个表达式,不⽤也不能写return语句,表达式的结果就是返回值。
def func24(x):
if x > 0:
return 1
else:
return 0
func24(0)
写成lambda表达式就是
ex = lambda x:1 if x > 0 else 0
print(ex(0))
通常情况下,匿名函数是充当函数的参数来使用的,例如:
def isPass(s):
if s >= 60:
return '及格'
else:
return '不及格'
scores = [60,58,50,88,90,45]
ret = map(isPass,scores) #可以让isPass函数依次处理
scores列表中的每⼀个列表元素
print(list(ret))
scores = [60,58,50,88,90,45]
ret = map(lambda x:'及格' if x >=60 else '不及
格',scores)
print(list(ret))
文件操作
文件打开
-
Python内置了一个open()方法,用于对文件进行读写操作。使用open()方法操作文件就像把大象塞进冰箱一样,可以分三步走,一是打开文件,二是操作文件,三是关闭文件
-
文件句柄/文件描述符
-
open()方法的返回值是一个file对象,可以将他赋值给一个变量,这个变量就是所谓的文件句柄。
-
file对象:
-
可以调用read()和write()方法,对打开的文件进行读写操作
-
-
open方法的语法
- f = open(filename,mode)
- filename:
- 一个包含了你要访问的文件名称的字符串值,通常是一个文件路径
- 文件路径作用:有很多种,默认是只读方式r
- mode:
- 打开文件的模式,有很多种,默认是只读方式r
-
打开文件的模式:
-
常规文件打开模式操作演示
- b模式:
- 二进制模式,通常用来读取图片、视频等二进制文件。注意,它在读写的时候是以bytes类型读写的,因此获得的是一个bytes对象而不是字符串。在这个读写过程中,需要自己指定编码格式。在使用带b的模式时,一定要注意传入的数据类型,确保为bytes类型。
- +模式:
- 对于w+模式,在读写之前都会清空文件的内容。
- 对于a+模式,永远只能在文件的末尾写入。
- 对于r+模式,也就是读写模式
-
编码问题
- 要读取非UTF-8编码的文件,需要给open()函数传入encoding函数,例如,读取GBK编码的文件
- 遇到有些编码不规范的文件,可能会抛出UnicodeDecodeError异常,这这表示在文件中可能夹杂了一些非法编码的字符。遇到这种情况,可以提供error='ignore'参数,表示如果遇到编码错误后如何处置。
文件对象操作
-
每当我们用open方法打开一个文件时,将返回一个文件对象,这个对象内置了很多操作方法。
-
f.read(size) #size读取数据的个数
- 读取 一定大小的数据,然后 作为字符串或字节对象返回。size是一个可选的数字类型的参数,用于指定读取的数据量。当size被忽略了或者为负值,那么该文件的所有内容都将被读取并且返回。
- 注意:
- 如果文件体积较大,请不要使用read()方法一次性读入内存,而是read(512)这种一点一点的读。
-
f.readline()
- 从文件中读取一行n内容。换行符为"\n"。如果返回一个空字符串,说明已经读取到最后一行。这种方法,通常是读一行,处理一行,并且不能回头,只能前进,读过的行不能再读了。
-
f.readlines()
- 将文件中所有的行,一行一样全部读入一个列表内,按顺序一个一个作为列表的元素,并返回这个列表。readlines方法会一次性将文件全部读入内存,所以也存在一定的风险。但是它有一个好处,每行都保存在列表里,可以随意存取。
#需求:读取文件中的数据 fp = open('./test.txt','r') text = fp.read(10) #读取指定字节的数据 text_line = fp.readline() #一次读取一行数据 text_lines = fp.readlines() #读取多行数据,返回一个列表 print(text_lines) fp.close()
-
总结:
- 几种不同的读取和遍历文件的方法比较:如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果时配置文件,调用readlines()最方便。普通情况,使用for循环更好,速度更快。
-
f.write()
- 将字符串或bytes类型的数据写入文件内。write()动作可以多次重复进行,其实都是在内存中的操作,并不会立刻写回硬盘,直到执行close()方法后,才会将所有的写入操作反应到硬盘上。在这过程中,如果想将内存中的文件修改,立刻保存到硬盘上,可以使用f.flush()方法。
fp = open('./test123.txt,'w') fp.write('hello bobo') fp.close() #‘w’将文件内容清空,在写入新数据
fp = open('./test123.txt,'a') fp.write('hello bobo') fp.close() #‘a’将文件内容清空,在写入新数据
-
f.close()
- 关闭文件对象。当处理完一个文件后,调用f.close()来关闭文件并释放系统的资源。文件关闭后,如果尝试再次调用该文件对象,则会抛出异常。忘记调用close()的后果时数据可能只写入一部分到磁盘,剩下的丢失了,或者更糟的结果。也就是大象塞进冰箱后,一定不要忘记关上冰箱的门。
with关键字
with关键字用于Python的上下文管理器机制。为了防止诸如open这一类文件打开方法在操作过程中出现异常或者错误,或者最后忘了执行close方法,文件非正常关闭等可能导致文件泄露、破坏的问题。Python提供了with这个上下文管理器机制,保证文件会被正常关闭。在他的管理下,不需要再写close语句。注意缩进。
with open('./test123.txt,'r') as fp:
test = fp.read(5)
print(test)
# 上下两组代码功效一样
fp = open('./test123.txt','r')
text = fp.read(5)
print(test)
fp.close()
-
对图片,音频,视频、压缩包等二进制的数据进行文件读写操作
- 实现一个图片文件的拷贝
-
打开一个图片文件,读取其二进制的数据
-
将读取到的数据写入到另一个路径下
def cpimg(ingPath,targetpath): fp = open(imgpath,'rb') img_data = fp.read() new_fp = open(targetPath,'wb') new_fp.write(img_data) fp.close() new_fp.close() cping('./girl.jpg','./girls/meinv.jpg')
序列化模块
序列化:将python中的字典,列表对象转化成指定形式的字符串
反序列化:将指定格式的字符串转化成字典,列表对象
-
基本使用
import json dic = { 'hobby':['bootball','pingpang','smoke'], 'age':20, 'score':97.6, 'name':'zhangsan' } #序列化:将字典对象转化成了json格式的字符串 r = json.dumps(dic) print(r)
import json str = '{"hobby": ["bootball", "pingpang", "smoke"], "age": 20, "score": 97.6, "name": "zhangsan"}' #反序列化 将json格式字符串转化成了字典对象 dic = json.loads(str) print(dic)
import json dic = { 'hobby':['bootball','pingpang','smoke'], 'age':20, 'score':97.6, 'name':'zhangsan' } fp = open('./dic.json','a') json.dump(dic,fp) fp.close()
import json fp = open('./dic.json','r') #load将文件中的字符串数据进行读取,且将其转化成字典类型 dic = json.load(fp) print(dic) fp.close()
正则模块(一般/不重要)http协议
-
什么是正则表达式?
- 正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a到z之间的字母)和特殊字符(例如,*,+,?等)。
- 正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
-
常用的正则标识
单字符: .: 除换行以外的所有字符 未完待续~
Comments NOTHING