python爬虫
2024-01-22 11:13:54 1 举报
AI智能生成
登录查看完整内容
Python笔记,主要为爬虫方向,包括基础语法,并发编程,数据库操作,前端知识,爬虫,scrapy框架,JS逆向。因为是学习笔记,所以并不是很专业,边学边记,随时查漏补缺,JS逆向部分还未完善。
作者其他创作
大纲/内容
尽管会警告,但还是照样运行告诉程序员看:我这个函数规定了数据类型,你传错类型后续出了什么事不负责
给变量赋值的时候,不需要指定变量的数据类型
python是动态语言
这个在下面字符串格式化里会详细讲
1+2=3
print(\"{0}+{1}={2}\".font color=\"#ff0000\
不写end,默认换行(end=\"\\")
print(\"balabalabala\
%10s,表示10个字符的宽度
字符串
%s
同上
整数
%i or %d
%.3f,保留三位小数
%10.3f,宽10并保留三位小数
浮点数
%f
\"这个是%s常用的%s格式化\" % (\"我\
f'我叫{\"你猜\"},今年{88}岁'
print
保留字
换行,一般在末尾,strip对其也有效
\
空字符
\\0
tab,四个空格
\\t
‘
\\'
“
\\''
\\
\\\\
以下不常用
发出系统响铃声
\\a
退格符,覆盖删除前一个
\\b
换行,并将当前字符串之前的所有字符删除
不常用
\\v
\\f
转义字符
不可变数据(3 个):Number(数字<包括int(整型)、float(浮点型:小数)、bool(布尔型)、complex(复数)>)String(字符串)、Tuple(元组);不可以增删改做改变之后 id() 查看内存地址,改变
可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。可以增删改增删改后用内置函数 id() 查看内存地址,不变
Number(数字)String(字符串)List(列表)Tuple(元组)Set(集合)Dictionary(字典)
作用:number:用来存储数字类型的数据,多用于运算,布尔型也属于number,用于逻辑运算,true为真,false为假string:用来存储字符串,如中文、英文等,一般用于一些描述性的文字输出list:用来存储列表,即多个数据类型任意的元素tuple:用来存储元组,一种有序的且不可更改的集合set:用来存储集合,一种无序的,且可以更改的集合dictionary:用来存储字典,一种无序的,可变的有索引的集合
内置的 type() 函数可以用来查询变量所指的对象类型
str()
int()
float()
字符类型转化
数据类型
单行:#
多行: ''' 或者\"\"\"
注释
input
+
-
*
/
%取余
**幂
//取整除,向下取整
算数
=
+=
-=
*=
/=
%=
**=
:=,不常用
赋值
==
!=
<
>
<=
>=
比较
and,与
or,或
not,非
逻辑(布尔)
按位与,都是1才是1,其他全是0
&
按位或,有1就是1
|
按位异或,不同就是1
^
按位取反,0变1,1变0
~
x<<n==x*(2**n)
左移动相应位数,高位丢弃,低位补0
<<
int除完会变成float所以只是数值上相等,type上还是不同的
x>>n==x/(2**n)
右移动相应位数,低位丢弃,高位补0
>>
位运算
or
and
**
优先级
运算符
顺序结构
python一切皆对象,所有对象都有一个布尔值,bool()
false
数值0
None
\"\
空字符串
空列表
空元组
空字典
set()
空集合
以下都为false
其他对象布尔值都为true
对象的布尔值
比较值
是否是同一个地址
is
if isinstance (当前对象,指定对象)
判断当前对象是否是指定对象的子类
isinstance
判断方法
if
单分支
if else
双分支
if elif elif (else)
多分支
if 套 if
嵌套
一个占位符:没想好if判断后执行啥,先pass占个位
pass语句
插一嘴
判断语句成立执行语句1,否则执行语句2
语句1 if 判断语句 else 语句2
三元运算
选择结构
range(stop)
range()
一般while true,也可以while+条件
while循环
for_in 循环
结束当前循环,本层
if...break
或者直接break
break跳出
结束当前循环,直接进入下次循环,本层
if...continue
或者直接continue
continue
当循环中不执行break,则执行else
for...else
break会让else中的代码不执行,continue就没这个功能
while...else
else
嵌套循环
循环结构
三大结构(顺序,选择,循环)
字符串长度为0或者1
符合标识符的字符串(只包含字符 数字 下划线)
a b c值一样,type也都是strb的值在运行之前就连接完毕了c的值是程序运行的时候通过join的方法和列表连接的,运行的时候会开辟新的存储空间,所以和a b的存储空间不一样
字符串只在编译时进行驻留,而非运行时
交互模式(cmd)
在内存中保存一份且不可变字符串的方法。(相同的字符串只保留一份)
import sysa = sys.intern(b)
相同的值可以强制驻留
pycharm对字符串进行了优化,有些不驻留的进行了强制驻留处理
因为字符串的拼接和修改比较影响性能
避免频繁重复创建销毁,提升效率节约内存
join()是先计算所有字符串的长度,再拷贝,只new一次对象,效率比+高
用str类型的join来拼接字符串,而非+
优点
驻留
找不到报valueerror
查询substr(子串)第一次出现的位置
index()
查询substr(子串)最后一次出现的位置
rindex()
找不到返回-1
find()
rfind()
返回str在start和end之间,在my_str里出现的次数
count()
检查字符串是否以指定字符串开头,返回True或False
startswith()
检查字符串是否以指定字符串结束,返回True或False
endswith()
查
所有都大写
a.upper()
所有都小写
a.lower()
大转小,小转大
a.swapcase()
第一个大写,其余小写
a.capitalize()
每个单词的,第一大写,其余小写
a.title()
大小写转换
参数第一个是宽度,第二个是填充符(默认空格)
如果宽度参数小于字符串长度,则返回原字符串
居中
左对齐
右对齐
一个参数,如果是-87654,在 - 后面填0
a.zfill(20)
右对齐,左0补齐
对齐
从左开始分割,默认从空格开始分割
放回一个列表
maxsplit是可选参数
split()
从右侧,同上
rsplit()
字符串分割为str前+str+str后三部分
partition(str)
若字符串中有换行,则按行分割,返回各行为元素的列表
splitlines()
分割
是否为合法标识符字符串(只包含字母(中文) 数字 下划线)
a.isidentifier()
是否全是空白字符(空格 换行 tab)
a.isspace()
是否全是字母(中文)
a.isalpha()
是否全是十进制数字
a.isdecimal()
只要是数字就行,中文数字,中文大写数字,罗马数字
是否全是数字
a.isnumeric()
这个就只是阿拉伯数字
是否只含数字
a.isdigit()
是否全是字母(中文)和数字
a.isalnum()
判断
第三个参数是最大替换次数,可选
a.replace('原字符串',‘替换成的字符串’,2)
替换
将列表中的元素用 * 合并成一个字符串
'*'.join(list)
元组同上
a = '*'.join('hello world')print(a)>>h*e*l*l*o* *w*o*r*l*d
单个字符当一个元素
字符串序列
join()
合并
比较规则:从第一个字符开始比较,到第一个不相同的字符
查看a的原始值
ord('a')
查看原始值为97的字符
chr(97)
比较原理:比较的是字符的原始值order value
从0到4
a[:5]
从6到最后
a[6:]
a[start : end : step]
step正从前开始,step负从后开始
切片
strip()
删除两端空白字符
其他方法
一共三位
{0:.3}
三位小数
{0:.3f}
宽10位,共三位小数
{0:10.3}
\"这个是{0}比较常用的{1}格式化\".format(\"我\
格式化(按固定格式输出)在字符串中加入变量
>>b'\\xd4\\xde\\xc3\\xc0\\xd3\\xde\\xd5\\xdf'>>b'\\xe8\\xb5\\x9e\\xe7\\xbe\\x8e\\xe6\\x84\\x9a\\xe8\\x80\\x85'
word = \"赞美愚者\"encoding1 = word.encode(encoding='GBK') # GBK编码格式 一个中文两个字节encoding2 = word.encode(encoding='utf-8') # utf-8 一个中文三个字节print(encoding1)print(encoding2)
将字符串转换成二进制数据(bytes)
编码
>>赞美愚者>>赞美愚者
decoding1 = encoding1.decode(encoding='GBK')decoding2 = encoding2.decode(encoding='utf-8')print(decoding1)print(decoding2)
将bytes数据转换成字符串
解码
编码转换
lst=[\"a\
lst=list([\"a\
创建
列表生成式
获取制定元素的索引(第一个)
lst.index(\"a\")
获取列表中index为1的元素
lst[1]
从前往后start
step为正
从后往前start
step为负
lstChild = lst[start : stop : step]
获取
in
not in
判断元素是否存在
item是可迭代对象
for item in lst:
首选
>>1>>2>>3
while
遍历
L.count('a')
统计元素个数
list.index('元素')
查询元素的索引
列表末尾添加一个元素
append()
extend()
任意位置插入一个元素
insert()
增
lst.remove(元素值)
一次删除一个,有重复的删除第一个
remove()
lst.pop(索引号)
删除指定索引的元素,不写索引默认最后一个元素
pop()
清空列表
clear()
del list[2]
根据下标删除
删除列表
del
删
lst[index]=修改后的元素
切片修改
lst[start:stop]=[........]
改
增删改查
通过id(lst)可查看还是同一个列表
key参数主要用于自主选择排序的方式,一般=某函数
lst.sort(key=function( ))
lst.sort()
创建了一个新列表
sorted(lst)
将列表倒叙
reverse()
排序
1. 列表对象有序排序2. 索引映射唯一数据3. 列表可以存储重复数据4. 任意数据类型混存5. 根据需要动态分配和回收内存
python test.py 卢卡斯>>卢卡斯
sys.argv就是一个列表,这个列表中存储着运行是传递的参数,注意全部是字符串
import sysprint(sys.argv)
给程序传递参数
列表
()可以省略
(\"元素\",“有一个元素”, 8 )
tuple((\"元素\",“有一个元素”, 8 ))
tuple(......)
一个元素的元组,一定要加逗号
(\"元素\",)
()
元组不可变,但元组中的可变数据可以改变,比如有个list元素
>>python>>good>>98
t = tuple((\"python\
元组
{......}
将列表转成集合
set([列表])
将元组转成集合
set((元组))
s1 = set(\"python\
将字符串转成集合
set(\"字符串\")
set(......)
用{}创建的是空字典
set() 空集合
s.add(一个元素)
s.update(至少一个元素)
元素不存在报keyerror
s.remove(元素)
元素不存在不报错
s.discard(元素)
s2 = s1.pop(),s2是s1中拿出的那个元素
不能加参数,随意删一个
s.pop()
清空元素
s.clear()
删除集合
del s
集合的元素为不可变类型,所以无法修改
!=
相等
s1是否是s2的子集
s1.issubset(s2)
子集
s2是否是s1的超集
s2.issuperset(s1)
超集
s1和s2是否不相交
s1.isdisjoint(s2)
不相交
集合之间的关系
s1.intersection(s2)
s1 & s2
交集
s1.union(s2)
s1 | s2
并集
s1.difference(s2)
s1 - s2
差集
s1.symmetric_difference(s2)
s1 ^ s2
对称差集(s1并s2 - s1交s2)
交并差
集合生成式(和列表差不多)
集合
列表,集合转换为元组
tuple()
可完成对list和tuple的快速去重
列表,元组转换为集合
set()
元组,集合转换为列表
list()
序列类型的类型转换(不包括字典)
from collections.abc import Iterableprint(isinstance([]font color=\"#ff0000\
判断数据是否可迭代
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表
zip()
a = {\"name\
c={},空字典
items = [\"a\
字典生成式
如果键不存在,报错,keyerror
a[\"name\"]>>Lucas
[键]
如果键不存在,返回None
如果键不存在,返回默认值
a.get(\"gaga\
a.get(\"name\")
get(键)
key的判断
for key in dict.keys(): print(key)
所有键
for val in dict.values(): print(val)
所有值
每对键值输出为一个元组tuple
for item in dict.items(): print(item)
所有元素
查(键对应的值)
删除指定键值对
del a[\"name\"]
删除整个字典a
del a
清空字典a
a.clear
a[\"新键\"]=对应的值
a[\"要修改的键\"]=新值
获取字典中所有的键(key)
dict.keys()
获取字典中所有的值(value)
dict.values()
获取字典中所有的键值对(key.value)
dict.items()
视图操作
获取键keys
print(item)
获取值values
print(a[item])
print(a.get(item))
for item in a:
遍历元素
1.键值对,键不可以重复,值可以重复2.元素是无序的3.key必须是不可变对象4.动态调整内存5.消耗较大内存6.hash表计算存储位置
字典
[x for x in range(4)]
[x for x in range(4) if x % 2 ==0]
加条件
自动x和y在取值范围内排列组合
for嵌套循环
[变量 for 变量 in 可迭代对象]
列表(List Comprehension列表解析)
快速生成数据
推导式
同上,默认取到的是key
快速提取
经典python交换变量值
拆包
def 函数名([函数参数]): 函数体 [return xxx]
返回长度
len
返回最大值
max
del a或者del(a)
稍后再说
time包
用到再说
random包
部分内置函数
自定义函数记得加说明文档
a和b
形参(定义处)
a和b的实际值
实参(调用处)
形参实参
参数调用
形参实参名称可以不同
参数传递
记住python赋值不是改变对象的值而是将对象指向存储值的内存就行
如果是不可变对象,在函数体内的修改不影响实参的值
如果是可变对象,在函数体内的修改影响实参的值
参数传递时的内存
函数定义时,给形参定义默认值,调用时只有和默认值不符时传递实参
这时args是一个元组,可以进行元组的各种操作
结果是元组
个数可变的位置参数
表示调用函数时多余的未命名参数都会以元组的方式存储到args里
一个函数只能定义一个位置参数
*args(未命名参数)
结果是字典
个数可变的关键字参数
表示调用函数时多余的命名参数都会以键值对的方式存储到kwagrs
*之后的参数只能用关键字参数传递
一个函数只能定义一个关键字参数,且要放在位置参数后面
**args(**kwagrs)(命名参数)
a= 1b= 2c= 3
a= 10b= 20c= 30
a= 11b= 12c= 13
总结
参数定义
return后面的语句不会被执行,只执行第一个return
返回值和结束函数2个作用
是元组就能拆包
返回多个值时,结果为元组
返回值return
*对列表、元组、集合可以拆包,但一般都是在调用函数时用
**可以对字典进行拆包,拆包的结果是命名参数
通过星号拆包
一般情况下用来打印提示等类似的功能
无参,无返回
一般情况下像采集数据等功能会用到
无参,有返回
一般情况下对某些变量设置数据而不需结果时用此类函数
有参,无返回
大多数
有参,有返回
4种函数
# 例1 画线def line(): print('---------------'*3)def print_lines(num): for i in range(num): line() i += 1print_lines(5)
函数嵌套
函数内的
局部变量
要在函数内修改全局变量,先用global声明
函数外的
全局变量
变量的作用域
add_up = lambda 形参1,形参2:表达式
接收任何数量的参数但只能返回一个表达式的值,其默认就是返回的,不用写return
1.像普通函数一样,变量名( )
2.在其他函数实参的位置,直接定义一个lambda
使用方式
匿名函数(lambda)
阶乘函数def fac(n): if n == 1: return 1 else: res = n*fac(n-1) return res
占内存效率低,但简单易懂啊
就是函数内调用自己
有调用有终止
其实递归是一种算法,一般用来解决“树”、“图”等操作,还能解决像“汉罗塔问题”等
递归函数
函数
面向过程 VS 面向对象
不同的数据类型属于不同的类
python中一切皆对象有id(内存空间),有type(数据类型),有值
900就是int数据类型下的一个对象
类 VS 对象
一般首字母大写,其余小写
class Student: pass
类的创建
可以在外面通过Student.gender = None,加一个类属性
类属性
当通过实例对象调用方法的时候,self能够自动指向实例对象,从而拥有了操作这个对象中的属性或者方法的可能。
self
比如上面的例子中,eat方法,并没有指向name或者age,所以其实属于静态方法
静态方法之所以不需要self,是因为方法内不需要实例对象的引用
能获取构造函数定义的变量,也不可以获取类的属性。
静态方法
不能获取构造函数定义的变量,可以获取类的属性。
类方法
组成
stu = Student(初始化方法的参数)
对象名.方法名
stu.eat()
类名.方法名(类的对象)----方法定义处的self
Student.eat(stu)
调用属性
stu.name
对象的创建
删除属性
print(Student.native_pace)print(stu.native_pace)
# 直接写在类里面的变量 native_pace = '江苏'
类中方法外的变量,被该类所有对象共享
Student.cm()
@classmethod def cm(cls): print('类方法写cls')
使用类名直接访问的方法
Student.method()
@staticmethod def method(): print('静态方法不加self')
调用
类
再用B.aa就可以调用A的属性和方法了
B.新属性aa=a
类A,实例对象a=A()类B,实例对象b=B()
对象关联
self.__age = age再用stu.__age是访问不到的,但用stu._Student__age还是能访问,纯靠自觉
如果属性不希望被外部调用,可以前面加2个_
将数据(属性)和行为(方法)包装到类对象中。(转为私有属性or隐藏属性)
封装
没写父类的默认继承object
super().__init__( )
定义子类时,要在构造函数中调用父类的构造函数
子类有多个父类
子主题
如果2个子类中都继承了同个父类,当在子类中通过父类名调用时,parent被执行了2次
如果2个子类中都继承了同个父类,当在子类中通过super调用时,parent被执行了1次
关于多继承的__mro__顺序用类名.__mro__,这个魔术方法,可以查看这个类的继承顺序
支持多继承
子类只有一个父类
单继承
1.super().__init__相对于类名.__init__,在单继承上用法基本无差2.但在多继承上有区别,super方法能保证每个父类方法只会执行一次,而类名.方法()会导致方法可能被执行多次3.多继承时,用super方法,对父类传参,由于super的算法,必须把参数全部传递,否则报错,所以就要加上*args和**kwargs4.单继承时,用super方法,不用全部传参,只能传父类方法所需的参数,否则报错注:在多继承时,尽量不用super,用父类名.方法()用super虽然对于同个父类的调用可以避免重复执行,但代码阅读难度增加,不利于debug
基本概念
返回对象的描述
https://blog.csdn.net/m0_57133702/article/details/120568504
__str__()方法
所有类的父类
object类
就是在子类中重新定义父类的方法先加个super().xx()这样可以继承父类的xx()方法然后再加新的功能
如果不加super(),就继承不了父类方法
子类想输出自己独有的东西,父类没有
方法重写
继承
反正就是调用类的方法很方便
>>人向狗进行了攻击>>普通狗汪汪叫>>人向狗进行了攻击>>狼狗狂吠>>人向狗进行了攻击>>普通狗汪汪叫
class Dog(): def bark(self): print(\"普通狗汪汪叫\")class LangDog(Dog): def bark(self): print(\"狼狗狂吠\
有别于如Java的静态语言,python这种动态语言的多态就相当灵活了如果一个变量存储了某一个实例对象的引用,且通过这个变量调用指向的对象中的某个方法,此时如果变量指向的对象是子类创建的那么就调用子类中的方法,如果是父类创建的对象那么就调用父类的方法
多态
三大特征(封装,继承,多态)
常用内建属性
如果需要当前类做多个实例化,则只需要创建一个空间来存储实例对象
帮助类在内存中只开辟一个空间储存属性和方法在__init__之前
__new__(cls)
__init__
实例对象p是通过__class__找到Person这个类的,然后才能调用类中的方法
__class__
实例对象要访问实例属性,要通过这个方法
方法中的item参数是实例属性的属性名称
如果访问的是name1,会被拦截,传回指定值,如果访问name2,则可以返回传入的值
用户访问属性,传入一个参数,可以返回提前设定好的值
能够完成属性访问时进行拦截
__getattribute__
输出类的说明描述。类的说明写在类名下一行,__doc__只调用那段注释。
类名.__doc__
__doc__
module打印所在的模块(所在的py文件)class打印所在类
__module__和__class__
一般不去改
当对象在内存中被释放之前,自动触发执行
__del__
可以在__call__中添加一些功能,用Foo()()直接调用
当成函数去用,就可以传参了
把实例对象当成一个函数去使用
__call__
返回类的属性和方法,以及对应的值返回实例对象的属性,名称和值
__dict__
返回值只接受string
如果在类中定义了__str__方法,打印方法时,默认输出这个方法的返回值
__str__
字典一定要用foo['hahaha']操作才能调用这三个方法
对容器类型进行操作,获取,设置,删除对于列表,元组,字符串。foo[index],索引对于字典。foo[key],键
内建属性
gender属性只有stu1有
show()只有stu1能调用
@staticmethoddef static_func(): print('这是一个静态方法')Student.address = '深圳' # 先创建一个类属性@classmethoddef class_func(cls): print(f'这是类方法,类属性值为:{cls.address}')Student.static_func = static_funcStudent.class_func = class_funcstu2.static_func()stu1.class_func()
创建完对象后,为个别对象动态绑定一个属性(方法),该属性只有这个对象有,其他对象没有
动态绑定属性和方法
限制属性的创建1.为了限制随意给对象添加属性或者方法2.对子类不起作用,只限制当前类3.不可以卸载__init__中
__slots__特殊属性
@property def func(self): return '调用func'foo = Foo()res = foo.func
当前类中,装饰在某个方法上,使得这个方法可以像属性那样被调用用于拿到当前方法中计算的返回值被property装饰的方法不能创建除了self之外的形参
>>第一页显示第0到第9条数据>>第一页显示第10到第19条数据
'''对于京东商城中显示电脑主机的列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据 这个分页的功能包括:1.根据用户请求的当前页和总数据条数,计算出m和n2.根据m和n去数据库中请求数据'''
简单例子:
>>100>>200>>... AttributeError: 'Goods' object has no attribute 'price'. Did you mean: '_price'?
用property进行商品价格控制
>>80.0>>setter被调用>>160.0>>deleter被调用>>.... AttributeError: 'Goods' object has no attribute 'original_price'
在商城开发中设置打折价格
1.装饰器方式
>>100
类属性 = property(需要装饰的方法)
>>当前get bar被执行>>a>>当前set bar被执行 d>>当前del bar被执行>>当前字符串是对property的一个解释说明
第一个参数是方法名,调用 对象.属性 时自动触发执行方法第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法第四个参数是字符串,调用 类名.属性.__doc__ ,此参数是该属性的描述信息注:property中的参数顺序一定要按照get set del排序
案例
python2.7中常这么用,3之后很少用
2.类属性方式,创建值为property对象的类属性
property属性
test_1
>>num1是可引用内容>>100>>AttributeError:不拉不拉,test_1没num2这个属性
test_2
函数入口:写在入口下的内容只能在当前文件内运行,被引用时,入口下的内容引用不到
if__name__=='__main__':
特殊属性
new和init的传参过程
特殊方法
用到再查
特殊属性和方法
类的赋值和浅拷贝
面向对象
for
# 一般写except Exception:当前python所有错误的基类
iter()和next()
可以记住遍历的位置的对象只能往前不能后退
如果对象只有__iter__方法,是可迭代对象如果对象有__iter__和__next__方法,则是迭代器
Iterable和Iterator
1. 先定义主要类
2. 给这个类定义一个迭代器
>>11>>22>>33
3. MyList的实例对象my_list这时就可以用for循环遍历了
例
自定义迭代器
1.初始化和add方法
2.iter和next
3.实例化,调用3次add,for循环遍历
例:学生系统
多继承也可以,但项目开发中,尽量少继承,继承的执行速度比对象关联慢
注释:如果有4个类,分别在4个py文件中这4个类的迭代的方法是一模一样的写一个迭代器直接导入引用
迭代器
定义了一个生成器函数,函数返回一个生成器对象,然后就可以通过for语句进行迭代访问了。
生成器函数返回生成器的迭代器。 \"生成器的迭代器\"这个术语通常被称作\"生成器\"。要注意的是生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法,其中一个就是__next__()。如同迭代器一样,我们可以使用__next__()函数来获取下一个值。
概念:含yield的函数
会在yield处暂停
执行流程
>>列表推导式时间为3.2063777446746826
列表解析(列表推导式)
>>生成器表达式时间为0.0
生成器表达式
对比
列表解析:1.开辟一个内存2.开完内存创建所有值生成器表达式:1.返回的不是具体序列数据,是生成器对象2.使用next()取生成器中的值,内存中只有一个值。调用一次,产生一个值
原因
>>True>>True>>25000000>>[]
创建长序列时使用(x for x in range(100))
>>0>>接收到参数,将此时的i(即0)+10输出=10>>1
注意 i 的值,第一次取值时在yield处暂停,第二次取值继续时,先读if判断,此时 i 还未 +1
1.send方法与next方法类似2.可以向生成器传递一个参数,并在生成器中进行接受3.一般不在第一次获取值时使用,第一次获取时必须传递None print(my_iter.send(None))4.可以在程序运行的过程中对当前的函数运行过程进行操作
send()
关闭之后再取值会抛出异常
my_iter.close()print(next(my_iter))
关闭当前生成器(相当于删除或销毁)
close()
内置方法
当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有执行。
当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield处暂停next()方法返回值就是yield处的参数
继续调用next()方法时,函数直接接着上一次暂停的yield处继续执行,到下一个yield出暂停如果后面没有yield就抛出StopIteration异常
return:代表整个函数结束,结束前返回值,函数中只能有一个,生成器中不可以有returnyield:不代表函数结束(只是暂停),返回一个生成器对象(可用next取到其值),函数中可以写多个yield
和return的区别
生成器内部声明了迭代器协议:__iter__ __next__,不需要自己定义
生成器
def person(): def info(): xxxxxxp = person() # 这里只引用了info(),并没有调用,所以info没有运行,此时person()这个函数还是占着内存p() # 这样就运行了info()这个函数,运行完就会释放内存
如果在一个函数中有内层函数如果内层函数没有释放,则外层函数会永远占着内存
>>内部函数的参数为: 10>>30
1.有函数嵌套,一般只嵌套一层2.内部函数引用了外部函数的参数就是当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。可以这样理解,闭包就是能够读取其他函数内部变量的函数。
>>1>>2>>True None None>>True>>2217856965072>>140714178485464
定义一个函数可以理解为:定义了一个全部变量,变量名就是函数名(test)这个test变量指向一个代码块,就是函数就是说test保存了一个代码块的地址,即引用
函数引用
>>6>>7>>51>>52
使用闭包修改函数中的变量nonlocal
闭包(优雅的编程技巧)
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。简单来讲:装饰器的作用就是为已经存在的函数或者对象添加额外的功能。
概念
1.将say_hello这个函数的引用,传递给debug函数(引用:给内存地址,不运行)
2.预读wrapper代码。预读开始
3.发现wrapper函数的返回值是say_hello函数的调用
4.返回wrapper函数的引用。预读结束
5.执行debug(),并传递一个say_hello的函数引用。开始运行
6.debug函数返回的是一个内部函数的引用
print(f'********')
print(‘hello’)
return func(),就是say_hello()
7.sayHello(),调用内部函数wrapper
这段代码的运行流程
>>[DEBUG]: enter say_hello()>>hello
初体验(现在不这么写)
1.say_hello()处,@debug,运行debug
2.将@debug下的函数传给func
3.预读wrapper,返回wrapper的引用
4.say_hello加(),实际上就是调用wrapper函数。>>打印[DEBUG]这段话
5.wrapper返回func(),就是say_hello()。>>打印hello
流程
效果一样,这是最简单的装饰器,被装饰函数无法传参
上面的debug函数其实已经是一个装饰器了,它对原函数做了包装并返回了另外一个函数,额外添加了一些功能。因为这样写实在不太优雅,在后面版本的Python中支持了@语法糖。在不修改say_hello函数原代码的基础上,加一个debug功能。
>>[DEBUG]: enter say()>>hello! lucas
被装饰函数可以传参
万能装饰器(被装饰函数可以有多个参数)
>>info: enter function say()>>hello lucas
终极装饰器(带参数的装饰器)
简单的类装饰器
>>INFO: enter function say()>>say lucas>>INFO: enter function say()>>say lll>>None
带参数的类装饰器
装饰器要求接收一个callable对象,并返回一个callable对象。所以类装饰器中,__init__接收函数,重载__call__()并返回一个函数一个装饰器可以装饰多个函数一个函数可以有多个装饰器(很少用)
类装饰器
装饰器(依赖闭包)
三器一闭
将它赋值给一个变量
拷贝它
为它增加属性
将它作为函数参数进行传递
所以可以对它做一下操作
创建一个类
代码解释
类的本质是个对象,
类是可以动态创建的
实例对象由类对象创建,所以type(实例对象),出来的是对应的类而类对象是由type创建,所以type(类对象),出来的是type。object也是类,所有类都继承于object,但其创建者是type
所有类都继承于object,所有类都是由type创建。object也是由type创建的,type也是由type创建
type函数的第二种用法(自己写函数不要让一个函数有两种完全不同的功能)
定义一个父类
定义一堆方法
将方法和父类放进子类中去
>>住在深圳的lucas同学>>lucas 深圳>>这是一个静态方法>>这是类方法 深圳
实例化后检查子类功能
使用type创建一个类
type
元类创建类,类创建实例对象
函数type实际上是一个元类。type是python在背后用来创建所有类的元类。str是用来创建字符串对象的类int是用来创建整数对象的类type是用来创建类对象的类可以通过__class__属性来看到这一点
元类本身的探究
__metaclass__
元类的主要目的是为了创建类时能够自动地改变类
自定义类
开发中用不到,用于阅读源代码
元类
一个简单的登录系统
输入两个整数算商
try正常运行,出对应异常走except
try--except
只执行一个分支
try--except--except(多个异常)
except后面跟一个元组
多个异常
try内没有异常走else
try--except--else用的不多
报不报错都要跑
try--except--else--finally
as e
捕获异常对象(异常本身信息)
用Exception
不写默认Exception
全捕捉
自定义的异常要继承Exception
例1
先定义一个异常类
在函数中主动抛出异常
调用函数
例2
str_1='d52a733i2327ha244i982d23s553b245'
通过异常捕获获得以下字符串里面的字母字符
自定义异常(用的不多)
处理异常
不加as e
打印出自定义信息
一般不这么用,在except下面加raise
raise
抛出异常
网络请求
文件读写
一般使用场景
https://docs.python.org/zh-cn/3.9/library/exceptions.html#exception-hierarchy
所有异常的基类
BaseException
不知道会有什么异常就用这个
常见异常的基类(父类)
Exception
除(or取模)0,所有数据类型
ZeroDivisionError
序列中没索引
IndexError
映射中没这个键
KeyError
未声明/初始化对象(没有属性)
NameError
语法错误
SyntaxError
传入无效参数
ValueError
常见异常类型
traceback模块
debug shift+F9
pycharm程序调试
异常处理
file_name 变量:是一个包含要访问的文件名称的字符串值。
- 读取 - 默认值。只能读取,如果文件不存在则报错。
'r'
- 追加 - 在原有内容基础上追加内容,在末尾写入,如果不存在则创建该文件。
'a'
- 写入 - 只能写入,如果文件不存在则创建该文件。
'w'
可读可写
‘w+’
- 创建 - 创建指定的文件,如果文件存在则返回错误。
'x'
- 文本 - 默认值。文本模式。
't'
- 二进制 - 二进制模式(例如图像)。
'b'
二进制格式打开一个文件,只读
'rb'
同上,只写
'wb'
同上,追加
'ab'
同上,读写
'wb+'
注意:尽量一次只用一种模式,+模式好像不太好用
access_mode 变量:指打开文件的模式,对应有只读、写入、追加等。access_mode变量值不是必需的(不带access_mode变量时,要求file_name存在,否则报异常),默认的文件访问模式为只读(r)。
buffering:(一般很少用)如果buffering的值被设为0,就不会有寄存;如果buffering的值取1,访问文件时就会寄存行;如果将buffering的值设为大于1的整数,表示这就是寄存区的缓冲大小;如果取负值,寄存区的缓冲大小就是系统默认的值。
内置函数open
E:/zoom/palegechong/图灵/基础语法/文件操作/文档/test.txt
从根文件夹开始
绝对路径
./文档/test.txt
相对于当前工作目录的路径
相对路径
路径
encoding='utf-8',设置编码
打开文件
read()
write()
读一行
readline()
放进列表,每行一个元素
readlines()
writelines()
基本文件操作
如果对文件进行了读写之后,要关闭文件流(close())因为文件读写需要使用计算机资源如果不关容易程序异常,还占系统资源
with open可以自动调用close
with open as f:
文件流操作
文件名这里写路径,不论是绝对路径还是相对路径
os模块
重命名
也是填路径
删除
普通版
fileinput读完一行释放一行内存
fileinput模块
迭代读取
StringIO函数
序列化/反序列化
json模块
前端的字典
JSON
文件操作
基础语法
80端口 HTTP服务21端口 FTP服务 上传下载22端口 SSH服务 远程链接443端口 HTTPS服务 加密的HTTP协议
知名端口0-1023
1024 < 可用端口 < 10000
ip定位服务器地址,端口定位在操作系统上的具体位置
ip: port
传输快,结构简单
广播:可以完成聊天室开发(在微信群里一个人发消息所有人都看见)
不安全,容易丢包,只负责发送,不保证收不收得到
文本、二进制文件【图片音频视频】
发送方也可使接收方
不严格区分客户端和服务端
一对多
网络直播游戏部分物联网
udp
严格区分客户端和服务端
发送数据前会先链接服务器,如果检查服务器不在线,就抛出异常
一对一
tcp
协议
TCP/IP
family -> 网络类型 ipv4 --> pc .AF_INET ipv6 --> 移动端 .AF_INET6
type -> 协议类型 tcp .SOCK_STEAM udp .SOCK_DGRAM ftp ssh ...
C/S架构的网络流程
发送信息
循环发送
0.创建套接字对象1.绑定本地地址(ip,端口),如果不绑定,每次都会随机分配一个端口2.接收数据,一次最大10243.打印接收到的数据,接收到的是(二进制信息,发送方地址)的一个元组,取index[0]且decode4.关闭套接字对象
接收消息
0.创建套接字对象1.绑定本地地址2.创建循环,循环接收3.接收数据,一次最大10244.打印接收到的数据[0]6.判断,收到exit就break7.循环外,关闭套接字
循环接收
发送函数和接收函数
主函数
可选择聊天器
UDP
网络流程
0.创建套接字1.链接tcp服务器.connect(ip和端口)# 服务器不在线则抛出异常2.向tcp发送消息.send(.encode('utf-8))3.关闭套接字# 要先让服务端连网
客户端
0.创建套接字对象1.绑定端口,随机端口别人可不好连接.bind2.使服务器转为监听(等待),tcp默认状态是连接别人.listen(128)默认最大连接数 多线程3.如果客户度连接成功,生成新套接字.accept() 堵塞方法,如果没有接收到消息,卡在这 进行数据接收和发送,获取客户端ip port 返回值是一个元组(套接字对象,(ip,port)),解包获取4.接收数据.recv(1024)5.打印信息6.收到消息后,发回执.send()(别了,还得改客户端)7.关闭套接字(2个)
服务器
注意事项
文件函数
if __name__ == '__main__': main()
前import socket后if __name__ == '__main__': main()
文件下载服务器
TCP
socket包/套接字
网络编程
线程有开始、顺序执行和结束3部分,有一个自己的指令指针,记录运行到什么地方。线程的运行可能被抢占(中断)或暂时被挂起(睡眠),从而让其他线程运行,这叫作让步。
线程是运行代码的一个最基本的资源
运行代码 是有线程去执行的
CPU读取内存中,代码的二进制数据。由进程分配内存(进程是操作系统资源的一个分配的单位)
进程创建一个线程对象去运行代码
代码运行起来都是单线程程序 操作系统创建一个进程来分配系统资源 进程创建一个线程来运行代码
线程
让进程创建多个线程来执行文件
多核CPU
任务切换机制理论上,一个双核CPU,同时只能执行2个任务。(全局解释器锁GIL)但时间片轮训,是让任务执行几纳秒的时间,然后切出去,让其他程序进来运行。快速切换,(就如两个智子以光速运动,感觉上是可以同时控制地球上的所有粒子加速器。)
时间片轮训
真正的用多个核心同时执行任务
并行
在一段时间中,多个任务进行切换执行
并发
python同一时刻只能运行一个线程,但切换速度快也能称之为多任务
多任务
hello是1秒打印一个
单线程
5个hello是同时打印
多线程
主线程结束后进入等待,等待子线程结束后退出
如果主线程代码执行完毕但子线程还在任务中主线程会等子线程完毕后退出程序
每段代码都有一个线程来运行
主线程主要负责执行.py文件如果.py文件中发现threading则自动创建一个子线程
主线程和子线程
一个简单的多线程任务
导入包
隔1秒打印一次
定义要运行的2个函数
放入线程
在线程执行过程中,执行线程的顺序是随机的运行代码时,只有一个线程被执行
输出结果
对线程执行顺序不加干扰
加个ctime方便看时间戳
前面都一样
关键是调用join()
输出
获取无顺序,下载无顺序,但要获取完才能下载
比如下载10部电影 首先开10个线程,获取电影数据 join()等待执行完成 再开10个线程,下载电影数据
干扰线程执行顺序
>[font color=\"#ff0000\
其他都一样,就是在线程start后print调用enumerate方法
查看当前线程数量
>>线程创建之前g_num为:0>>线程创建之后g_num为:690959>>线程1计算结果为:1000000>>线程2计算结果为:1264604
修改全局变量步骤:1.获取2.修改3.赋值回去多线程特点:(有可能是这样的)t1线程走完font color=\"#ff0000\
如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确。
t1获取全局变量时,上锁,t1解锁后,其他线程才能操作全局变量
锁对象是全局变量
上了锁的2个函数
>>add_1: 1000000>>add_1: 2000000>>主线程:2000000
主线程要阻塞
声明2把锁
A上锁后,等待1秒,再往下B锁已经被线程2上锁了,只有等线程2给锁B解锁后,线程1才能在锁B锁
用锁A锁全部,用锁B锁中间
run_1
B上锁后,等待1秒,再往下A锁已经被线程1上锁了,只有等线程1给锁A解锁后,线程2才能在锁A锁
用锁B锁全部,用锁A锁中间
run_2
2个线程都在等对方解锁,所以会卡死
主线程
先看代码
死锁
互斥锁(避免资源竞争)
共享全局变量
1. 如果要加实例属性新的构造函数要继承父类的构造函数Thread中的__init__有大量参数
2.重写方法run这个就不用继承了父类中的run方法只是单纯的调用了target引用的函数
注意
创建类,继承threading.Thread
创建实例对象,因为有继承父类,所以实例对象可以调用父类的start方法
用面向对象写线程
先导包
消息控制类用于处理连上服务器后接收传过来的消息和服务器信息
TCP套接字在initi里建套接字,绑本地信息,改listen在run里accept,调用Message(接收信息),start
主函数启动服务器
启动后可以同时连多个客户端
tcp并发服务器(再次强调,python没有并行)
threading.Thread(target=子线程函数,daemon=True)
如果想让子线程随主线程一起结束
启动新线程成本高,涉及与操作系统交互。尤其当需要创建大量生存期很短的线程时,线程池可极大提高性能.且线程过多系统会卡,池可以限制运行的线程数量
创建大量空闲线程(指定数量)有任务则启动执行,任务结束后返回空闲状态
from concurrent.futures import ThreadPoolExecutor
包
pool = ThreadPoolExecutor(max_workers=2)# 池内2条线程
创建对象
future_1 = pool.font color=\"#ff0000\
提交线程给池
print(future_1.done())
判断线程是否结束
print(future_1.result())
查看任务返回结果(阻塞,没算出结果阻塞当前线程)
pool.shutdown()
关池
整体
用法
用add_done_callback(),不阻塞
map(),同个函数不同参数
线程池(有待完善)
功能
定义
区别
优缺点
线程VS进程
一个程序运行起来后,代码与使用到的系统资源被称之为进程,它是操作系统分配资源的基本单元。
运行条件都已经具备,正在等待CPU执行
就绪态
CPU正在执行其功能
执行态
等待某些条件满足,例如一个程序sleep了,此时就处于等待态
等待态
进程状态
创建进程,一定要加if __name__=='__main__':函数入口具体原因解释起来挺麻烦的,先这么记着
运行哪个
target
target引用的函数的参数,元组
args
传命名参数,字典
kwargs
给进程命名,一般不用
name
指定进程组,一般不用
group
属性
启动
start()
判断是否存活
is_alive()
等待该进程结束,或者等几秒
join([timeout])
终止进程(可用于关闭僵尸进程)
terminate()
方法
不加p.join(),最后一个返回还是True,因为系统回收进程需要时间,主程序直接运行等不到
Process操作
子进程和主进程也是并发的
Process(target=要运行的对象)
1.创建、启动进程
os.getpid()获取当前进程pidos.getppid()获取当前进程父进程pid
2.查看进程pid
关于Process的参数
3.给进程传参
子进程是复制了主进程的代码重新开辟的一块新内存线程是同一个进程内的操作,就是同一块内存空间进程是重新开辟新内存空间
通过id()可以发现,2个子进程和主进程中的lst不是同一个列表
代码说明
4.进程不共享全局变量
put添加元素put_nowait不等待get提取元素get_nowait不等待full是否满了empty是否为空
队列:先进先出列表(栈是先进后出)
模拟抓取下来的数据
创建一个列表,把queue里的数据拿出来、塞进去
模拟数据读写
记得加join,保证都抓下来再读写
开启进程
#一个进程抓网站数据#一个进程数据读写
通过队列实现进程通信
5.进程间的通信(Queue)
创建进程池后,会自动创建设置好的进程个数
进程池(10个进程),重复利用比如有一百万个任务,放入池中十个十个执行,其他等待池中有任务执行完毕再进池运行
如果任务数大于进程数,多出来的任务会等待被执行
关闭进程池,关的是池这个容器,不是进程关了之后就不能动态创建进程了,但原本已启动的进程照常
close要在join之前
知识点
worker一个记录随机休眠的函数主程序创建一个3的池,循环10个创建worker进程
同步执行要十几秒
异步执行要比同步执行 快很多
6.进程池
Queue
多进程
一般情况下,代码编写要统一风格就是说如果使用线程,则整个程序统一使用线程
线程和进程都是计算机提供的都是os提供的api,就是系统层面的功能接口,python只是负责调度。但协程是完全python创建的,语言层面的创造。多线程其实也只有一个线程在运行,其他线程等在那也会浪费一些性能所以不如就开一个线程。
协程性能大于多线程
协程的大致运行状态:在一个线程中运行多个任务,任务和任务之间来回切换,并在同一时间内只能运行一个任务
这样就能输出1 3 2 4
greenlet(现在不用)
这个是1 3 4 2从1到2后,回不去1了,强行查个yield from func1()在2里,会报错
或者用yield(也很牵强,因为yield只能从上往下)
task对象和事件循环
@asyncio.coroutine
异步执行两个函数1.同时输出1 3 2.第一个await开始等待2s,第二个await也开始等待2s3.2s后,输出2和4如果下面的sleep改成3,那就是1 3,2s后font color=\"#ff0000\
1.函数被async声明,则函数为一个协程函数对象2. 函数对象不能被直接调用3.要运行协程函数,需要借助事件循环4.await是协程关键字, 用于等待一些耗时的任务并拿到这些任务的返回值之后才解堵塞5.在协程中,func是引用,func()不是调用,是返回协程对象6.事件循环只能执行可被等待对象:协程对象,asyncio对象,task对象
async
主流用法
基本思路
第一个链接拿到数据,没写入完成,第二是链接不会开始读的
单线程爬虫
协程爬虫
铺垫
事件循环做了什么:1.循环整个任务列表2.将任务分为可以执行和执行完毕两种3.将完成的任务从列表中剔除4.当列表为空,中断循环
原理
基本应用
事件循环
等待某些IO任务await + 可等待对象
同步单线程,红线后等待2秒协程对象1执行完成后,才会执行协程对象2事件循环中放入的是一个普通的协程对象,不是task对象
可以通过await执行其他协程函数await本质就是等待
await探究
await关键字
如果await后面是协程对象,则会同步切换
普通型(代码重复)
done内容
set( )
pending内容
task放入列表
task对象
asyncio.run中封装了loop和creat_task
比如像这样,就是先创建task对象,再run运行(自动创建事件循环)其实没必要,run方法会自动创建task对象列表里放入func()就没问题了
创建task对象时,要保证事件循环已经存在
如果创建一个全局列表1.列表中放入协程函数对象,2.再用asyncio.wait()将列表转为可等待对象3.然后再用run
事件循环/task对象
示例
task是继承future的在future对象中会保存当前执行的这个协程任务的状态如果当前任务状态为finished,则await不再等待
Future对象(比较底层不太用得到)
>>当前方式会自动创建一个线程池去执行普通函数: test协程函数中可以运行一个线程池,但这个池必须由事件循环处理
如果协程函数中自己手动创建了一个线程池,则必须用run_in_executor调用
自建线程池
同上,但如果要创建进程池,一定要用if __name__ == '__main__':
自建进程池
run_in_executor
有些库不支持协程,这时就可以用线程\\进程池
交叉编程(一般不这么用)
# 迭代器执行的代码一般是IO耗时操作
异步迭代器(一般不这么用)
异步上下文管理器(更用不到)
uvloop(了解一下)
异步编程
协程
并发编程
创建数据库,相当于文件夹
在数据库中创建表,相当于文件中创建文件
数据表必须有表名
表中有字段
数据有类型
一些字段可能存在约束
表中可以存储相关数据
表与表之间有关系
关系型数据库
int
-128~127
tinyint
整型
交易,金额
decimal(精度高)别名:货币类型
float
小数
创建字符串要规定长度
创建时候设定30,存hello,占用的是5
不固定长度
varchar(常用)
创建时候设定30,存hello,占用的也是30
固定长度
char
长文本
text(超过255字符)
date
time
datetime
日期时间
gender性别:男/女
选择,选项
enum
枚举类型
不为空,不重复
primary key
主键
not null
非空
不重复
unique
唯一
存数据的时候没写这个字段,系统会填入建表时设置的默认值
default
默认
foreign key
外键
无符号(不能为负数)
auto_increment
自动增长
约束
--这是一个注释
/*多行*/
mysql -uroot -p
连接
show databases;
查看
use 数据库名;
进入数据库
select database();
查看当前数据库
create database 数据库名 charset=utf8;
show create database 数据库名;
查看数据库创建过程
drop database 数据库名;
删除库
\\q,exit
退出mysql
库的基本操作
show tables;
查看表
创建表
desc 表名;
查看表结构
alter table 表名 add 列名 类型;
表添加新列
添加字段
alter table 表名 change 字段原名 字段新名 类型 [约束];
alter table 表名 modify 原字段名 类型 [约束];
不重命名
修改字段
alter table 表名 drop 字段名;
删除字段
show create table 表名;
查看表的创建过程
drop table 表名;
删除表
表的基本操作
select * from 表名;
select 字段,字段 from 表名;
select s.name from students as s;
表
字段(只在查询结果中)
设别名
只是字段去重查询
select distinct 字段名 from 表名;
去重
select * from 表名 where 条件;
enum类型相当于一个列表,条件查询的时候尽量用下标sql语言里尽量不要用中文
=><>=<=!= or <>
比较运算
andornot(select * from 表名 where not 条件;)
逻辑运算
%,任意多个字符_,任一字符
like
模糊查询
select * from 表名 where 字段 in (选项);
in(非连续)
select * from 表名 where 字段 between 下限 and 上限;(包括上下限)
between(连续)
范围查询
null和‘ ’不一样
is null
is not null
判断是否为空
where
条件
前面都一样order by 要排序的列名或列index(从1开始计数)
asc升序order by默认升序
desc降序
列1 asc,列2 desc;
order by
总行数
count(*)
字段的最大值
max(字段)
字段的最小值
min(字段)
求和
sum(字段)
平均值
avg(字段)
聚合函数
对字段1分组,并显示对应组里的字段2
select 字段1,group_concat(字段2) as 字段2 from 表名 group by 字段1
+group_concat
+聚合函数
having一定要在group by后面
+having
+ with rollup(很少用)
group by
分组
查第5-10这六条数据
limit要在最后,不能写公式进去要写具体值
分页
2表一一对应,不显示对应不上的
inner join on
主表中的数据对应不上子表数据,则显示null子表中数据对应不上主表的不显示
主表 left join on 子表
left(right)join on
join on
连接查询
aid是自身id,pid是所属id通过where后面的条件找到所属上级,通过join on后面的条件找到结果
自己 inner join on 自己可以同张表as不同的名字
就是表内数据间有关联
自关联
selec结果相当于一个临时表,嵌套就是在临时表中查询
嵌套查询(巨慢,别用,会被认为是外行)
子查询和外键查询,如果是公司的数据库,不要用,数据量稍微大点,CPU就100%
子查询(可替代)
insert into 表名(字段名1,字段名2.....不写也行,写就要写全)values(数据1,数据2....)如果有默认值,也要写value
全字段
insert into 表名(字段名1,字段名2)values(数据1,数据2)
部分字段
insert into 表名 values(....),(......);
全字段插入
多行插入
update 表名 set 字段1=值1,字段2=值2.... where 条件;
delete from 表名 where 条件;
is_delete=0 False,在页面中显示数据is_delete=1 True,隐藏数据,数据在库里,但看不懂
表中数据的基本操作
create view 视图名 as select语句
drop view 视图名
相当于把语句分装到函数里,语句结果建立一个临时表调用时当表去select命名v_开头
视图
事务不可分割,全成功or全失败
原子性
事务将数据库从一种一致性状态转换到另一种一致性状态。一致性状态:1.系统的状态满足数据的完整性约束2.系统的状态反应数据库本应描述的现实世界真实状态,比如转账前后2个账户的金额总和应保持不变( 所有操作全部成功之后也不会立即生效,要提交)
一致性
并发执行的事物不会相互影响,其对数据库的影响和它们串联执行时一样。(一个事务所做的执行,在提交前对其他事务不可见)比如多个用户同时往一个账户转账,最后账户的结果应该和它们按先后次序转账的结果一样。
隔离性
一旦提交,数据永久保存
持久性
特性ACID
修改数据的命令会自动触发事务,insert update delete
开启begin;orstart transaction;
头
报错的语句不执行,其他都提交
提交commit;
报错回滚报错那条语句,这好像是废话
回滚取消之前一切操作
回滚rollback;
尾
操作(有头有尾不出错)
commit;
begin;insert into classes(name) values('python_04期');select * from classes;查询发现有新数据
终端A
A提交后,才能查询到新数据
查询不到A新插入的数据
select * from classes;
终端B
开两个终端
表引擎必须是innodb类型才支持事务
事务
MySQL(存在磁盘)
数据与数据之间有关联
支持事务
适合数据结构复杂的数据
数据相对安全
存在磁盘里,不删会一直在
数据存储持久化
不支持sql语言,每种数据库有单独的语言
数据与数据之间无关联
不支持事务
适合结构复杂的数据
数据不太安全
页面缓存,验证码
如Redis,存在内存里,断电就没了
非关系型数据库
NoSQL VS SQL
简介:特性优势应用场景
redis-cli,连接Redis服务器
ping,返回pong,说明连接正常
select 10,选择10号数据库
16个默认数据库(0-15)
默认存二进制
redis数据库
set key value
过期时间为秒
setex key seconds value
多个键值对
mset key1 value1 key2 value2 .....
获取key对应的value
get key
获取多个键的values
mget key1 key2...
字符串string
查看所有key
keys *
查看m开头的所有key
keys m*
查看这个key是否存在(0不存在/1存在)
exists key
默认str
查看value的数据类型
type key
以key删数据
del key1 key2...
键命令
hset key field value
只能在同一个大key下设置多个键值对
hmset key field1 value1 field2 value2
查key对应的field
hkeys key
查对应的value
hget key field
hmget key field1 field2...
查整个key的所有values
hvals key
hdel key field1 field2...
del key
hash用于存储对象,对象结构为属性、值,值类型为string
redis中的hash数据
哈希hash
left插入
lpush key value1 value2....
right插入
rpush key value1 value2....
-1是最后一位
只有左取值
lrange key start stop
count>0 从左往右删count<0 从右往左删count=0 删除所有
lrem key count(要删除的个数) value
redis中的列表按照插入顺序排序先入后出
列表list
sadd key member1 member2...
smembers key
删指定元素
srem key member
元素都是string,元素唯一不重复
无序集合set
zadd key socre1 member1 socre2 member2....
zrange key start stop
zrem key member2 member2...
有序集合zset
数据操作
Redis(存在内存)
需要自己创建数据库
没有表的概念,自己创建集合(相当于MySQL的表)
简介
mongo
终端进入
db
show dbs
查看数据库s
如果数据库中无数据,且到别的数据库后该数据库会消失
use
切换or新建数据库
v 4.0用小写d
db.dropDatabase()
删除当前数据库
version()
看当前数据库版本
数据库
db.createCollection('数据库名')
一般不用
创建限定大小的集合
创建数据库
show collections
查看集合s
drop v 4.0/Drop v 3.0
db.集合名.drop()
16进制数字,类似主键
文档ID
Object ID
String
true,false
布尔
integer
浮点值
double
数据或列表,多个值存到一个键
arrays
理解为对象
Object
Null
时间戳
Timestamp
日期
Date
_id不可以重复
db.集合名.insert(font color=\"#ff0000\
插入数据
db.集合名.remove({字段},{justOne: true})如果字段有重复,加justOne: true
db.集合名.save(id重复也可以保存)
用font color=\"#ff0000\
db.集合名.font color=\"#ff0000\
自动pretty()
findOne查询一个
find().pretty()对数据格式化输出
db.集合名.find({可添加搜索条件})查询所有
等于 :
小于 $lt
小于等于$lte
db.stu_info.find({age:{$gt: 18}})
大于 $gt
大于等于$gte
不等于 $ne
比较运算符
font color=\"#ff0000\
范围
,
{$or: [{条件一}, {条件2}]}
$or
45岁的或者hometown在桃花岛或者华山的
整合应用
逻辑运算符
只能用/ /
{字段: /^abc/}
用$regex,后面可‘ ’可/ /
{字段: {$regex: '^abc'}}
以abc开头
789$
以789结尾
正则
查前3条数据
find().limit(3)
跳过前3条数据
find().skip(3)
find().skip(2).limit(2)
翻页
js语法
自定义查询
find({条件,查*就空着},{需要显示的字段:1,_id:(【默认】显示:1,不显示:0)})0只针对_id
投影
find().sort({字段:正序1,倒序-1,条件2})
count({条件})或者find({条件}).count()
find().count()
统计
distinct(‘字段’,{条件})
统计男女各多少条
统计男女各多少条数据+男女各自的平均年龄
不加分组条件(统计所有)
$group
查询年龄大于20的
过滤(按条件筛选)
$match
修改输⼊⽂档的结构,如重命名、增加、删除字段、创建计算结果
记得把_id去掉
$project
逻辑很简单,注意各种标点就行
联合使用
普通排序
查询结果排序
$sort
和find的翻页用法差不多,加个$而已
输出指定条数
$limit
跳过指定条数
$skip
将数组类型的字段进行拆分
$unwind
常用管道
计算总和,$sum: 1 表示一倍计算
$sum
$avg
最小值
$min
最大值
$max
在结果文档中插入
$push
获取第一条数据
$first
获取最后一条数据
$last
表达式
db.集合名称.aggregate({管道:{表达式}})
聚合操作
查集合里的数据
查(主要操作都在这)
索引优化(一般情况下MongoDB不设置索引)
数据
命令
MongoDB(存在磁盘)
纯数据库
异常
cursor()游标对象,负责执行SQL语句
connect函数
类型
1.利用pymysql连接数据库
2.创建表
3.插入数据(更新表/删除表也一样,就是sql不一样)
插入数据,使用execute()的参数
pymysql
MySQL(3306)
1.创建对象
2.添加字符串
get_result = redis_obj.get('name')print(get_result.decode())
3.获取数据
4.修改数据(重新set)
del_result = redis_obj.delete('姓名')
5.删除key
6.获取key
string
太长了,点击图片查看
hash
from redis import Redis
Redis(6379)
client['数据库']['集合'],不存在对应数据库或者集合,会自动创建
创建连接
放字典
单条
推导式,注意for 放哪里
多条
find_one( )find( )
查询
update_one()
一条
name为test0-test9,添加age字段,值为0-9其实这条语句用update_one就行
修改所有name为test1的数据
update_many()
更新
delete_one()
注意值的数据类型
delete_many()
from pymongo import MongoClient
练习
MongoDB(27017)
python
<!DOCTYPE html> <head> <title></title> <style></style> <script> </script> </head> <body> </body></html>一切靠开头+Tab补全
基础结构
file:///E:/zoom/1palegechong/1%E5%9B%BE%E7%81%B5/6.%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80/%E5%89%8D%E7%AB%AFvip%E8%AF%BE%E4%BB%B6/html%E6%A0%87%E7%AD%BE.md
点开慢慢找
标签
file:///E:/zoom/1palegechong/1%E5%9B%BE%E7%81%B5/6.%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80/%E5%89%8D%E7%AB%AFvip%E8%AF%BE%E4%BB%B6/html%E5%B1%9E%E6%80%A7.md
用相对路径,绝对路径换机器就找不到文件了
资源路径
现在都用ul无序列表,少有用ol有序列表的
列表标签(双)
<table>标签:表示一个表格 <tr>标签:表示表格中的一行 <th>标签:表示表格中的表头 <td>标签:表示表格中的列
表格标签(双)
action属性 设置表单数据提交地址
提交
name属性 设置表单元素的名称,该名称是提交数据时的参数名
value属性 设置表单元素的值,该值是提交数据时参数名所对应的值
**<form>**标签 表示表单标签,定义整体的表单区域
**<label>**标签 表示表单元素的文字标注标签,定义文字标注
type属性type=\"text\" 定义单行文本输入框type=\"password\" 定义密码输入框type=\"radio\" 定义单选框type=\"checkbox\" 定义复选框type=\"file\" 定义上传文件type=\"submit\" 定义提交按钮type=\"reset\" 定义重置按钮type=\"button\" 定义一个普通按钮
**<input>**标签 表示表单元素的用户输入标签,定义不同类型的用户输入数据方式
**<textarea>**标签 表示表单元素的多行文本输入框标签 定义多行文本输入框
**<option>标签与<select>**标签配合,定义下拉列表中的选项
**<select>**标签 表示表单元素的下拉列表标签 定义下拉列表
表单标签(双)
HTML
<div style=\"width:100px; height:100px; background:red \">hello</div>
行内(优先级1)
<head> <style type=\"text/css\"> h3{ color:red; } </style></head>
内嵌(优先级2)
<link rel=\"stylesheet\" type=\"text/css\" href=\"css/main.css\">
外联(优先级3)
引入方式
<style type=\"text/css\"> p{ color: red; }</style>
以标签开头
<style type=\"text/css\"> .blue{color:blue} .big{font-size:20px} .box{width:100px;height:100px;background:gold} </style><div class=\"blue\">这是一个div</div><h3 class=\"blue big box\">这是一个标题</h3><p class=\"blue box\">这是一个段落</p>
以 . 开头
E 的所有 F 后代
E F{ }
E 的所有 子 F
E>F{ }
G和F同父,F紧接在G之后
G+F{ }
G和F同父,F在G后面
G~F{ }
层级
#id名{ }
id
.box1font color=\"#ff0000\
,分开
组
a:link{ }未访问过的链接
a:visited{ }访问过的链接
property:active{ }点击后的效果
property:hover{ }鼠标放置的效果
div p:nth-child(n) 第n个p标签
伪类
选择器
布局常用属性
文本常用属性
子元素(标签)的尺寸超过父元素(标签)的尺寸,设置overflow属性
元素溢出
none 元素隐藏且不占位置inline 元素以行内元素显示block 元素以块元素显示
display
显示特性
属性(属性名:属性值)
盒子模型
CSS
<input type=\"button\" name=\"\" onclick=\"alert('ok!');\">
<script type=\"text/javascript\"> alert('ok!');</script>
<script type=\"text/javascript\" src=\"js/index.js\"></script>
javaScript 是一种弱类型语言,也就是说不需要指定变量的类型,JavaScript的变量类型由它的值来决定
var 变量名 = 变量值;
变量
// 单行注释
/* 多行注释 多行注释*/
1、number 数字类型
2、string 字符串类型
3、boolean 布尔类型 true 或 false
4、undefined undefined类型,变量声明未初始化,它的值就是undefined
5、null
6、object 数组、函数和JavaScript对象都属于复合类型
1、区分大小写 2、第一个字符必须是字母、下划线(_)或者美元符号($) 3、其他字符可以是字母、下划线、美元符或数字
变量命名规范
对象o Object 比如:oDiv 数组a Array 比如:aItems 字符串s String 比如:sUserName 整数i Integer 比如:iItemCount 布尔值b Boolean 比如:bIsComplete 浮点数f Float 比如:fPrice 函数fn Function 比如:fnHandler
匈牙利命名风格
变量和数据类型
<script type=\"text/javascript\"> // 函数定义 function fnAlert(){ alert('hello!'); }</script>
function
调用:函数名()
1、返回函数中的值 2、执行完return函数执行结束
return
返回值
局部
全局
变量作用域
if...if...elseif...else if...else
switch case
例如 x=5
条件语句
document.getElementById( )
获取标签元素
设置标签元素
事件=‘函数名()’
onMouseOver onMouseOut
加载页面时执行对应函数
onLoad in body
onLoad in element
获得焦点
onFocus
失去焦点
onBlur
失去焦点和变值
onChange
选中
onselect
onSubmit
事件
获取数组的长度
根据下标取值
从数组最后添加和删除数据
start:必需,开始删除的索引。num:可选,删除数组元素的个数。elementN:可选,在start索引位置要插入的新元素。
根据下标添加和删除元素
数组
条件不成立时,do也会执行一次
do-while
循环
字符串拼接
以指定的时间间隔(以毫秒计)调用一次函数的定时器
以指定的时间间隔(以毫秒计)重复调用一个函数的定时器
clearTimeout(计时器对象)
clearInterval(计时器对象)
清除
定时器
即JS的基本语法,JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关。
ECMAScript标准
DOM
BOM
JS组成
docment常见属性对象
navigator.webdriver是否是模拟浏览器
window对象的navigator属性
Window对象的Location属性
frames 属性返回窗口中所有命名的框架
Window frames 属性
window history属性
window.screen 对象包含有关用户屏幕的信息。window.screen对象在编写时可以不使用 window 这个前缀。一些属性:1. screen.availWidth - 可用的屏幕宽度2. screen.availHeight - 可用的屏幕高度
Window Screen属性
window对象
<script> var person = new Object(); // 添加属性: person.name = 'tom'; person.age = '25'; // 添加方法: person.sayName = function(){ alert(this.name); } // 调用属性和方法: alert(person.age); person.sayName();</script>
object类创建
字面量创建
JavaScript对象
JavaScript
<script src=\"js/jquery-1.12.4.min.js\"></script>
如果下载了,可以把文件放在本地,用相对路径引用
<script src=\"https://code.jquery.com/jquery-3.1.1.min.js\"></script>
或者去搜jQuery在线地址
一定要先开一个<script></script>把jQuery先引入
引入
// 完整写法 $(document).ready(function(){ ... });
// 简化写法 $(function(){ ... });
jQuery入口函数
$('#myId') //选择id为myId的标签$('.myClass') // 选择class为myClass的标签$('li') //选择所有的li标签$('#ul1 li span') //选择id为ul1标签下的所有li标签下的span标签$('input[name=first]') // 选择name属性等于first的input标签<div id=\"div1\" class='myClass'>这是一个div元素</div><input type=\"text\" name=\"first\" id=\"input1\" value=\"20px\"><a href=\"#\" id=\"myId\" class=\"sty01\">这是一个链接</a>
标签选择器类选择器id选择器层级选择器属性选择器
jQuery选择器
修改第一个div
has(选择器名称)
修改第2个div
eq(索引)
选择集过滤
<script> $(function(){ var $div = $('#div01'); $div.prev().css({'color':'red'}); $div.prevAll().css({'text-indent':50}); $div.next().css({'color':'blue'}); $div.nextAll().css({'text-indent':80}); $div.siblings().css({'text-decoration':'underline'}) $div.parent().css({'background':'gray'}); $div.children().css({'color':'red'}); $div.find('.sp02').css({'font-size':30}); }); </script>
$('#box').prev(); 表示选择id是box元素的上一个的同级元素$('#box').prevAll(); 表示选择id是box元素的上面所有的同级元素$('#box').next(); 表示选择id是box元素的下一个的同级元素$('#box').nextAll(); 表示选择id是box元素的下面所有的同级元素$('#box').parent(); 表示选择id是box元素的父元素$('#box').children(); 表示选择id是box元素的所有子元素$('#box').siblings(); 表示选择id是box元素的其它同级元素$('#box').find('.myClass'); 表示选择id是box元素的class等于myClass的元素
以选择的标签为参照,获取转移后的标签
选择集转移
获取标签后.html()
html(修改的内容直接写在括号里)
添加.append()
html()
获取和设置元素内容
$获取.prop(‘属性名’)
$获取.val(),获取value简写方式
prop()
获取和设置元素属性
$获取 . 事件(事件函数);
click() 鼠标单击blur() 元素失去焦点focus() 元素获得焦点mouseover() 鼠标进入(进入子元素也触发)mouseout() 鼠标离开(离开子元素也触发)ready() DOM加载完成
jQuery事件
点击子元素,点击事件会向它父级元素传递,也会触发父元素的点击事件
事件会向它的父级一级一级传递
事件冒泡
$(function(){ $ali = $('#list li'); $ali.click(function() { $(this).css({background:'red'}); });})<ul id=\"list\"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li></ul>
一次性绑定所有子元素
一般事件绑定
$父元素 . delegate(子元素,事件名,执行函数)
事件绑定在父元素上,执行对应子元素才会临时绑定一次事件
事件代理
jQuery
{ \"name\":\"tom\
对象格式
[\"tom\
数组格式
JavaScript Object Notation(javascript对象表示法)json本质上是一个字符串
JSON中key一定是“”区别字典和json,就是引号的单双
JSON.parse(JSON对象)
json转js对象
ajax
前端
如搜索引擎
通用爬虫
针对特定的网站
聚焦爬虫
爬虫分类
1.获取到资源地址
2.发送请求获取数据
3.提取信息
4.保存数据
爬虫流程
列子:https://www.baidu.com/item/10056474?fr=aladdin http://IP:port/资源路径/?wd=python#flg 协议 :这代表网页使用的请求协议域名部分:该URL的域名部分为“www.baidu.com”。一个URL中,也可以使用IP地址作为域名使用:202.108.22.5端口部分:跟在域名后面的是端口,域名和端口之间使用“:”作为分隔符。端口不是一个URL必须的部分,如果省略端口部分。资源部分:从域名后的最后一个“/”开始到“?”为止,是资源部分查询参数:从“?”开始到“#”为止之间的部分为参数部分,又称搜索部分、查询部分
协议-域名-(端口)-资源-查询参数
http://IP:port/资源路径/?wd=python#flg
1.URL
hypertext,html就是超文本,网页上看到的图片,结构,标签等等我们在浏览器里看到的网页就是超文本解析而成的
2.超文本
超文本传输协议,默认端口80http+ssl(安全套接字),默认端口443
3.http和https
图解
1.浏览器向地址栏中的URL发起请求,并获取相应
2.在返回的响应内容(html)中,会带有css、js、图片等url地址,以及ajax代码,浏览器按照响应内容中的顺序依次发送其他的请求,并获取相应的响应
3.浏览器每获取一个响应就对展示出的结果进行添加(加载),js,css等内容会修改页面的内容,js也可以重新发送请求,获取响应
4.从获取第一个响应并在浏览器中展示,直到最终获取全部响应,并在展示的结果中添加内容或修改————这个过程叫做浏览器的渲染
注:但是在爬虫中,爬虫只会请求url地址,对应的拿到url地址对应的响应(该响应的内容可以是html,css,js,图片等)浏览器渲染出来的页面和爬虫请求的页面很多时候并不一样所以在爬虫中,需要以url地址对应的响应为准来进行数据的提取
过程
4.http请求过程
http基本原理
f12面板
请求方法(Request Method)请求的网址 (Request URL)请求头(Request Headers)请求体(Request Body)。
一般常用的就get和post两个
1. 请求方法Request Method
,即统一资源定位符 URL,它可以唯一确定我们想请求的资源。
2.请求网址Request URL
3.请求头Request headers
POST 请求中的表单数据,而对于 GET 请求,请求体则为空
4. 请求体(在Payload里查看)
响应状态码(Response Status Code)响应头 (Response Headers)响应体(Response Body)
常见状态码
1.响应状态码Status Code
Date:标识响应产生的时间。Last-Modified:指定资源的最后修改时间。Content-Encoding:指定响应内容的编码。Server:包含服务器的信息,比如名称、版本号等。Content-Type:文档类型,指定返回的数据类型是什么,如 text/html 代表返回 HTML 文档,application/x-javascript:代表返回 JavaScript 文件,image/jpeg 则代表返回图片。Set-Cookie:设置 Cookies。响应头中的 Set-Cookie 告诉浏览器需要将此内容放在 Cookies 中,下次请求携带 Cookies 请求。Expires:指定响应的过期时间,可以使代理服务器或浏览器将加载的内容更新到缓存中。如果再次访问时,就可以直接从缓存中加载,降低服务器负载,缩短加载时间。
常用响应头
2. 响应头Response Headers
获取到的数据
3. 响应体Response、(图片在Preview里看)
5. 响应
http请求
这玩意多用久熟了,尽量看英文的
浏览器开发者工具 F12
有始有终的一系列动作 / 消息
1.会话Session
某些网站为了辨别用户身份、进行会话跟踪而存储在用户本地终端上的数据
2.cookies
3.cookies列表
会话与cookies
面向对象写法
直接for循环写法
获取图片
socket套接字
基础知识
发get请求用content接收content.decode()解码
解码类型:requests模块自动根据http头部响应的编码做推测在接收请求时用encoding=‘ ’修改
响应体str类型
.text
响应体bytes类型
.content
响应体json类型
.json()
响应状态码
.status_code
请求头
.request.headers
响应头
.headers
响应的cookie
.cookies
常用属性
headers是字典
带header的请求
也是字典一般会有很多参数,删掉没用的,一个个试
params=参数
带参数
post需要发送data或者json给服务器在Payload里找,也是字典
form data的数据是data=,request payload是json=,query string params是params=
POST
反向代理:浏览器不知道服务器真实地址,例如Nginx正向代理:浏览器知道服务器真实地址,例如VPN
透明代理(Transparent Proxy):透明代理虽然可以直接“隐藏”你的IP地址,但是还是可以查到你是谁。匿名代理(Anonymous Proxy):使用匿名代理,别人只能知道你用了代理,无法知道你是谁。高匿代理(Elite proxy或High Anonymity Proxy):高匿代理让别人根本无法发现你是在用代理,所以是最好的选择。
从请求使用的协议可以分为:http代理https代理socket代理等
如果返回代理ip,则可用
检测代理ip是否可用
代理池要更新,付费代理很多时候也有超过10%的用不了
代理
注意:cookie有过期时间可以用专门程序获取cookie,但有的网站获取时间较长,所以还是直接复制简单
在request headers里找到cookies,然后加在header里cookie的内容都是键值对,去键和值就行
1.在headers里面
以键值对的方式,传参给cookies=
2.传参
session实例在请求了一个网站后,对方服务器设置在本地的cookie会保存在session中,下一次再使用session请求对方服务器的时候,会带上前一次的cookie
3.使用session处理
response.cookies是CookieJar类型使用requests.utils.dict_from_cookiejar,能够实现把cookiejar对象转化为字典
在前面的requests的session类中,我们不需要处理cookie的任何细节,如果有需要,我们可以使用这个
4.requests中cookirJar的处理方法
cookies
ssl.CertificateError ...
import requestsurl = \"https://www.12306.cn/mormhweb/\"response = requests.get(url)
import requestsurl = \"https://www.12306.cn/mormhweb/\
这是加个参数,忽略证书
ssl的证书不安全导致
您的连接不是私密连接
处理证书错误
参数timeout比如检测代理ip,总不能一直等在那
超时
@retry(stop_max_attempt_number=3)重试3次再报错
一个装饰器
retrying
request
发送请求
结构化数据:json,xml等
处理方式:正则表达式、xpath、bs4
非结构化数据:HTML,字符串
爬虫中的数据分类
查看语法-文件操作-json
JSON数据
匹配不到会报错,所以用的少
match方法
匹配单个字符
match(从头找一个)
search(找一个)
返回列表,没有返回空列表
findall(找所有)
re.sub(\"\\d\
sub(替换)
p = re.compile(\"\\d\
compile(编译)
常用方法
{m,n}中间不能加空格
匹配多个
^开头字符结尾字符$
不以【】里的的字符开头的
[^字符字符字符]
开头结尾
\\\\1:\\转译 \\1引用(1)匹配的结果,即html
\um,引用分组(第num组),匹配到的结果match_obj = re.match(\"<([a-zA-Z1-6]+)>.*</\\\\1>\
起名span style=\
match_obj = re.match(\"<(?P<name1>[a-zA-Z1-6]+)><(?P<name2>[a-zA-Z1-6]+)>.*</(?P=name2)></(?P=name1)>\
分组匹配
例如\"\\"的原始字符串就是\"\\\\",或者r‘\’
不转译(比如Windows中的路径)
python中原始字符串r的用法
大部分中文
[\\u4e00-\\u9fa5]
匹配中文
贪婪:最大长度匹配非贪婪:最小长度匹配
非贪婪:在量词后面加?
贪婪非贪婪
基础版
异步版
爬取前十页列表中的小说名
线程池异步爬取猫眼电影图片
正则(最快)
快速的定位特定元素以及获取节点信息
选中该元素。
nodename
从根节点选取、或者是元素和元素间的过渡。
从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
//
选取当前节点。
.
选取当前节点的父节点。
..
//li/span[@class='s2']
选取属性。
@
选取文本。
text()
语法
所有的电影的名称//span[@class='title'][1],href,//div[@class='hd']/a/@href评分,//span[@class='rating_num']评价人数//div[@class='star']/span[4]
top250
xpath
获取响应字符串转element再转字符串
lxml能够把确实的标签补充完成,但是请注意html是人写的,很多时候由于网页不够规范,或者是html的bug,即使参考url地址对应的响应去提取数据,任然获取不到,这个时候我们需要使用etree.tostring的方法,观察etree到底把html转化成了什么样子,即根据转化后的html字符串去进行数据的提取。
from lxml import etree
获取到的数据为列表
返回的是element对象
html = etree.HTML(text) ret_list = html.xpath(\"xpath字符串\")
小说名xpath://div[@class='l']//span[@class='s2']/a/text()链接xpath://div[@class='l']//span[@class='s2']/a/@href
取属性和文本
利用三元运算,如果内容为空显示None
取节点
把每个小说的详情页面链接和小说的名字组成一个字典
https://sz.lianjia.com/ershoufang/pudong/pg2/
爬取链家网里的租房信息获取【标题,位置,房屋的格局(三室一厅),关注人数,单价,总价】
在python中使用xpath
lxml
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxmll。BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。
创建beautiful soup对象对输出格式化
soup对象.find_all('标签名')
1.传字符
soup对象.find_all(re.compile(\"正则表达式\"))
2.传正则
soup对象.find_all(['标签名1','标签名2'])
3.传列表
name参数
soup对象.find_all(attrs={'class':\"blue_no_underline\"})
keyword参数
返回第一个匹配结果
find
soup.select('span')
标签选择器
soup.select('.类名')
类名中间带空格soup.select('.adsbygoogle.allinone_good')
类选择器
soup.select('#id名')
id选择器
div下的 类
soup.select('div .类名')
层级选择器
soup.select('tr[align=\"center\"]')
soup.select('a[class=\"navigationlink\"]')
属性选择器
soup.select('tr td:nth-child(3)')
select()返回列表
css选择器
get_text()
获取文本
get(‘属性的名字’)
获取属性
搜索文档树
http://ip.yqie.com/ipproxy.htm获取每个ip的代理IP地址、端口、服务器地址、是否匿名、类型、存活时间
BS4(最慢)
抓取工具排名
提取数据
with open(‘文件名’,‘操作类型’,encoding=‘utf-8’)as f:
txt文本储存
python对象转成json对象,生成字符串
json.dumps()
dict类型数据转成str,并写入json文件中
json.dump()
json字符串解码成python
json.loads()
从json文件中读取数据
json.load()
操作
JSON数据只能有一个顶级[ ]or{ },所以要循环写入多个数据,要注意数据格式
JSON standard allows only one top-level value
csv.writer(文件).writerow([写入的内容])
写入
字典写入
表格csv
创建数据表
爬取百度招聘
MySQL存储
import pymongo # 如果是云服务的数据库 用公网IP连接client = pymongo.font color=\"#ff0000\
连接数据库
collection = client['students']['stu']
指定数据库和表
插入单条
插入多条
MongoDB
数据储存
t1.setDaemon(True)
t1.join()等待子线程结束再运行下面的代码
在python3中,主线程主进程结束,子线程,子进程不会结束为了能够让主线程回收子线程,可以把子线程设置为font color=\"#ff0000\
守护线程
q = Queue(maxsize=100)
创建队列设置最大容量为100
放入数据,队列满的时候回等待
q.put(值)
不等待直接放,队列满的时候会报错
q.put_nowait(值)
放入值
取出数据,队列为空的时候会等待
q.get()
不等待直接取,队列空的时候会报错
q.get_nowait()
取出数据
put一次size+1get一次size-1
获取队列中现存数据的个数
q.qsize()
队列中维持了一个计数,计数不为0时候让主线程阻塞等待,队列计数为0的时候才会继续往后执行
q.join()
put的时候计数+1,get不会-1,get需要和task_done 一起使用才会-1
q.task_done()
from queue import Queue
队列
思路
E:\\1palegechong\\1图灵\\7.爬虫\\4.高性能爬虫
爱奇艺视频
搜狐视频
代码基本一样,但Windows电脑的数据库信息要放在类属性里且用进程专用队列
占资源,但也不是用不到
用不到
线程池
E:\\1palegechong\\1图灵\\7.爬虫\\4.高性能爬虫\\day10\\课件
了解一下
使用方式和requests基本保持一致requests使用代理是proxies,aiohttp是proxyaiohttp获取进制数据是read()
requests无法异步,使用aiohttp, 基于asyncioasync 生成一个异步函数await 声明程序挂起
异步爬虫(协程)
使用 aiohttp 来代替 requests 来执行异步的网络请求操作,使用 motor 来代替同步的 pymongo 库来操作mongo数据库使用aiomysql来代替pymysql
aiomysql异步存储
高性能爬虫
通过selenium将动态网页转为静态网页,绕过反爬
官方文档
https://selenium-python-zh.readthedocs.io/en/latest/
browser = webdriver.Chrome()browser = webdriver.Firefox()browser = webdriver.Edge()browser = webdriver.PhantomJS()browser = webdriver.Safari()
打开指定浏览器
browser.get(\"http://www.baidu.com/\")
指定加载页面
browser.maximize_window()
页面最大化
设置宽高
js_code = 'window.open(\"http://httpbin.org/ip\");'browser.execute_script(js_code)
打开新页面(通过js代码)
通过(标签属性)选择文本框元素,并设置内容
通过属性找到按钮,并操作
print(browser.page_source.encode('utf-8'))
提取页面
print(browser.get_cookies())
提取cookie
print(browser.get_screenshot_as_file('123.png'))
获取当前截屏
print(browser.current_url)
提取当前页面地址
browser.quit()
关闭浏览器
browser.close()
关闭当前页面
关闭
基本使用
prefs = {\"profile.managed_default_content_settings.images\": 2}options.add_experimental_option(\"prefs\
禁止加载图片
关闭自动关闭
options.add_argument(\"-headless\")
无头模式不打开浏览器,在后台运行
关这个
设置user agent
extension_path = r'E:\\BaiduNetdiskDownload\\Chrome插件\\iguge_2011\\igg_2.0.11.crx'options.add_extension(extension_path)
添加插件
options.add_argument(\"--proxy-server=http://58.20.184.187:9091\")
设置代理
首先申明options对象options = webdriver.ChromeOptions()然后设置options最后将options加入浏览器对象browser = webdriver.Chrome(options=options)
初始化设置(这些命令用到查文档或者百度)
通过标签name
ID选择器
xpath选择器
find_element()
列表中的每个节点都是 WebElement 类型
find_elements(),获取的是列表
查找节点
输入文字
send_keys
清空文字
clear
点击按钮
click
节点交互
不切换iFrame获取不到iFrame里面的标签
切换 IFrame(html里嵌套另一个html)
按住-拖动-放
动作链
js代码
页面滚动
标签的WebElement对象.text
获取标签中的文字
get_attribute(‘属性名’)
https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions
其他EC.的等待条件
不按时间算按加载项算
延时等待
print(browser.window_handles)
所有标签
下标1,第二个页面
browser.switch_to.window(browser.window_handles[1])
切换
选项卡
https://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions
所有异常看文档
https://bot.sannysoft.com/
加选项,设置屏蔽
options.add_argument('--disable-blink-features=AutomationControlled')
f12 console,判断是否是模拟浏览器
navigator.webdriverfalse则绕过成功true说明没成功
绕过检测
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keys # 模拟键盘操作
唯品会口红
义乌购
selenium
主界面
File
Edit
View
Charles抓包(不全)
IP池搭建(直接用付费代理)
32位
MD5
base64编码
40位
SHA-1
DES
加密 编码
爬虫
目的:不让其他的环境资源干扰到当前项目
conda env list:查看当前所有虚拟环境
最好用管理员打开cmd
最好加个指定目录conda create python=3.9 --prefix=D:\\anaconda3\\envs\\py_scrapy_1
-p/--prefix和-n/--name不能同时使用
conda create -n 环境名 python=x.x:创建虚拟环境
activate 环境名:激活虚拟环境
deactivate:退出虚拟环境
安装的环境在anaconda3\\envs里,也可以直接删除文件夹
conda remove -n 环境名 --all:删除对应环境
打开anaconda prompt(将anaconda script地址添加到环境path里,就可以用cmd操作了)
pip list
pip freeze > ./requirements.txt
导出
下载了别人的项目,导入别人的requirements
pip install -r ./requirements.txt
导入
项目环境导入导出(用到哪些库)
搭建虚拟环境
1. 调度器(scheduler)把requests对象-->引擎(引擎判断这是requests对象)-->下载中间件--->下载器
2.下载器发送请求,获取响应---->下载中间件---->引擎--->爬虫中间件--->爬虫
3.爬虫提取url地址,组装成request对象---->爬虫中间件--->引擎(引擎判断这还是requests对象)--->调度器(scheduler)
4.爬虫提取数据--->引擎(引擎判断这是items)--->管道(pipeline)
5.管道(pipeline)进行数据的处理和保存
模块于模块之间相互独立,只和引擎交互
scrapy已实现
总指挥:负责数据和信号在不同模块间的传递
Scrapy Engine引擎
一个队列,存放引擎发送过来的requests请求
Scheduler调度器
下载引擎发送过来的requests请求,并返回给引擎
Downloader下载器
需要手写
处理引擎发来的response,提取数据,提取URL,并交给引擎
Spider爬虫
处理引擎传过来的数据,比如存储
Item Pipeline管道
一般不用手写
可以自定义的下载扩展,比如设置代理,设置请求头
Downloader Middlewares下载中间件
可以自定义requests请求和进行response过滤
Spider Middlewares爬虫中间件
各个模块及作用
首先进入虚拟环境
下载后scrapy,查看信息
pip install scrapy==2.5.0
在空环境里下载scrapy
一键生成各种模块
scrapy startproject myspider
在项目目录下创建项目
*** *** 文件名 目标域名
scrapy genspider douban movie.douban.com
先cd到project目录下(cd myspider)
右击项目文件夹,pycharm单独窗口打开,在setting里导入之前创建的虚拟环境中的python解释器(interpreter)
编写douban.py
返回日志,目前是403
在settings里把 ROBOTSTXT_OBEY = False
查看豆瓣的robots.txt
加入user-agent
调试
请求成功
# scrapy封装的命令执行from scrapy import cmdline*********if __name__ == '__main__': cmdline.execute('scrapy crawl douban --nolog'.split())
在pycharm中调试
scrapy crawl douban(爬虫name)--nolog(无日志)
response对象属性
*注*
创建爬虫文件
yield item将item返回给引擎
对douban爬虫进行修改完善
修改pipelines.py文件
ITEM_PIPELINES
在settings.py设置开启pipeline
开启管道,数据会被管道取出,item里就没数据了
这里要在配置里关闭pipeline
cmdline.execute('scrapy crawl douban -o douban.json -s FEED_EXPORT_ENCODING=\"utf-8\"'.split())
自动生成json文件,用utf-8保存
保存
- CRITICAL:严重错误- ERROR:一般错误- WARNING:警告- INFO: 一般信息- DEBUG:调试信息**注意:** 默认的日志等级是DEBUG
日志等级
Versions:所使用的工具版本信息Overridden settings: 重写的配置Telnet Password:Telnet 平台密码(Scrapy附带一个内置的telnet控制台,用于检查和控制Scrapy运行过程)Enabled extensions :开启的拓展功能Enabled downloader middlewares:开启的下载器中间件Enabled spider middlewares:开启的爬虫中间件Enabled item pipelines:开启的管道Dumping Scrapy stats:所以的信息汇总
日志输出信息
LOG_LEVEL = 'WARNING' # 设置日志显示的等级为WARNINGLOG_FILE = './log.txt' # 将日志信息全部记录到log.txt文件中
修改配置文件
调整输出
*日志
1. 找到下一页的url地址2. 构造url地址的请求,传递给引擎
手动版
原方法
start_urls = ['https://movie.douban.com/top250?start={}&filter=']
重写start_requests方法(只要重写方法就行,不用考虑调用的问题)
框架自带
1. 中括号中的参数为可选参数2. callback:表示当前的url的响应交给哪个函数去处理3. meta:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延迟,请求深度等4. dont_filter:默认为False,会过滤请求的url地址,即请求过的url地址不会继续被请求,对需要重复请求的url地址可以把它设置为Ture,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动5. method:指定POST或GET请求6. headers:接收一个字典,其中不包括cookies7. cookies:接收一个字典,专门放置cookies8. body:接收一个字典,为POST的数据
总览
meta的形式:字典meta的作用:meta可以实现数据在不同的解析函数中的传递
特别注意1. meta参数是一个字典2. meta字典中有一个固定的键`proxy`,表示代理ip,关于代理ip的使用我们将在scrapy的下载中间件的学习中进行介绍3. meta的download_timeout设置请求超时
例子
meta参数的使用
*scrapy.Request的更多参数
先定义item,确定爬取的目标
Item能够做什么1. 定义item即提前规划好哪些字段需要抓取,scrapy.Field()仅仅是提前占坑,通过item.py能够让别人清楚自己的爬虫是在抓取什么,同时定义好哪些字段是需要抓取的,没有定义的字段不能使用,防止手误2. 在python大多数框架中,大多数框架都会自定义自己的数据类型(在python自带的数据结构基础上进行封装),目的是增加功能,增加自定义异常
在items.py文件中定义要提取的字段:
导入 from myspider.items import MyspiderItem
实例化,创建一个管道对象这时的item的type为<class 'myspider.items.MyspiderItem'>
将原来的item={}改成item = MyspiderItem()
修改douban.py
items.py
scrapy提供的一个终端工具,能够通过它查看scrapy中对象的属性和方法,以及测试xpath
- response.xpath():直接测试xpath规则是否正确- response.url:当前响应的url地址- response.request.url:当前响应对应的请求的url地址- response.headers:响应头- response.body:响应体,也就是html代码,默认是byte类型- response.requests.headers:当前响应的请求头
进入虚拟环境scrapy shell https://movie.douban.com/top250
*scrapy shell
https://www.jianshu.com/p/df9c0d1e9087
settings.py
比如连接数据库
比如关闭数据库
首先在settings里开启pipeline
然后修改pipeline.py
1. 不同的pipeline可以处理不同爬虫的数据,通过spider.name属性来区分2. 不同的pipeline能够对一个或多个爬虫进行不同的数据处理的操作,比如一个进行数据清洗,一个进行数据的保存3. 同一个管道类也可以处理不同爬虫的数据,通过spider.name属性来区分
pipeline管道
项目实战
1. 主要功能是在爬虫运行过程中进行一些处理,如对非200响应的重试(重新构造Request对象yield给引擎)2. 也可以对header以及cookie进行更换和处理3. 其他根据业务需求实现响应的功能
scrapy中间的作用
- 当每个request通过下载中间件时,该方法被调用。处理请求的方法- 返回None值:继续请求- 返回Response对象:不再请求,把response返回给引擎- 返回Request对象:把request对象交给调度器进行后续的请求
- 当下载器完成http请求,传递响应给引擎的时候调用。拦截响应- 返回Resposne:交给process_response来处理- 返回Request对象:交给调取器继续请求
方法汇总
middlewares.py
放入UA列表
打开下载中间件
新增UA
一样的操作,在下载中间件中添加类并加上权重
设置代理,设置延迟判断是200否
代理IP
实操
下载中间件的使用方法
- 请求时数字font color=\"#e74f4c\
权重
中间件
dont_filter实现了框架去重的功能(默认False,不去重则改为True)默认网址去重,同一个网址只请求一次,防止同样的数据返回多次
网址去重
下载器中间件配置自动化(结合selenium爬去腾讯招聘)
进阶
Crawler类是一个爬虫类,主要用来管理整个执行引擎ExecutionEngine类和爬虫类实例化
1.1 Scrapy中的Crawler对象体系
Scrapy的执行流程
scrapy
框架
头部签名验证
请求参数签名验证
cookie验证
响应数据验证
常见反爬
单行注释以 // 开头多行注释以 /* 开始,以 */ 结尾
函数作用域
var x;此时x是undefined
var可以定义全局变量和局部变量var的作用域主要和函数的定义有关
var x = 2x = 3
可重复声明 声明的变量可修改
JS引擎在预编译代码时,会优先获取所有被var声明的变量和function声明的函数,将它们放在代码的头部然后从上到下执行。且函数提升优于变量提升
变量提升
var
块作用域由 { } 包括
块作用域
可修改
不可重复声明没有变量提升
只有等到声明变量的那一行代码出现,才可以获取和使用该变量
有暂存性死区
let
只能声明一个只读的常量
这也意味着const声明时就必须初始化,不能等到之后赋值。
不可重复声明、修改
const
作用域: var声明的是全局作用域或函数作用域;而let和 const 是块作用域。 声明初始化: var和let在声明的时候可以不进行初始化;而 const 在声明的时候必须初始化。 修改与重复声明: var在可以修改和重复声明;而let只能修改,不能在同一作用域下重复声明;const 声明常量不可修改也不可重复声明。 变量提升: var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined;let和 const 不存在变量提升,即它们所声明的变量一定要在声明后使用,否则会报错。 暂存性死区: var不存在暂时性死区;let和const存在暂存性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
>>456>>123这里就涉及函数提升,函数声明会提到前面去
function 函数名(){}
变量 = function(){}
声明函数
多个自执行函数,在前面加个修饰符,常用 !
(function(参数){代码块;})();
自执行函数
定义一个全局变量,把函数体赋值给全局变量,然后就能调用这个内部函数了
var _xl;!(function () { function xl(){ console.log('hello') } _xl = xl;})();_xl()
内部函数外部调用
类型是object不论传入的参数数量与定义的参数数量是否一致,都可以看到
默认参数arguments,可以用来查看传入的参数
参数
也是再return处终止代码,下面的不会执行
JavaScript 变量生命周期在它声明时初始化。局部变量在函数执行完毕后销毁。全局变量在页面关闭后销毁。
js变量的生命周期
var car = {name:\"xialuo\
对象也是一个变量,但对象可以包含多个值(多个变量),每个值以 name:value 对呈现。
每个Object类型的实例共有的属性和方法:constructor: 保存着用于创建当前对象的函数。hasOwnProperty:用于检测给定的属性在当前对象的实例中是否存在。isPrototypeOf : 用于检查传入的对象是否是当前对象的原型propertyIsEnumerble : 用于检查给定属性能否使用for-in来枚举toLocaleString() : 返回对象的字符串表示。toString() : 返回对象的字符串表示。valueOf() : 返回对象的字符串,数值,或布尔表示。通常和toString() 返回的值相同。
person = new Object();
person.firstname=\"John\";person.lastname=\"Doe\";person.age=50;
给对象赋值
person.firstname;person['firstname']
属性的值
访问对象
this 指向调用它所在方法的对象。
var person = { firstName: \"xl\
对象方法
一组数据和功能的集合
对象
onclick那些
JSON.parse() // 用于将一个 JSON 字符串转换为 JavaScript 对象。JSON.stringify() // 用于将 JavaScript 值转换为 JSON 字符串。
json转换
JS基础
Elements
保留日志Preserve log+ 勾选每次刷新不会清除之前的请求
停用缓存Disable cache+ 勾选后不会从缓存里面拉数据,每一次都是最新的数据,方便后续JS动态调试
Network
page : 所有资源文件
filesystem: 关联本地文件
在“来源”面板中打开
编辑js文件
Ctrl+S保存,文件会变成紫色
放入替换文件夹
勾选 启用本地替换
之后每次打开F12,会加载本地的js
可以使用替换技术把解混淆的JS放进去调试 注:只可以针对原文件JS 他是一个文件地址 原理就是关系映射 用处:1、删除JS里面的debugger 2、替换混淆JS 3、替换动态变化JS文件
overrides: 可以做文件替换,比如替换JS
content script内容插件
这里有全部的浏览器环境、扣JS的时候可以使用他调试 先跑通JS再去pycharm里面补环境即可
snippets代码段
Sources
js标签是从上往下顺序单线程加载的
请求服务器 -> 加载HTML文件 -> 加载JS和CSS文件 -> 用户触发JS -> 加载了某一段JS -> 对参数加密 -> 发包 -> 解密数据 -> 接收并刷新网页数据
做逆向的的时候 重点是关注发包
从点击事件开始 发包 密码从明文变成了密文
dom断点(在加密前断点)
执行比较靠后 距离加密函数相对较近 可以根据栈快速定位**注意**:非`XHR`发送的就断不住
xhr断点(在加密后)
作用:对数据进行监听,跟值进行分析业务逻辑被执行才可以被断住
blackpoint断点调试
浏览器面板
调用栈是解析器的一种机制,可以在脚本调用多个函数时,通过这种机制,我们能够追踪到哪个函数正在执行,执行的函数体又调用了哪个函数。- 当脚本要调用一个函数时,解析器把该函数添加到栈中并且执行这个函数。- 任何被这个函数调用的函数会进一步添加到调用栈中,并且运行到它们被上个程序调用的位置。- 当函数运行结束后,解释器将它从堆栈中取出,并在主代码列表中继续执行代码。
栈,先进后出
方法栈
再debugger处右击never pause here
1.
添加条件断点 false
2.
浏览器方法
function 目标方法(){ }
方法置空:将包含debugger的方法充值为空方法
将debugger注释掉或者删除,用本地js文件替换
根据不同网站,注入的代码不同这个是在debugger还是那传参
var _constructor = constructor;Function.prototype.constructor = function(s) { if ( s== \"debugger\") { console.log(s); return null; } return _constructor(s);}
在控制台注入代码
https://gaokao.chsi.com.cn/zyk/zybk/例如次网站,打开F12就会进入debugger,无法加载网站信息
debug原理
JS调试
JS逆向
python爬虫
0 条评论
回复 删除
下一页