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