Python

基础初级

1.基本使用

  1. 输入输出

    • print()打印到屏幕

    • 输入函数:输入的数会是字符串格式,口号中双引号引起来可以添加提示信息。

      • input()

      • raw_input()

  2. 进入和退出python命令窗口中

    1. 进入:python

    2. 退出函数:exit()

  3. 执行python文件

    1. 想要执行命令的话想要找到文件然后用(python)文件和后缀名

    2. 如:a.py

  4. 同一行显示多条语句

    1. Python可以在同一行中使用多条语句,语句之间使用分号(;)分割,以下是一个简单的实例。

    2. import sys; x = 'runoob'; sys.stdout.write(x + '\n')

  5. 帮助信息

    1. 很多程序可以执行一些操作来查看一些基本信息,Python可以使用-h参数查看各参数帮助信息:

      1. $ python -h

      2. 我们在使用脚本形式执行 Python 时,可以接收命令行输入的参数,具体使用可以参照 Python 3 命令行参数。

      3. python的第一行命令 //# -- coding:utf-8 -- 设置为utf-8

  6. 函数

    1. callable()判断对象是否可以被调用,能被调用的对象就是一个callables对象,比如函数

    2. ascii()调用对象的repr()方法,获得该方法的返回值.ascii() ,函数会将所有非 ascii 字符替换为转义字符:

    3. dir()不带参数时返回当前范围内的变量,方法和定义的类型列表,带参数时返回参数的属性,方法列表。

    4. classmethod()用来指定一个方法为类的方法,由类直接调用执行,只有一个cls参数,执行类的方法时,自动将调用该方法的类赋值给cls.没有此参数指定的类的方法为实例方法。

    5. delattr()删除对象的属性

    6. filter()  过滤器,构造一个序列,等价于[ item for item in iterables if function(item)],在函数中设定过滤条件,逐一循环迭代器中的元素,将返回值为True时的元素留下,形成一个filter类型数据。

      filter(function, iterable)
      参数function:返回值为TrueFalse的函数,可以为None
      参数iterable:序列或可迭代对象。
      def uno(x):
        return x > 10
      v=filter(uno,[1,11,2,45,7,6,13])
    7. iter(),iter(o[, sentinel])

      返回一个iterator对象。该函数对于第一个参数的解析依赖于第二个参数。

      如果没有提供第二个参数,参数o必须是一个集合对象,支持遍历功能(iter()方法)或支持序列功能(getitem()方法),

      参数为整数,从零开始。如果不支持这两种功能,将处罚TypeError异常。

      如果提供了第二个参数,参数o必须是一个可调用对象。在这种情况下创建一个iterator对象,每次调用iterator的next()方法来无

      参数的调用o,如果返回值等于参数sentinel,触发StopIteration异常,否则将返回该值。

    8. 三元运算(三目运算)

      v = 成立  if 条件 else 不成立
    9. 函数的模块导入

      #python的第一行是 #!/usr/bin/env python
      # -*- coding:utf-8 -*-
      input()    #输入函数
      print()    #输出函数
      type()     #查看数据类型
      while()   #循环
      pass    #不做任何操作
      break    #终止循环
      continue    #回到循环位置
         
      #编译器:是一步完成在做下一步,如c/c++/c#/java/go。
      #解释器:是一行完成在做下一行,如python/php/ruby。
    10. 深浅拷贝

         import copy     # ying puo 进口
         v = copy.copy(n) # kao bei拷贝浅只拷贝可变的
         v = copy.deepcopy(n) # di puo kao bei深拷贝只有在嵌套的时候有用

2.运算符呀

  1. 关系运算符,指定表达式之间的关系,有6种运算关系。

      作用 示例 结果
    < 小于 6<5 false
    > 大于 6>5 true
    <= 小于等于 6<=5 false
    >= 大于等于 6>=5 false
    != 不等于 6!=5 true
    == 恒等于 6==5 false
    • 优先级:同级从左往右。

  2. 算数运算符,对数值型数据进行计算,有5种。

    符号 作用 示例 结果
    + 加法 6+5 11
    - 减法 6-5 1
    * 乘法 6*5 30
    / 实数除法 6/5 1.2
    // 整数除法 6//5 1
    % 求余 6%5 1
    ** 乘方 6**5 7776
    • 优先级:乘方、乘除、求余、加减

  3. 逻辑运算符,判断真假,有3种。

    符号 作用 示例 结果
    and 与关系 2>5 and 5>4 true
    or 或关系 2>5 or 5>4 true
    not 非关系 2>5 and not 5>4 false
    • 优先级:或、与、非

  4. 赋值运算符,有6种

    符号 作用 示例 结果
    = 等于 a = 5 5
    += 加等于 a +=2 7
    -= 减等于 a -= 2 3
    *= 乘等于 a *=2 10
    /= 除等于 a /=2 2.5
    %= 求余等于 a %= 2 1
  5. 位运算符,按二进制位来计算的,4种

    符号 作用 示例 结果 说明
    & 位与运算 12 & 9 8 按位全一出一
    | 位或运算 12 | 9 13 按位见一出一
    ^ 位异运算 12 ^ 9 5 按位异出一
    ~ 反数加 ~ 12 -13 正负数调转加位,最后一位加1
    << 尾加零 3<< 2 12 前面数转二进制后面数尾部加几个零
    >> 头加零 3>>2 0 前面数转二进制后面数头部加几个零
  6. 成员运算符,属于和不属于,两种

    符号 作用 示例 结果 说明
    in 属于 “1” in "1234" true 1属于1234
    not in 不属于 “1” not in "1234" false 1不属于1234
  7. 身份运算符,身份运算符用于比较两个对象的存储单元

    符号 作用 示例 结果
    is 有相同标识 "a" is "a" true
    is not 没有相同标识 "a" is not "a" true
  8. Python运算符优先级,以下表格列出了从最高到最低优先级的所有运算符:

    运算符 描述
    ** 指数 (最高优先级)
    ~ + - 按位翻转, 一元加号和减号 (最后两个的方法名为 +@ 和 -@)
    * / % // 乘,除,取模和取整除
    + - 加法减法
    >> << 右移,左移运算符
    & 位 'AND'
    ^ | 位运算符
    <= < > >= 比较运算符
    <> == != 等于运算符
    = %= /= //= -= += *= **= 赋值运算符
    is is not 身份运算符
    in not in 成员运算符
    not and or 逻辑运算符

3.基本认识

3.1 编码

  • ascii:不包含中文,早期用于计算机。八位表示字节。

  • unicode :又称万国码,万国码32位表示一个字母(包含全球所有的语言)

  • utf-8:万国码的压缩,虽说32表示一个但不一定都占用,可以用过utf8来去除没用的位。

3.2 变量

  • 随时变化的量被称为变量。

  • 定义变量:a=1 代表a等于1

  • 变量只能有数字、下划线、字母组成,不能用数字开头,不建议用下划线定义变量。一个变量可以赋给多个变量。

  • id(变量):用来查看某个变量,在内存中的地址。

3.3 保注

  • 保留字即关键字,我们不能把它们用作任何标识符名称。Python 的标准库提供了一个 keyword 模块,可以输出当前版本的所有关键字:

    import keyword
    print(keyword.kwlist)
    #单行注释,开头写
    '''
    多行注释,开头结尾都写
    '''

3.4 缩行

  • 空行

    • 函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。类和函数入口之间也用一行空行分隔,以突出函数入口的开始。

    • 空行与代码缩进不同,空行并不是Python语法的一部分。书写时不插入空行,Python解释器运行也不会出错。但是空行的作用在于分隔两段不同功能或含义的代码,便于日后代码的维护或重构。

    • 记住:空行也是程序代码的一部分。

  • python最具特色的就是使用缩进来表示代码块,不需要使用大括号 {} 。

  • 缩进的空格数是可变的,但是同一个代码块的语句必须包含相同的缩进空格数。

  • 实例如下:

    #单行语句
    if True:
       print ("True")
    else:
       print ("False")
    #多行语句
    #Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠(\)来实现多行语句。
    total = item_one + \
           item_two + \
           item_three
  • 多个语句构成代码组

  • 缩进相同的一组语句构成一个代码块,我们称之代码组。

    • 像if、while、def和class这样的复合语句,首行以关键字开始,以冒号( : )结束,该行之后的一行或多行代码构成代码组。

    • 我们将首行及后面的代码组称为一个子句(clause)。

    • print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end="":

      • print( x, end=" " )

4.数据类型

  1. python数据类型:数字,字符串,列表,元组,字典

  2. 查看类型的命令:type()

  3. hasattr(),hasattr(object,name)判断对象object是否包含名为name的特性(hasattr是通过调用getattr(object,name))是否抛出异常来实现的。

  4. hash()哈希值hash(object)注意:可哈希的即不可变数据类型,不可哈希即可变数据类型

  5. 如果对象object为哈希表类型,返回对象object的哈希值。哈希值为整数,在字典查找中,哈希值用于快递比价字典的键。

  6. 两个数值如果相等,则哈希值也相等。

  7. help()返回对象的帮助文档,调用内建的帮助系统,如果不包含参数,交互式帮助系统将在控制台启动。如果参数为字串,则可以是模块,类,方法等名称,并且帮助页面将会在控制台打印。参数也可以 。

  8. object(),获取一个新的,无特性(geatureless)对象。Object是所有类的基类。它提供的方法将在所有的类型实例中共享。

  9. 强制转化:dict(),list(),tuple(),int(),str(),bool(),set()

4.1 数值型(Number)

  1. 数字类型:整型,布尔,浮点型,复数型

  2. 将可转为数值的函数:int(),整型:整数表示的范围是2的32次方,正负各占一半。

  3. bool (布尔), 如 True;loat (浮点数), 如 1.23、3E-2;complex (复数), 如 1 + 2j、 1.1 + 2.2j

  4. 函数

    v1 = abs(1) # 绝对值
    v2 = float(55) # 转换为浮点型(小数)
    v3 = max([1,2,3,4,5]) # 找到最大的值
    v4 = min([1,2,3,4,5]) # 找到最小的值
    v5 = sum([1,2,3,4,5]) # 求和
    v6 = divmod(1233,4) # 两数相除的商和余数
    v7 = bool(1) # 测试对象是真值还是假值,0是假值,其他都是真值
    # 进制转换
    v8 =bin(15) # 将十进制整数转换为二进制,0b代表二进制
    v9 = oct(8) # 将十进制整数转换为八进制,0o代表八进制
    v10 = hex(8) # 将十进制整数转换为十六进制,0x代表十六进制
    v11 = int("0b1101",base=2) # 将其他进制转换为十进制 参数1是需要转的字符串前面加进制代表,参数2是要转为几进制
    v12 = pow(2,3) # 几得几次方
    v13 = round(1.127,2) # 四舍五入,参数2是保留几位小数
    v14 = chr(65) # 将十进制数字转换成Unicode编码中的对应字符串。
    v15 = ord('A') # 根据字符在Unicode编码中找到其对应的十进制。
    import random
    v16 = random.randint(65,90) # 模块调用randint生成随机数字
  5. map,循环每个元素(第二个参数),然后让每个元素执行函数(第一个参数),将每个函数执行的结果保存到新的列表中,并返回。

    • 对于参数iterable中的每个元素都应用fuction函数,并将结果作为列表返回。

    • 如果有多个iterable参数,那么fuction函数必须接收多个参数,这些iterable中相同索引处的元素将并行的作为function函数的参数。

    • 如果一个iterable中元素的个数比其他少,那么将用None来扩展改iterable使元素个数一致。

    • 如果有多个iterable且function为None,map()将返回由元组组成的列表,每个元组包含所有iterable中对应索引处值。

    • 第一个参数:必须是一个函数

    • 第二个参数:(可以被for循环)

       map 随机法
       v1[11,22,33,44]
       def func(arg):
           return arg + 100
       result = map(func,v1)#然后将函数的返回值添加到[None,None]
       print(list(result))
       result = map (lambda x:x+100,v1)
         print(result)
       print(list(result))
         filter 挑刺法
         result = filter(lambda x: True if type(x)== int else False,v1)
         print(list(result))
       
         result = filter(lambda x: type(x) ==int ,v1)
         print(list(result))
         reduce 先加法
         import functools
         v1 = ["wo","hao","e"]
         def func(x+y):
             return x+y
         result = functools.reduce(func,v1)
         print(result)
       
         import functools
         v1 = ["wo","hao","e"]
         result = functools.reduce(lambda x,y:x+y,v1)
         print(result)

4.2 字符串(String)

  1. python中单引号和双引号使用完全相同。

  2. 使用三引号('''或""")可以指定一个多行字符串。

  3. 转义符 \反斜杠可以用来转义

    1. 在字符串前面添加一个 r,表示原始字符串,不会发生转义。这里的 r 指 raw,即 raw string。

    2. 使用\n代表换行,使用反斜杠()+n转义特殊字符。

    3. 使用\t代表制表符,用于空4格

    4. 使用\'和\"代表可以在字符串中加入双引号和单引号。

  4. 按字面意义级联字符串,如"this " "is " "string"会被自动转换为this is string。

  5. 字符串可以用 + 运算符连接在一起,用 * 运算符重复。

  6. Python 中的字符串有两种索引方式,从左往右以 0 开始,从右往左以 -1 开始。

  7. Python中的字符串不能改变。

  8. Python 没有单独的字符类型,一个字符就是长度为 1 的字符串。

  9. 字符串的截取的语法格式如下:变量[头下标:尾下标:步长]

    print(str[0:-1])           # 输出第一个到倒数第二个的所有字符
    print(str[0])              # 输出字符串第一个字符
    print(str[2:5])            # 输出从第三个开始到第五个的字符
    print(str[2:])             # 输出从第三个开始后的所有字符
    print(str * 2)             # 输出字符串两次
    print(str + 'haaa')        # 连接字符串
  10. 字符串方法中方法前面加is会有不同的返回结果.

  11. 函数

    v1 = "aaaa".upper()   # 变大写
    v2 = "AAAA".lower()   # 变小写
    v3 = "aaaa".isupper() # 判断是否是大写
    v4 = "AAAA".islower() # 判断是否是小写
    v5 = "aaabaa".find("b") # 找到在字符串中对应值的索引位置,找不到返回-1
    v6 = "123".isdigit()   # 是数字为True,不是数字False
    v7 = "123".isdecimal()   # 判断是不是数字
    v8 = "   dsfz".lstrip()   # 去掉左边的空格
    v9 = "dsfz   ".rstrip() # 去掉右边的空格
    v10 = " dsfz ".strip() # 去除两边空格
    v11 = "nxs".replace("x","u",1) # 替换
    v12 = "nsdxs".split(",",1) # 分割
    v13 = "nsdxs".rsplit(",",1) # 右分割
    v14 = len("nsdxs")   # 计算长度
    v15 = "dfghfh"[1:3:1] # 索引,三个数第一个是开头数,第二个是结尾数,第三个是跳着取。
    v16 = zip([4,5,6],[1,2,3]) # 将对象逐一配对[(1, 4), (2, 5), (3, 6)]
    v17 = zip(*zip([4,5,6],[1,2,3])) # 与 zip 相反,*zip([4,5,6],[1,2,3]) 可理解为解压,返回二维矩阵式[(1, 2, 3), (4, 5, 6)]
    stri = "this is string example....wow!!!";
    v18 = stri.startswith( 'is', 2, 4 ) # 只加第一个参数代表是否已什么开头,加了后代表第几个索引开始到第一个索引结束,的范围内寻找.
    v19 = stri.endswith( 'is', 2, 4 ) # 只加第一个参数代表是否已什么结尾,加了后代表第几个索引开始到第一个索引结束,的范围内寻找.
    v20 = "{0},{1}",format("li",1) # 占位符
    v21 = "%s,str,%d,int" %("asd",4) # 占位符
    v22 = "asd".encode("utf-8") # 用于编码,把unicode转成utf-8存储在硬盘中,默认不加参数,是把str数据转换成bytes类型
    v23 = "asd".decode("utf-8") # 用于解码,默认不加参数,是把bytes数据转换成str类型
    v24 = ".".join(["a","3","3"])     # 循环每个元素;并在元素和元素之间加入连接符。
    v25 = "123".rjust(1, '0') # 字符串右边补零
    v26 = "123".ljust(1, '0') # 字符串左边补零
    v27 = "123".center(1, '0') # 字符串左右补零
    v28 = range(1,100,2)   # 生成数,第三个参数如果数1的话是从左到右,-1是从右到左。
    v29 = "adfg".replace("a","b")   # 第一个参数是要替换的字符,第二个替换成的字符,替换的个数。
    v30 = "hello word".title() # 将首字母变成大写
    v31 = "dfghnk".index("n") # 判断字符在字符串中的位置(注意如果指定的字符串中找不到会报错)
    v32 = bytes([1,2,3]) # 将一个字符串转换成字节类型4
    v33 = compile("1+2",'<string>', 'eval') # 将字符串编译成python能识别或可以执行的代码,也可以将文字读成字符串再编译。
    """
    compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
      将source编译为代码或者AST对象。代码对象能过通过exec语句来执行或者eval()进行求值。
      参数source:字符串或者AST(abstract syntax trees)对象。
      参数filename:代码文件名称,如果不是从文件读取代码则传递一些可辨认的值。
      参数model:指定编译代码的种类。可以指定'exec', 'eval', 'single'。
      参数flag和dont_inherit:这两个参数为可选参数。
    参数model,用于标识必须当做那类代表来编译;如果是一个代码语句序列组成,则指定exec,如果是单个表达式,则指定eval;如果是一个单独的交互式语句,则指定single。必须要制定,不然肯定会报错。
    """
    s = ["a","b","c"]
    for i ,v in enumerate(s,1): # 返回一个可以枚举的对象,该对象的next()方法将返回一个元组。
    print(i,v) # >>>>>1 a 2 b   3 c
    v34 = complex("1") # 当做字符串处理,创建一个值为real + imag * j的复数或者转化一个字符串或数为复数。如果第一个参数是字符串,则不需要指定第二个参数
    v34 = exec("12+10") # 执行字符串或complie方法编译过的字符串,没有返回值
    v35 = eval("1+2") #将字符串str当成有效的表达式来求值并返回计算结果,取出字符串中内容
    # pyperclip模块的复制粘贴
    # pip install pyperclip
    import pyperclip as py
    py.copy("Hello world!") #复制到剪切板
    print(py.paste()) #如果剪切板有变化就输出
    TODO 用于提示

4.3 列表型(List)

  1. 存储有序\重复的任意类型

  2. 函数

    data = [1,111,1111]
    data.append(11111)   # 追加
    data.insert(1,12)   # 指定位置插入元素
    data.remove(12)   # 值删除
    v = data.pop(1)   # 索引删除并返回删除的值
    data.clear()   # 删除全部
    del data[1]   # 删除索引的值
    data[1]="aa"   # 修改索引的值
    data.extend(["a",22])    # 将一个列表里面的值放到另外一个列表中
    data.sort(reverse=True)       # 将数据按升序或降序排列,True是下降,Fale是上升
    data.reversed()   # 列表翻转
    sorted(data) # 对列表进行临时排序,并不改变列表元素的顺序
    enumerate(data) # 可以得到列表值、以及值的序号。
    [1,2,3,4,5,6,7][slice(2,5,2)] # 切片功能
    locals() # 打印当前可用的局部变量的字典

4.4 元组型(Tuple)

  • 元组tuple,使用圆括号表示,与列表类似,唯一的区别是它的元素不能被修改。

4.5 字典型(Dict)

  1. 字典类型包括键和值,并且使用大括号表示,键值对的添加顺序没有关系,存储的键值对的关联关系。

  2. 函数

    data = {"k1":"v1"}
    data.keys()    # 获取所有键
    data.values()   # 获取所有值
    data.items()    # 获取所有键和值
    data.get("k1",13)    # 判断特定的值是否在字典中,不在默认返回None
    data.update()    # 更新
    del data["k1"] # 使用del命令来进行删除字典中的键值对,被删除的键值对永远的消失了。
    data.sorted(reverse=True)   # 降序排列,括号中加上reverse=True,将会变成升序。
    data.setdefault("k1", "123") # 有两个参数主要用于判断键是否在某个字典中。

4.6 集合型(Set)

  1. 集合是无序和无重复的

  2. 函数

    frozenset() # 创建一个不可修改的集合。
    set() # 一样
    # set和frozenset最本质的区别是前者是可变的,后者是不可变的。当集合对象会被改变时(例如删除,添加元素),只能使用set,
    # 一般来说使用fronzet的地方都可以使用set。
    # 参数iterable:可迭代对象。    
    data = set(1,2,3,4,5,6,7)
    data.add(8) # 添加
    data.discard(1) # 删除  
     
    data.intersection({1,"李少妻","小黑"})#(交集)v1和v2里面都有的
    data.union({1,"李少妻","小黑"}) #(并集)v1和v2里面全部的
    data.difference({1,"李少妻","小黑"}) #(差集)v1里面没有的
    data.symmetric_difference({1,"李少妻","小黑"})# (对称差集)
  3. 集合(嵌套):列表\字典\集合都是可变的不能嵌套

4.7 布尔型(Bool)

  • True、False

4.8 数组

  1. 函数

    import scipy.io as sio
    a = np.ones((2, 3, 4, 5)) # 以下都省略 import numpy as np
    a.shape # 查看数组维数的大小。
    b = np.transpose(a,(3, 2, 1, 0)) # 改变数组维数的位置,如把3*5的矩阵变成5*3,每个数字表示a数组所在的维数位置,如3表示把a中最后一维换到b的第一维
    c = sio.loadmat('x_data_6_for_mxnet.mat') # 加载.mat文件(matlab数据格式),特别适合高维的.mat文件。
    dict = {'a' : 'apple', 'b' : 'banana', 'g' : 'grape', 'o' : 'orange'}
    a_value = dict['a'] # 取dict中的一个value。
    a_value
    a.size # 查看数组的元素个数。
    from mxnet import nd
    x = nd.array([3.0])
    x.asscalar() # 转换ndarray数据(mxnet中的数据)为python数据。
    seasons = ['Spring', 'Summer', 'Fall', 'Winter']
    list(enumerate(seasons)) # 用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

5.文件操作

  1. open,打开、close,关、read,读、write,写、seek,调整光标的位置、readlines,每一行分割

  2. 只读只写字符串(r、w、a)、可读可写字符串(r+、w+、a+)、只读只写二进制(rb、wb、ab)、可读可写二进制(r+b、w+b、a+b)

    f = open('美女模特空姐护士联系方式.txt',mode='r',encoding='utf-8')
    content = f.read()
    print(content)
    f.close()
    f = open('美女模特空姐护士联系方式.txt',mode='w',encoding='utf-8')
    content = f.write(b"nihao") #是以二进制储存
    print(content)
    f.close()
    f = open("a.txt",mode="wb")
    data = "我好空"
    content = data.encode("utf-8")
    f.write(content)
    f.close
  3. tell,获取当前光标所在的字节位置、flush,强制把内存里面的东西刷到硬盘上

  4. 简便方式

    #青年方式
    # replace 替换
    with open("a.txt",mode="r",encoding = "utf-8") as f1:
       data = f1.read()
    new_data = data.replace("飞沙","666")
    print(new_data)
  5. 保存变量

    # shelve保存变量
    import shelve
    shelfFile = shelve.open("mydata")
    shelfFile["cats"] = ["alex","wupeiqi"] #加一次使用时直接打开索引即可,cats就是键。
    shelfFile["cats"]
    shelfFile.close()
    # pprint.pformat保存变量
    import pprint
    cats = [{"name":"alex","desc":"chubby"}]
    pprint.pformat(cats)
    fileObj = open("myCats.py","w")
    fileObj.write("cats = "+pprint.pformat(cats)+"\n")
    fileObj.close()
    import myCats
    myCats.cats
    myCats.cats[0]
  6. 大文件可以用这种方法

    f1 = open("a.txt",mode="r",encoding="utf-8")
    f2 = open("b.txt",mode="w",encoding="utf-8")
    for i in f1:
    n = i.replace("nihao","nisi")
    f2.write(n)
    f1.close()
    f2.close()
  7. 三元运算(三目运算)

    # v = 成立 if 条件 else 不成立
    data = input(">>>")
    value = ing(data) if data.isdecimal() else None

6.用函数呀

  1. 截至目前:面向过程编程。【可读性差/可重复性差】。

  2. 写代码的方式:面向过程-->函数式编程-->面向对象编程

  3. 本质:将n行代码拿到别处并给起个名字,以后通过名字就可以找到这段代码被执行。

  4. 场景:代码重复执行,代码量特别多超过一屏,可以选择通过函数进行代码的分割。

    def send_email():
    import smtplib
    from email.mime.text import MLMEText
    from email.utils import formaddr
    msg = MIMEText('信息'"plain","utf-8")
    msg["From"] = formataddr(["发件人","xxxxxx@163.com"])
    msg["To"] = formataddr(["收件人","xxxxxxxx@qq.com"])
    msg["Subject"] = "标题"
    server = smtplib.SMTP ("smtp.163.com",25)
    server.login("xxxxxx@163.com","密码")
    server.sendmail("xxxxxx@163.com",["xxxxxxxx@qq.com",],msg.as_string())
    server.quit()
    user_input = input("请输入角色:")
    if user_input =="管理员":
    send_email()
    elif user_input =="管理员":
    send_email()
    elif user_input =="管理员":
    send_email()
  5. 函数的基本结构 and 参数

    def nnn(u):
       n[11,22,33,44]
       print(n[u])
    nnn(0)
    def func(max_range):
       result = []
       while True:
           if len(result) ==0:
               result.append(1)
           elif len(result) == 1:
               result.append(1)
           else:
               val = result[-1] + result[-2]
               if val > max_range:
                   dreak
               result.append(val)
       return result
    v = func(100)
    print(v)
    def write_file(file_path,content="\n"): #换行
    def read_file(file_path,num=1): #只读第一行
       
    def fuync(*args): #*args 可以传n个参数
       print(args) #传的值是元组
       func(1)
       func(1,2)
       func(1,2,3)
       
    def fuync(*args): #*args 可以传n个参数
       print(args)
    func((11,22,33,44,55))
    func(*(11,22,33,44,55))  
    def fuync(**kwargs): #**kwargs 只能是指定传参
       print(kwargs) #它传的参是字典
    func(k1=1,k2=2)
    func(**{"K1":"V2","K2":"V3"})
    def fuync(*args,**kwargs):
       print(args,kwargs)
    func(1,2,3,4,5,{"K1":"V2","K2":"V3"})
    #如果要想给value设置默认值是空列表
    #不推荐
    def func(data,value=[]):
       pass
    #推荐
    def func(data,value=None):
       if not value:
           value = []
           
    #补充:对于函数的默认值慎用可变类型。  
  6. getattr()  获取对象的属性

    getattr(object, name [, defalut])
    """
      获取对象object名为name的特性,如果object不包含名为name的特性,将会抛出AttributeError异常;如果不包含名为name的特性
      且提供default参数,将返回default。
      参数object:对象
      参数name:对象的特性名
      参数default:缺省返回值
    """

6.1 全与局变量

  1. global,找全局里面的数据;nonlocal,找上一级的数据

    name = "老男孩"
    def func():
    name = "alex"
    def inner():
    global name
    print(name)
    name = 999
    inner()
    print(name)
    func()
    name = "老男孩"
    def func():
    name = "alex"
    def inner():
    nonlocal name
    print(name)
    name = 999
    inner()
    print(name)
    func()

6.2 匿名函数呀

  1. 用lambda来实现的

    #定义一个简单的函数将他赋给func5传入两个值进行判断。
    func5 = lambda n1, n2: n1 if n1 > n2 else n2
    v = func5(7, 1)
    print(v)
    def func5(n1,n2):
    if n1>n2:
    return n1
    else:
    return n2
    v = func5(5,3)
    print(v)
    v = []
    def f(x):
    b = v.append(x)
    return b
    n = f("nnnnnnn")
    print(n)
    f = lambda x:x.split("l")
    v1 = f("alex")
    print(v1)
  2. 函数可以做返回值

    def func():
    print(123)
    def dar():
    return func
    v =dar()
    print(v())

6.3 快用闭包呀

  1. 闭包是用到函数可以返回函数的特性所实现的

    #不是闭包
    def func1(name):
    def inner1():
    return 123
    return inner
    #是闭包:封装值+内层函数需要使用。
    def func1(name):
    def inner2():
    print(name)
    return 123
    return inner

6.4 用作用域呀

  • 递归,函数自己调用自己。(效率低)

    def func():
    print(1)
    func()
    func()
    def func(i):
    print(i)
    func(i+1)
    func(1)
    #递归次数最大1000
    #在cmd里打开python解释器,调用sys模块(import sys),查看所有(dir(sys)),找到getrecursionlimit并调用(sys.getrecursionlimit())
  • 函数执行的流程分析(函数到底是谁创建的?)

  • 闭包概念:为函数创建一块区域并为其维护自己数据,以后执行时方便调用。[应用场景:装饰器/SQLAIchemy源码]

7.练习题呀

  1. 翻页

    '''
    要求:
    每页显示10条数据
    让用户输入要查看的页面:页码
    '''
    user_list = []
    for i in range(1,836):
    temp = {"NAME":"你李少妻-%s" %i,"email":"123%s@qq.com"%i }
    user_list.append(temp)
    #数据总条数
    total_count = len(user_list)
    #每页显示10条
    per_page_count = 10
    #总页码数
    max_page_num,a = divmod(total_count,per_page_count)
    if a>0:
    max_page_num += 1
    while True:
    pager = int(input(">>>"))
    if pager <1 or pager > max_page_num:
    print("页码不合法,必须是1~%s" %max_page_num)
    else:
    start = (pager-1) * per_page_count
    end = pager * per_page_count
    data = user_list[start:end]
    for item in data:
    print(item)
  2. 将ip转换为二进制在转换为十进制

    def transformation_ip(ip):return [(bin(int(i)).replace("0b","").rjust(8,"0")) for i in ip.split(".")]
    print(transformation_ip(input(">>>"))[0])

基础中级

1.基本使用

  1. 主文件

    if __name__ == "__main__"
    page.pager()
  2. 如果你想将a和b的值调换,python有一个写法

    a=1
    b=2
    a,b = b,a
  3. 斐波那契数列

    num1 = 0
    num2 = 1
    while True
    print(num2)
    num1,num2 = num2,num1+num2
  4. 对于包的定义:

    1. py2:文件见中必须有__ init __.py。

    2. py3:不需要__ init __.py

    3. 推荐大家以后写代码时,都要加上此文件。

2.用模块呀

  1. 模块基本知识

    1. 内置模块,python内部提供的功能。

      import sys
      print(sys.atgv)
    2. 第三方模块,下载/安装/使用。

      #把pip.exe所在的目录添加到环境变量中。
      #https://pypi.org
      pip install 要安装的模块名称 #pip install xlrd
      模块会被安装到c:/python39/lib/site-packages
    3. python36 -m pip install --upgrade pip

  2. 自定义模块

    # xxx.py
    def f1():
    print("f1")
    def f2():
    print("f2")
    # r1.py
    #掉用自定义模块中的功能
    import xxx
    xxx.f1()
    xxx.f2()
  3. 导入模块

    import abc
    from abc import asc
    from abc import *
    from abc import asc as a
  4. 模块路径

    import os
    import sys
    base_dir = os.path.dirname (os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(base_dir)

2.1 md5

  1. 将字符串加密

    def get_md5(data)
    import hashlib #hai si lei buo
    obj = hashlib.md5() #md5 加密
    obj.update(data.encode("utf-8")) #要加密的字符串,以压缩格式
    result = obj.hexdigest()
    return result
  2. 加盐

    def get_md5(data)
    import hashlib #hai si lei buo
    obj = hashlib.md5("sdfjsdkljg".encode("utf-8")) #md5 加密,默认在想要加密的前面加上MD5后面的字符串
    obj.update(data.encode("utf-8")) #要加密的字符串,以压缩格式
    result = obj.hexdigest()
    return result
  3. 示例

    import hashlib
    user_list = []
    def get_md5(data)
    obj = hashlib.md5("sdfjsdkljg".encode("utf-8"))
    obj.update(data.encode("utf-8"))
    result = obj.hexdigest()
    return result
    def register():
    print("请创建账户")
    while True:
    user = input("请输入用户名")
    if user =="n":
    return
    pwd = input("请输入密码")
    temp = {"username":user,"password":get_md5(pwd)}
    user_list.append(temp)
    def login():
    print("用户登录")
    user = input("请输入用户名:")
    pwd = input("请输入密码:")
    for item in user_list:
    if item["username"] == user and item["password"] == get_md5(pwd):
    return True
    register()
    result = login()
    if result:
    print("成功")
    else:
    print("失败")
  4. 密码不显示(只能在终端中用)

    import getpass #gai te pa si
    pwd = getpass.getpass("请输入密码:")
    if pwd == "123":
    print("正确")

2.2 $sys

  1. 基本使用

    import sys
    sys.argv() # 显示文件名
    sys.path() # 默认python去导入模块时,会按照sys.path中的路径挨个查找,sys是解释器相关的数据:递归次数/引用次数
    sys.exit(0) # sys模块里的exit方法,作用终止程序
    a = [11,22,33]
    B = a
    print(sys.getrefcount(a)) # a被调用的次数,获取一个值的应用计数
    v1 = sys.getrecursionlimit() # python默认支持的递归数量
    sys.stdout.write("sdfjl")
    print("sdjfl\r",end="") # \n 换行、\t 制表符、\r 回到当前行的起始位置
    import time
    for i in range(1,101):
    msg = "%s%%\r" %i
    print(msg,end="")
    time.sleep(0.05) # 然程序在这多执行0.05
  2. 模块参数

    import sys
    #获取用户执行脚本时,传入的参数。
    #c:\sjldj\sldjf.exe d:/djks/dslkf/d.py d:/sdklf
    #sys.argv = [d:/djks/dslkf/d.py ,d:/sdklf]
    path = sys.argv[1]
    import shutil
    shutil.rmtree(path)#删除path的所有文件
    sys.path

2.3 $$os

  1. 基本使用

    import os
    os.stat(file_path).st_size # 获取文件大小
    v = r"d:\code\ksd.mp4" # r是转义,遇到\n\t\r当作普通的字符串处理
    os.listdir(path) # 查看一个目录下所有的[第一层]
    os.walk(path) # 查看一个目录下所有的[所有层]
    os.remane("name","name2") # 重命名
    os.mkdir(path) # 创建目录
    os.makedirs(path) # 创建文件夹
    os.getcwd() # 获取当前路径
    os.chdir(path) # 修改当前路劲
    os.listdir(path) # 查看路径下的所有文件
    # os.path.xxx() # os.path下的方法
    os.path.join(path,path,path) # 路径拼接
    os.path.abspath(path) # "."代表当前的绝对路径,".\\path"代表当前的相对路径,返回path规范化的绝对路径。
    os.path.isabs(path) # 查看一个路劲是相对路径还是绝对路径,绝对True。
    os.path.relpath(path,start) # 将绝对路径转为相对
    os.path.basename(path) # 获取路径中的最后一段
    os.path.dirname(path) # 获取路径中除最后一段的所有路劲
    os.path.split(path) # 将路径分割
    os.path.getsize(path) # 查看路径中文件的字节大小
    os.path.exists(path) # 查看路径是否存在
    os.path.isfile(path_file) # 查看文件是否存在并且是一个文件
    os.path.isdir(path) # 查看路径是否存在并且是一个文件夹
    os.path.exists(path) # 查看路径的闪存盘是否于电脑连接
    os.path.commonprefix(list_path) # 返回列表元素中共有路径
    os.path.normcase(path) # 在Linux和Mac下,该函数会原样返回path,在win下会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠
    os.path.normpath(path) # 规范化路径
    os.path.splitdrive(path) # 返回元组,元组两个元素盘符和路径。
    os.path.splitext(path_file) # 返回元组,元组两个元素路径和扩展名。
    os.path.getatime(path) # 文件或者目录的最后存取时间
    os.path.getmtime(path) # 文件或者目录的最后修改时间

2.4 shutil

  1. 基本使用

    import shutil
    shutil.rmtree("test") # 删除目录
    shutil.move("zz","bb") # 重命名
    shutil.make_archive("zzh","zip","b:\") # 压缩文件
    shutil.unpack_archive("zzh.zip",extract_dir=r"b:\",format="zip") # 解压文件
  2. 练习题

    import os
    import shutil
    from datetime import dateime
    ctime = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
    #1/压缩lizhongwei文件夹 zip
    #2/放到code目录(默认不存在)
    #3/将文件解压到d:\x1目录中。
    if not os.path.exists("code"):
    os.makedirs("code")
    shutil.make_archive(os.path.join("code",ctime),"zip","d:\code\slkj\lizhongwei")
    file_path = os.path.join("code",ctime)+".zip"
    shutil.unjpack_arbhive(file_path,r"d:\x1","zip")

2.5 time

  1. UTC/GMT:世界时间;本地时间:本地时区时间。

  2. 基本使用

    import time
    time.time() # 时间戳:1970-1-1 00:00
    time.sleep(10),等待秒数。
    # ##############获取datetime格式时间
    v1 = datetime.now() # 当前本地时间
    # 当前东7区时间
    tz = timezone(timedelta(hours=7))
    v2 = datetime.now(tz)
    v3 = datetime.utcomw() #当前UTC时间
    # ##########把datetime格式转换成字符串
    v1 = datetime.now()
    val = v1.strftime("%Y-%m-%d %H:%M:%S")
    # ##############字符串转成datetime
    v1 = datetime.strptime("2011-11-11","%Y-%m-%d")
    # #################datetime时间的加减
    v1 = datetime.strptime("2011-11-11","%Y-%m-%d")
    v2 = v1 - timedelta(days=140)
    date = v2.strftime("%Y-%m-%d")
    # ############### 时间戳和datetime关系
    ctime = time.time()
    v1 = datetime.fromtimestamp(ctime)
    v1 = datetime.now()
    val = v1.timestamp()

2.6 json

  1. json是一个特殊的字符串,

  2. json和pickle

    1. json,优点:所有语言通用;缺点:只能序列化基本的数据类型list/dict/int...

    2. pickle,优点:python中所有的东西都被他序列化(socke对象);缺点:序列化的内容只有python认识。

    3. loads,字符串转换回

      v = {1,2,3,4}
      val = pickle.dumps(v)
      print(val)
      data = pickle.loads(val)
      print(data,type(data))
  3. 基本使用

    import json
    v = {1,2,3,4}
    f = open ("x.xt",mode="wb")
    val = pickle.dump(v,f) # 第一个转换第二个写入
    f.close
    # ##################################
    f = open ("x.xt",mode="rb")
    data = pickle.load(f) # 转换并读出
    f.close
    v = [12,3,4,{"k1","v1"},True,"asdg"]
    v1 = json.dumps(v) # 序列化,将python的值转换为json格式的字符串。
    v = "[3,4,{"k1","v1"},True,"asdg"]"
    v1 = json.loads(v) # 反序列化,将python的值转换为json格式的字符串。
  4. 示例

    import requests
    import json
    response = requests.get("https://wwww sdl.com")
    data = json.loads(response)
    print(type(data))
    print(data)
    # ###############################
    import requests
    response = requests.get("https://www.runoob.com/python3/python3-tutorial.html")
    print(type(response))
    print(response)
    with open("b.html", mode="wb",)as f:
    for i in response:
    f.write(i)

3.用三器呀

3.1 装饰器

  1. 装饰器:在不改变原函数内部代码的基础上,在函数执行之前和之后自动执行某个功能。

  2. 函数伪装装饰器

    def func(arg): #创建函数func并传参arg
    def inner(): #在func函数里面创建函数inner
    print("before") #在inner里面打印字符串"before"
    v = arg() #将arg里面的函数运行并赋给v
    print("after") #在inner里面打印字符串"after"
    return v #在inner里面返回v的值
    return inner #在func里面返回inner
    #装饰器就是在不改变index的值的情况下添加"before"和"after"
    def index(): #创建函数index
    peint("123") #在函数index里面打印"123"字符串
    return "666" #在index里面返回字符串"666"
    index = func(index) #调用func函数并传入index函数
    index() #调用index函数
  3. 真正的装饰器

    def func(arg):
    def inner():
    print("before")
    v = arg()
    print("after")
    return v
    return inner
    #第一步:执行func函数并将下面的函数参数传递,相当于:func(index)
    #第二步:将func的返回值重新赋值给下面的函数名。index = func(index)
    @func #装饰器
    def index():
    peint("123")
    return "666"
    index()
    def x(func):
    def inner():
    return func()
    return inner
    @f
    def index():
    pass
    # ######################################
    # 然你理解没意义的一段代码
    def x(func): # 2 运行x函数并创建inner函数
    def inner(a1,a2): # 5 运行inner函数并传入两个值
    return func() # 6调用func函数就是index函数
    return inner # 3 返回inner函数并将
    # inner赋给index(index = innder)
    @x # 1 0调用x函数并吧index当做参数传给x
    def index(): # 7 运行index函数
    pass
    index(1,2) # 4 调用index函数index就是inner函数并传入两个实参
    # 不管有几个参数都可以用的装饰器
    def x1(func):
    def inner(*args,**kwargs):
    return func(*args,**kwargs)
    return inner
    @x1(1
    def f1():
    pass
    @x1
    def f2(a1):
    pass
  4. 装饰器建议写法:

    def x1(func):
    def inner(*args,**kwargs):
    data = func(*args,**kwargs)
    return data
    return inner
  5. 示例

    #如何查看一个路径是否存在?
    import os
    result = os.path.exists("路径地址")
    # result为True,则表示路径存在。
    # result为False,则表示路径不存在。
    # ######################################
    def wrapper(func):
    def inner(*atgs,**kwargs):
    #检测路径是否存在
    path = args[0]
    #路径不存在就执行判断下面命令
    if not os.path.exists(path):
    print("路径不存在")
    return None
    result = func(*atgs,**kwargs)
    return result
    return inner
    @wrapper
    def read_userinfo(path):
    file_obj = open(path,mode="r",encoding="utf-8")
    data = file_obj.read()
    file_obj.close()
    return data
    content = read_userinfo("/usr/bin/xxx/xxx")
    print(content)
    import time
    def wrapper(func):
    def inner():
    start_time = time.time() #获取当前时间
    v = func()
    end_time = time.time() #获取当前时间
    print(start_time-end_time)
    return v
    return inner
    @wrapper
    def func1():
    time.sleep(2) #等待两秒时间
    print(123)
    def func2():
    time.sleep(1) #等待两秒时间
    print(123)
    def func3():
    time.sleep(1.5) #等待两秒时间
    print(123)
    func1()
    #加参数
    def func(arg):
    def inner(*args,**kwargs):
    print("before")
    v = arg(*args,**kwargs)
    print("after")
    return v
    return inner
    @func
    def index():
    peint("123")
    return "666"
    index("yidn")

3.2 迭代器

  1. 迭代器,帮助你对某种对象(str/list/tuple/dict/set类创建的对象)中的元素进行逐一获取。

    v1 = iter([11,2,2,3,]) # 列表转换成迭代器
    v2 = v1.__iter__()
    v2.all() # 接受一个迭代器,如果迭代器里有一个元素为真,那么返回True,否则返回False
    v2.any() # 接受一个迭代器,如果迭代器里有一个元素为真,那么返回True,否则返回False
    try
    val = v1.__next__() # 反复调用,迭代器想要获取每个值
    except Stoplteration as e: # 直到报错:Stoplteration错误,表示已经迭代完毕
    pass
  2. 如何判别一个对象是否是迭代器:内部是否有__ next__方法。

  3. 可迭代对象是内部具有__ iter__()方法并且可以被for循环。

3.3 生成器

  1. 生成器函数(内部是否包含yield)

    def func():
    print("你好")
    yield 1
    print("1")
    yield 2
    print("3")
    yield(3)
    print("4")
    # 函数内部代码不会执行,返回一个生成器对象。
    v1 = func()
    #生成器是可以被for循环,一旦开始循环那么函数内部代码就会开始执行。
    for item in v1:
    print(item)
    #################################################################
    count = 1
    def func():
    while True:
    yield count
    count += 1
    val = func()
    for item in val:
    print(item)
  2. 示例

    def func():
    """
    分批去读取文件中的内容,将文件的内容返回给调用者。
    :return:
    """
    cursor = 0
    while True:
    f = open("dd",'r',encoding = "utf-8")
    f.seek(cursor)
    data_list[]
    for i in range(10):
    ilne = f.readline()
    if not line:
    return
    ata_list.append(line)
    cursor = f.tell()
    f.close()
    for row in data_list:
    yield row
  3. 总结:

    1. 函数中如果存在yield,那么该函数就是一个生成器函数,调用生成器函数会返回一个生成器,生成器只有被for循环时,生成器函数内部的代码才会执行,每次循环都会返回执行。

    2. 迭代器,对可迭代对象的元素进行逐一获取,迭代器对象的内部都有一个next方法,用于以一个个获取数据。

    3. 生成器,函数内部有yield则就是生成器函数,调用函数并返回一个生成器,循环生成器时,则函数内部代码才会执行。

4.推导式呀

  • 列表推导式

    vals = [i for i in "alex"]
    v1 = [i for i in "alex"]
    v2 = [i+100 for i in range(10)]
    v3 = [ 99 if i>5else 66 for i in range(10)]
    def func():
    print(123)
    v4 = [func for i in range(10)]
    v5 = [lambda :100 for i in range(10)]
    def num():
    return [lambda x:i*x for i in range(4)]
    print([m(2) for m in num()])
    v9 = [i for i in range(10) if i > 5]
  • 集合推导式

    v1 = { i for i in "alex"}
  • 字典推导式

    v1 = {"k"+str(i):i for i in range(10)}

5.异常处理

  1. 基本使用

    def func(c):
    try:
    return a.strip()
    #e里面保存的是报错信息
    except Exception as e:
    pass
    return False
    v = func("alex")
    if not v:
    print("函数执行失败")
    else:
    print("结果是",v)
  2. 练习题

    # 1.写函数,函数接受一个列表,请将列表中的元素每个都 +100
    def func(arg):
    result = []
    for item in arg:
    if item.isdecimal():
    result.append(int(item)+100)
    return result
    # 2.写函数去,接受一个列表。列表中都是url,请访问每个地址并获取结果。
    import requests
    def func(url_list):
    result = []
    try
    for url in url_list:
    response = requests.get(url)
    result.append(response.text)
    except Exception as e:
    pass
    return result

6.面向对象

  1. 类和对象,类是具有相同属性的摸具,对象有摸具创造具有相同的属性和方法。

  2. 三大特性,封装、继承、多态,python原生支持多态,崇尚鸭子模型。由于python函数传参时,无序指定类型:

    def func(arg):# arg可以是多种类型,只要其中有send方法即可。
    arg.send()
  3. 编写面向对象程序,归类+提取公共值

  4. self到底是谁,self参数是python帮助我们自动传递(就是自己)。

  5. 如果执行面向对象中的方法时,前面需要有一个对象:xxx.func()

    class Foo:
    def f1 (self):
    pass
    def f2(self):
    self.f1()
    obj = Foo()
    obj.f1()
  6. python支持多继承

    def __init__(self,name): #构造方法
  7. 经典类和新式类

    • 新式类在py2中,如果自己的前辈要有人继承object,那么此类就是新式类。

    • py3经典类和新式类的查照成员的顺序不一致。

      # 经典类是一条道走到黑(深度优先),会按照继承关系,先在自己这寻找,找不到后去父类中找,只找继承父类的第一个,直到第一代,会回来再去找第二个父类。
      print(Foo.__mrc__) # 新式类是由c3算法实现
  8. 基本使用

    class abc(obj):
    def func(self,name):
    print(name)
    return 123
    def func2(self,name):
    print(name)
    return 456
    #调用类中的方法
    obj = abc() #1.创建该类的对象
    result = obj.func("alex") #2.通过对象调用方法
    print(result)
  9. 对象的作用,存储一些值,以后方便使用。

    class File:
    def read(self):
    with open(self.xxxx,mode="r",encoding="utf-8") as f:
    data = f.read()
    return data
    def write(self,content):
    with open(self.xxxx,mode="w",encoding="utf-8") as f:
    data = f.write(content)
    #实例化了一个File类的对象
    obj1 = File()
    #在对象中写了一个xxxx = "test.log"
    obj1.xxxx = "test.txt"
    #通过对象调用类中的read方法,read方法中的self就是obj。
    obj1.write("alex")
    obj1.read()
    class Person:
    #初始化方法(构造方法),给对象的内部做初始化。
    def __init__(self,n,a,g):
    self.name = n
    self.age = a
    self.gender = g
    def show(self):
    temp = "我是%s,年龄%s,性别%s" % (self.name,self.age,self.gender,)
    print(temp)
    #类()实例化对象,自动执行此类中的__init__方法。
    p1 = Person("里",12,"男")
    p1.show()
  10. 总结:将数据封装到对象,方便使用。

6.1 成员三类

  1. 说明

    class Foo:
    #方法
    def __init__(self,name):
    #实例变量/字段
    self.name = name
    #方法
    def func(self):
    pass
    # obj,Foo类的对象
    # obj,Foo类的实例
    obj = Foo("holle")

1.变量

  1. 实例变量(字段),公有和私有实例变量(字段)

    class Foo:
    def __init__(self,name):
    self.__name = name # 私有实例变量,就是在变量前面加上两个下划线(私有字段)
    self.age = 123
    def func(self):
    print(self.__name)
    obj = Foo("holle")
    print(obj.__name) # obj.__name 无法访问
    obj.func() # 找个人内部的人访问
  2. 类变量(静态字段),公有和私有类变量(静态变量)

    class Foo:
    #类变量(静态字段)
    country = "中国"
    def __init__(self,name):
    #实例变量(字段)
    self.name = name
    obj1 = Foo("holle")
    obj2 = Foo("你好")
  3. 准则

    1. 实例变量(字段)访问时,使用对象访问,即:obj1.name

    2. 类变量(静态字段)访问时,使用类访问,即:Foo。country实在不方便时,(才使用对象)

    3. 什么时候用类变量?当所有对象中有共同的字段时则要改都改要删都删时,可以将 实例变量(字段)提取到 类变量(静态字段)

2.方法

  1. 实例方法

    class Foo(object):
    def __init__(self,name):
    self.name = name
    #实例方法
    def func(self):
    print(self.name)
  2. 静态方法

    class Foo(object):
    #静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
    @staticmethod
    def display():
    print("666")
    Foo.display()
  3. 类方法

    class Foo(object):
    @classmethod
    def show(cls,x1,x2):
    print(x1,x2)
    Foo.show(1,8)
  4. 总结

    1. 定义时:方法上方写:@classmethod,方法的参数:至少有一个cls参数

    2. 执行时:类名.方法名() #默认会将当前类传到参数中。

    3. 什么时候用?如果在方法中会使用到当前类,你们就可以使用方法。

3.属性

  1. 写法

    class Foo(object):
    @property
    def start(self):
    return 1
    @proprety
    def end(self):
    return 10
    obj = Foo()
    print(obj.start)
    print(obj.end)
    class Foo(object):
    def __init__(self,age):
    self.age = age
    def display(self):
    print(self.age)
    data_list = [Foo(8),Foo(9)]
    for i in data_list:
    #在print的时候先执行方法,在执行对象
    print(i.display(),i.age)
  2. 总结:

    1. 编写时:方法上方写@property,方法参数:只有一个self

    2. 调用时:无序加括号 对象.方法

    3. 应用场景:对于简单的方法,当无需传参则有返回值时,可以使用@property

6.2 其他成员

  1. 主动调用其他类的成员

    # Base.实例方法(自己传self),与继承无关
    class Base(object):
    def f1(self):
    print("5")
    class Foo(object):
    def f1(self):
    print("3")
    Base.f1(self)
    obj = Foo()
    obj.f1()
    # 按照类的继承顺序
    class Base(object):
    def f1(self):
    print("5")
    class Foo(Base):
    def f1(self):
    print("3")
    super().f1() # 找下一个同名方法
    obj = Foo()
    obj.f1()
    ###########################
    class Foo(object):
    def f1(self):
    super().f1()
    print("5")
    class Bar(Base ):
    def f1(self):
    print("3")
    class Info(Foo,Bar):
    pass
    obj = Info()
    obj.f1()
  2. 特殊成员

    class Foo(object):
    def __init__(self,a1,a2):
    self.a1 = a1
    self.a2 = a2
    def __call__(self,*args,**kwargs):
    print(111,args,kwargs)
    def __getitem__(self,item):
    print(item)
    return 8
    def __setitem__(self,key,value):
    print(key,value,111)
    def __delitem__(self,key):
    print(key)
    def __add__(self,other):
    return self.a1 + other.a2
    def __enter__(self):
    print(1111)
    return 999
    def __exit__(self,exc_type,exc_val,exc_tb):
    print(2222)
    # 1.类名() 自动执行__init__
    obj = Foo(1,2)
    # 2.对象() 自动执行__call__
    obj(5,2,k1=122)
    #3.对象[xx] 自动执行__getitem__
    ret = obj["yu"]
    print(ret)
    #4.对象["xx"] = 123 自动执行__setitem__
    obj["k1"]=123
    #5.del 对象["sss"] 自动执行__delitem__
    del obj["uuu"]
    #6.对象+对象 自动执行__add__
    obj1 = Foo(1,2)
    obj2 = Foo(88,99)
    ret = obj2 = obj1
    print(ret)
    #7.with 对象 自动执行__enter__进入,__exit__退出
    obj = Foo
    with obj as f:
    print(f)
    print("内部")
    # #################################
    #8.真正的构造方法
    class Foo(object):
    def __init__(self,a1,a2):#初始化方法
    self.a1 = a1
    self.a2 = a2
    def __new__(cls,*args,**kwargs):#构造方法
    v1 = object.__new__(cls)#python内部创建一个当前类的对象(创建时内部是空的)。
    print(v1)
    return v1
    obj = Foo(1,2)
    print(obj)
    class Foo(object):
    def __init__(self):
    pass
    def func(self):
    pass
    #查看obj时返回"F1"
    def __str__(self):
    return "F1"
    obj = Foo()
    print(obj,type(obj))
    class Foo(object):
    def __init__(self,name,age):
    self.name = name
    self.age = age
    def func(self):
    pass
    def __iter__(self):
    return iter([11,22,33,4])
    """
    如果想要把不可迭代对象 -> 可迭代对象
    1.在类中定义__iter__方法
    2.iter 内部返回一个迭代器
    """
    obj = Foo("楼上的",99)
    for item in obj1:
    print(item)
    _ _dict _ _ # 获取封装值
    _ _doc _ _ # 打印类注释

6.3 类的有关

  1. issubclass and isinstance

    class Base(object):
    pass
    class Foo(Base):
    pass
    class Bar(Foo):
    pass
    print(issubclass(Bar,Base)) # 检查第一个参数是否是第二个参数的子类,是True,不是False
    obj = Foo()
    print(issubclass(obj,Foo)) # 检查第一个参数(对象)是否是第二个参数(类)的实例(包含父类),是True,不是False
    print(type(obj)) # 获取当前对象是由那个类创建。
  2. next and send

    def a():
    print('aaa')
    p = yield '123' # p1是send传过来的
    print('bbb')
    r = a()
    print(next(r)) # next获得yield后面的值
    r.send('hello') # send将值传入yield前面的变量
  3. 约束

    class BaseMessage(object):
    def send(self):
    """
    必须继承BaseMessage,然后其中必须编写send方法,用于完成具体业务逻辑。
    raise NotInplementedError(".send() 必须被重写。")
    """
    raise NotInplementedError(".send() 必须被重写。")
    # BaseMessage类用于约束,约束其派生类:保证派生类中必须编写send方法,不然执行可能就会报错。
    class BaseMessage(object):
    def send(self,x1):
    raise NotImplementedError(".send() 必须被重写。")
    class Email(BaseMessage):
    def send(self,x1):
    print("发送邮箱")
    obj = Email()
    obj.send(1)
  4. 总结

    1. 什么是接口以及作用?接口是一种数据类型,主要用于约束派生类中必须实现指定的方法,python中不存在,java和c# 中是存在的。

    2. python中使用过什么类约束呢?抽象类+抽象方法,编写上麻烦,人为主动抛出异常

    3. 约束时,抛出的异常是否可以用其他的?不专业:raise Exception(".send() 必须被重写."),专业:raise NotImplementedError(".send() 必须被重写.")

7.用三闲呀

7.1 error

  1. 自定义异常用于,各种异常。

    #知识点:如何自定义异常类?
    class MyException(Exception):
    def __init__(self,code,msg):
    self.code = code
    self.msg = msg
    try:
    #知识点:主动抛出异常
    raise MyException(1000,"操作异常")
    except KeyError as obj:
    print(obj,1111)
    except MyException as obj: #知识点:捕获异常
    print(obj,2222)
    except Exception as obj:
    print(obj,3333)

7.2 日志

  1. 用的不多,了解即可。

    import logging
    logger = logging.basicConfig(filename="xxx.txt"
    fornat="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
    level=30)
    logging.debug("x1") #10 #测试
    logging.info("x2") #20 #正常
    logging.warning("x3") #30 #警告
    logging.error("x4") #40 #报错
    logging.critical("x5") #50 #严重错误
    logging.log(10,"x6") #60 #自定义日志
    #filename,"文件名或文件路径"fornat,datefmt"asctime时间 - name用户名 - levelname级别 - module当前文件名: %message文件内容"时间,level=级别
    import logging
    logger = logging.basicConfig(filename="xxx.txt"
    fornat="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
    level=30)
    import traceback
    def func():
    try:
    a = a + 1
    except Exception as e:
    #获取当前错误的堆栈信息
    msg = traceback.format_exc()
    logging.error(msg)
    func()
  2. 自定义日志

    import logging
    # 创建一个操作日志的对象logger(依赖FileHandler)
    file_handler = logging.FileHandler('l1.log', 'a', encoding='utf-8')
    file_handler.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s"))
    logger1 = logging.Logger('s1', level=logging.ERROR)
    logger1.addHandler(file_handler)
    logger1.error('123123123')

7.3 正则

  1. 在实际开发中常用查找某些复杂规则的字符串的需要,这时候就可以使用正则表达式。通用性强。

  2. 匹配Regex对象

    import re
    phoneNumRegex = re.compile(r"\d\d\d-\d\d\d-\d\d\d") #创建一个Regex对象.
    mo = phoneNumRegex.search("My number is 145-126-187.") #将Regex和要匹配的字符串进行匹配,匹配满足条件的第一个
    mo = phoneNumRegex.findall("My number is 145-126-187 or 568-874-589.") #将Regex和要匹配的字符串进行匹配,匹配满足条件的所有返回list
    mo = phoneNumRegex.sub("000-000-000","My number is 145-126-187.") #将Regex和要匹配的字符串进行匹配,匹配满足条件后进行替换
    print(mo.group()) #获取匹配的数据

     

  3. 正则表达式就是记录文本规则的代码,正则表达式需要用到re模块

    import re
    result = re.match(正则表达式,要匹配的字符串) #使用match方法进行匹配
    result.group() #提取数据,如果正则中分过组,可以加入数字参数获取每个小组.
  4. 单个字符匹配

    . #匹配任意1个字符(除\n)
    [] #匹配[]中列举的字符
    \d #匹配数字 0~1
    \D #匹配非数字
    \s #匹配空白
    \S #匹配非空白
    \w #匹配非特殊字符,a~z/A~Z/0~9/_/汉字
    \W #匹配特殊字符
  5. 多个字符匹配

    * #匹配前一个字符出现0或n次
    + #匹配前一个字符出现1或n次
    ? #匹配前一个字符出现0或1次
    {m} #匹配前一个字符出现m次
    {m,n}#匹配前一个字符出现从m到n次
  6. 匹配开和尾

    ^ #匹配字符串开头
    $ #匹配字符串结尾
  7. 匹配分组

    | #匹配左右任意一个表达式
    (ab) #将括号中字符作为一个分组
    \num #引用分组num匹配到的字符
    (?p<name>) #分组起别名
    (?P=name) #引用别名为name分组匹配到的字符串

8.练习题呀

  1. 进度条

    #导入模块os
    import os
    #获取文件的总大小(字节)
    file_size = os.stat("asdlkfjsldjf.mp4").st_size
    # 2.一点一点的读取文件
    read_size = 0
    with open("asdlkfjsldjf.mp4",mode="rb") as f1:
    while read_size < file_size:
    chunk = f1.read(1024)
    #每次最多去读取1024字节
    read_size += len(chunk)
    val = int(read_size / file_size * 100)
    print("%s%%\r" %val,end="")
  2. 商品管理

    def goods():
    """
    商品管理
    :return:
    """
    func_dict = {"1":goods_list,"2":search_goods,"
    3":create_goods}
    while True:
    print("1,商品列表;2.模糊搜索,3.求人信息")
    num = input("请选择序号(N/n):")
    if num.upper() =="n":
    return
    func = func_dict.get(num)
    if not func:
    print("输入错误,请重新输入!")
    continue
    func()

基础高级

1.基础知识

  1. 架构有:CS架构(Client/Server)、浏览器:BS架构(Broeser/Server)

  2. 如何实现相互通信,编写两个软件,软件之间相互通信、两个人直接连接 (网线)。

  3. TCP和UDP

  • TCP (Transmission Control Protocol)可靠的、面向连接的协议、传输效率低全双工通信、面向字节流。使用TCP的应用:web浏览器;电子邮件、文件传输程序。

  • UDP(User Datagram Prolocol)不可靠的,无连接的服务,传输效率高,一对一,一对多,多对一,多对多,面向报文,静最大努力服务,无堵塞控制。使用UDP的应用:域名系统(DNS);视频流;IP语音。

  1. 一个及其联网需要有一个ip、子网掩码、网关、DNS。

  2. 局域网内:广播、单播、广播风暴、arp协议

  3. 总结:

  4. 相互通信本质发送二进制,交换机用于连接局域网中的设备,通过ipconfig查看自己的内网IP

  5. DHCP,自动为局域网内容电脑分配ip;网关,路由器中连接交换机的口;ip 4个点分的十进制表示

  6. DNS

    • 域名:名称.一级.二级:端口\文件夹\文件.html

    • 问题来了,域名和ip的对应关系在哪里?

      • 本地:

        • win本地电脑:c:\windows\System32\drvers\etc\hosts(11.11.11.11 liyongqiang.com)

        • Linux/Mac电脑:/etc/hosts

      • DNS服务器:全球顶级DNS服务器13台:www.luffycity.com 47.95.64.113

  7. 网络基础

    • 端口,是什么?为什么要有?

      • 端口是为了将同一个电脑上的不同程序进行隔离。

      • 总范围1 - 65535,但是1 - 1024已经被系统程序占了。

  8. 国际标准化组织(ISO/OSI)

    • 7层:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

    • 5层:应用层(表示层/会话层)、传输层、网络层、数据链路层、物理层

    • 4层:应用层(应用层/表示层/会话层)、传输层、网络层、物理层(数据链路层/物理层)

  9. TCP三次握手/四次挥手

    • socket客户端向服务端发起连接请求:请求连接、允许连接、连接成功

    • 服务端和客户端端来连接:请求断开连接、正在整理数据、允许断开连接、程序断开完成

    • 补充:断开连接时,反应到代码上:抛出异常/发送空内容

  10. 操作系统/应用程序

    1. 硬件:硬盘、cpu、主板、显卡、内存、电源

    2. 装系统(软件),系统就是一个由程序员写出来软件,该软件用于控制计算机的硬件,让它们之间进行相互配合。

    3. 安软件(安装应用程序),qq、百度云、pycharm

  11. 并发和并行:并发,伪,由于执行速度特别快,人感觉不到迟钝;并行,真,创建10个人同时操作。

  12. 到底什么是线程?什么是进程?python之际没有,python中调用的操作系统的线程和进程。

    import threading
    print("666")
    def func(arg):
    print(arg)
    t = threading.Thread(target=func)
    t.start()
    print("end")
  13. 线程:工作的最小单元,共享进程中所有的资源,每个线程可以分担一些任务,最终完成最后和结果。

  14. 进程:独立开辟内存,进程之间的数据隔离

  15. 总结

    1. 程序员写好代码在操作系统上运行(依赖解释器)

    2. python多线程情况下:计算密集型操作:效率低。(GIL锁)、IO操作:效率高

    3. python多进程的情况下:计算密集型操作:效率高(浪费资源)、IO操作:效率高(浪费资源)

    4. 以后写python时:IO密集型用多线程,文件/输入输出/socket网络通信、计算密集型用多进程

    5. JAVA多线程情况下:计算密集型操作:效率高、IO操作:效率高

    6. python中线程和进程(GIL锁)GIL锁,全局解释器锁。用于限制一个进程中同一时刻只有一个线程被cpu调动。扩展:默认GIL锁在执行100个CPU指令(过期时间)

2.基本网络

2.1 socket

  1. 服务端

    import socket
    # 创建服务端socket对象
    server = socket.socket()
    # 绑定IP和端口
    server.bind(('192.168.13.155',8000))
    # 后边可以等5个人
    server.listen(5)
    print('服务端准备开始接收客户端的连接')
    # 等待客户端来连接,如果没人来就傻傻的等待。
    # conn是客户端和服务端连接的对象(伞),服务端以后要通过该对象进行收发数据。
    # addr是客户端的地址信息。
    # #### 阻塞,只有有客户端进行连接,则获取客户端连接然后开始进行通信。
    conn,addr = server.accept()
    print('已经有人连接上了,客户端信息:',conn,addr)
    # 通过对象去获取(王晓东通过伞给我发送的消息)
    # 1024表示:服务端通过(伞)获取数据时,一次性最多拿1024字节。
    data = conn.recv(1024)
    print('已经有人发来消息了',data)
    # 服务端通过连接对象(伞)给客户端回复了一个消息。
    conn.send(b'stop')
    # 与客户端断开连接(放开那把伞)
    conn.close()
    # 关闭服务端的服务
    server.close()
  2. 客户端

    import socket
    client = socket.socket()
    #王晓东向服务端发起连接请求(递伞)
    #堵塞,去连接,直到连接成功后才会继续向下走。
    client.connect(("xx.xx.xx.xx",8000))
    #连接上服务端后,向服务端发送消息
    client.send(b"hello")
    #万晓东等待服务端给它发送消息
    data = client.recv(1024)
    print(data)
    #关闭自己
    client.close()
  3. 基于socket模块实现网络通信

  4. 为什么要网络通信发送的是字节?而不是字符串?py2,send/recv 都是字节、py3,send/recv 都是字符串。

  5. 服务端:accept,阻塞:等待客户端来连接、recv,阻塞:等待客户端发来数据。

  6. 客户端:connect,阻塞:一直在连接,值到连接成功才往下运行其他代码、recv,阻塞:等待服务器发来数据。

2.2 subprocess/struct

  1. 标准输入

    import subprocess
    res=subprocess.Popen("dir",#指定命令名
    shell=True,
    stderr=subprocess.PIPE,
    #错误输出
    stdout=subprocess.PIPE,)
    #接收系统返回的值
    print(res.stdout.read().decode("gbk"))
    #输出接收到的值以“gbk”
  2. 包判定计算大小

    import struct
    #i模式把123456压缩,四个字节,如果大于就报错
    # 最大是2的31次方减一
    header_pack=struct.pack("i",2147483647)#压包
    print(header_pack)
    obj = struct.unpack("i",beader_pack)#解包
    print(obj)

2.3 socketserver

  1. 快速server模块

    import socketserver
    #所有的网络通信都在这里面执行
    class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
    whlie True:
    try:
    print("等待")
    #之前的conn现在是self.request
    data = self.request.recv(1024)
    if data == b"t"
    break
    response = data + b"sb"
    self.request.send(response)
    except Exception as e:
    break
    self.request.close()
    #创建服务端加ip,端口,类名
    #1.创建socket对象 2.self.socket.bind() 3.self.socket.listen(5)
    server = socketserver.ThreadingTCPServer(("127.0.0.1",8899),Myserver)
    #等待连接连接成功后会从类开始执行
    server.serve_forever()

3.用三程呀

3.1 线程

  1. 使用threading实现

    import threading
    import time
    def func(arg):
    #获取当前执行该函数的线程的对象
    t = thredding.current_thread()
    #根据当前线程对象获取当前线程名称
    name = t.getName()
    time.sleep(2)
    print(name,arg)
    t1 = threading.Thread(target=func,args=(3,))
    #为True就让主进程不等待直接执行完
    #为False就让主进程等待子进程,默认时等待的
    t1.setDaemon(True)
    #设置名
    t1.setName("哇哈哈")
    t1.start
    #无参数,让主线程在这里等着,等到子线程t1执行完毕,才可以继续往下走。
    #有参数,让主线程在这里最多等待n秒,无论是否执行完毕,会继续往下走。
    t1.join(2)
  2. 自定义创建多线程

    import socket
    import threading
    def task(conn):
    data = conn.recv(1024)
    print(data)
    conn.close()
    server = socket.socket()
    server.bind(("127.0.0.1,8822"))
    server.listen(5)
    while True:
    conn,addr = server.accept()
    t = threading.Thread(target=task,args=(conn,))
    t.start()
  3. 面向对象多线程

    import therading
    #1
    def func(arg):
    print(arg)
    t1 = threading.Thread(target=func,args=(11,))
    t1.start
    #2
    class MyThread(threading.Thread):
    def run(self):
    print(1111,self._args,self._kwargs)
    t1 = MyThread(args=(11,))
    t1.start()
    t2 = MyThread(args=(22,))
    t2.start()
  4. uuid模块

    import uuid
    随机生成字符串
    v = str(uuid.uuid4())
    print(v)
  5. requests模块

    #需要下载 pip install requests
    #爬图片
    import reqquest
    import uuid
    import threading
    url_list = []
    def task(url):
    """
    1.DNS解析,根据域名解析出ip
    2.创建socket客户端 sk = socket.socket()
    3.向服务端发起连接请求 sk.connect()
    4.发起数据(我要图片) sk.send(...)
    5.接收数据 sk.recv(8096)
    """
    #帮你创建连接然后域名解析,然后然他给你发图片
    ret = requests.get(url)
    #请求下载下来的图片
    file_name = str(uuid.uuid4()) + ".jpg"
    with open(file_name,mode="wb")as f:
    f.write(ret.content)
    for url in url_list:
    t = threading.Thread(target=task,args=(url,))
    t.start()
  6. 锁的初步认识

    import time
    import threading
    lock = threading.RLock()
    n = 10
    def task(i):
    print("这段代码不加锁")
    lock.acquire()#加锁
    global n
    print("当前线程",i,"读取到的n值为:",n)
    n = i
    time.sleep(1)
    print("当前线程",i,"修改n值为:",n)
    lock.release()#解锁
    for i in range(10):
    t = threading.Thread(target=task,args=(i,))
    t.start()
  7. 总结

    1. 应用程序/进程/线程的关系?

    2. 为什么要创建线程?由于线程时cpu工作的最小单元,创建线程可以利用多核优势实现并行操作(JAVA/C#)

    3. 为什么要创建进程?进程和进程之间做数据隔离(Java/c#)

    4. python

      1. python中存在一个GIL锁,造成:多线程无法利用多核优势、解决:开多进程处理(浪费资源)

      2. 线程的创建:Thread、MyTread

      3. 其他:join、setDeanon、setName、threading.current_thread()

      4. 锁,获得、释放

    5. python的GIL锁

      • python内置的一个全局解释器锁,锁的作用就是保证同一刻一个进程中只有一个线程可以被cpu调用。

      • 为什么有这把GIL锁?python语言的创始人在开发这门语言时,目的快速把语言开发出来,如果加上GIL锁(c语言加锁)

    6. 进程和线程的区别?线程,cpu工作的最小单元。、进程,线程提供一个资源共享的空间、一个进程中默认是有一个主线程。

    7. 进程和线程的使用准则:计算密集型:多进程、IO密集型:多线程

    8. 线程创建的越多越好吗?不好,线程之间切换时,要做上下文管理。

1.线程池

  1. 基本使用

    from concurrent.futures import ThreadPoolExecutor
    import time
    def task(a1,a2):
    time.sleep(2)
    print(a1,a2)
    #创建了一个线程池(最多5个线程)
    pool = ThreadPoolExecutor(5)
    for i in range(40):
    #去线程池中申请一个线程,让线程执行task函数
    pool.submit(task,i,8)
  2. 生产者和消费者模型

    # by luffycity.com
    import time
    import queue
    import threading
    q = queue.Queue() # 线程安全
    def producer(id):
    """
    生产者
    :return:
    """
    while True:
    time.sleep(2)
    q.put('包子') # 存值
    print('厨师%s 生产了一个包子' %id )
    for i in range(1,4):
    t = threading.Thread(target=producer,args=(i,))
    t.start()
    def consumer(id):
    """
    消费者
    :return:
    """
    while True:
    time.sleep(1)
    v1 = q.get() # 取值
    print('顾客 %s 吃了一个包子' % id)
    for i in range(1,3):
    t = threading.Thread(target=consumer,args=(i,))
    t.start()
  3. 生产者消费者模型解决了什么问题?不用一直等待的问题。

2.独立空间

  1. 作用:内部自动为每个线程维护一个空间(字典),用于当前存储属于自己的值。保证线程之间的数据隔离。

  2. 基本使用

    import time
    import threading
    v = threading.local()
    def func(arg):
    #内部会为当前线程创建一个空间用于储存:phone=自己的值
    v.phone = arg
    time.sleep(2)
    print(v.phone,arg)#去当前线程自己空间获值
    for i in range(10):
    t = threading.Thread(target=func,args=(i,))
    t.start()
  3. local原理

    import time
    import threading
    def func(arg):
    ident = threading.get_ident()
    DATA_DICT[ident] = arg
    time.sleep(2)
    print(DATA_DICT[ident],arg)
    for i in range(10):
    t = threading.Thread(target=func,args=(i,))
    t.start()
  4. 实例

    # by luffycity.com
    """
    以后:Flask框架内部看到源码 上下文管理
    """
    import time
    import threading
    INFO = {}
    class Local(object):
    def __getattr__(self, item):
    ident = threading.get_ident()
    return INFO[ident][item]
    def __setattr__(self, key, value):
    ident = threading.get_ident()
    if ident in INFO:
    INFO[ident][key] = value
    else:
    INFO[ident] = {key:value}
    obj = Local()
    def func(arg):
    obj.phone = arg # 调用对象的 __setattr__方法(“phone”,1)
    time.sleep(2)
    print(obj.phone,arg)
    for i in range(10):
    t =threading.Thread(target=func,args=(i,))
    t.start()

3.2 进程

  1. 基本使用

    #mac系统不会报错#win系统会报错
    def task(arg):
    print(arg)
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,))
    p.start()
    #这样win系统就不会报错了
    def task(arg):
    print(arg)
    if __name__ =="__main__":
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,))
    p.start()
  2. 不共享

    import multiprocssing
    import threading
    import queue
    data_list = []
    def task(arg):
    data_list.append(arg)
    print(data_list)
    def run():
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,))
    p.start()
    if __name__ =="__main__":
    run()
  3. 进程常用功能

    import multiprocssing
    import threading
    import queue
    def task(arg):
    time.sleep(2)
    print(arg)
    def run():
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,))
    p.start()
    p.join()#等待进程
    if __name__ =="__main__":
    run()
    ###########################
    def task(arg):
    time.sleep(2)
    print(arg)
    def run():
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,))
    p.daemon = False#True不等子进程,相反
    p.start()
    if __name__ =="__main__":
    run()
    ###################
    def task(arg):
    #获取当前进程
    p = multiprocessing.current_process()
    print(p.name)
    time.sleep(2)
    print(arg)
    def run():
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,))
    p.name = "pp1"#给进程命名
    p.start()
    if __name__ =="__main__":
    run()
    #########################
    def task(arg):
    #获取当前进程
    p = multiprocessing.current_process()
    print(p.ident)#打印当前进程
    time.sleep(2)
    print(arg)
    def run():
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,))
    p.name = "pp1"#给进程命名
    p.start()
    if __name__ =="__main__":
    run()
  4. 面向对象创建进程

    import multiprocssing
    import threading
    import queue
    class MyProcess(multiprocessing.Process):
    def task(arg):
    def run(self):
    print("当前进程",multiprocessing.current_process())
    def run():
    p.MyProcess()
    p.start()
    if __name__ =="__main__":
    run()
  5. 进程间的数据共享:先进先出 队列、先进后出 站,进程间的共享

    #linux这样写
    import multiprocessing
    q = multiprocessing.Queue()
    def task(arg,q):
    q.put(arg)
    def run():
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,q,))
    while True:
    v = q.get()
    print(v)
    if __name__ == "__main__"
    run()
    ############################
    #windows这样写
    import multiprocessing
    def task(arg,q):
    q.put(arg)
    if __name__ == "__main__"
    q = multiprocessing.Queue()
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,q,))
    while True:
    v = q.get()
    print(v)
  6. 进程间的共享 Manager

    #linux这样写
    import multiprocessing
    q = multiprocessing.Manager()
    dic = m.dict()
    def task(arg,q):
    dic[arg] = 100
    def run():
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,q,))
    p.start()
    input(">>>")
    print(dic.values())
    if __name__ == "__main__"
    run()
    ############################
    #windows这样写
    import multiprocessing
    def task(arg,q):
    dic[arg] = 100
    if __name__ == "__main__"
    m = multiprocessing.Manager()
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,m,))
    p.join()
    print(dic)
    ######################2
    import multiprocessing
    def task(arg,q):
    dic[arg] = 100
    if __name__ == "__main__"
    m = multiprocessing.Manager()
    dic = m.dict()
    process_list[]
    for i in range(10):
    p = multiprocessing.Process(target=task,args=(i,cic))
    p.start()
    process_list.append(p)
    while True:
    count = 0
    for p in process_list:
    if not p.is_alive():#True是还在执行,Fasle是已执行完
    cunt +=1
    if count == len(process_list):
    break
    print(dic)
  7. 进程锁

    import time
    import multiprocessing
    lock = multiprocessing.RLock()
    def task(arg):
    print("鬼子来了")
    lock.acquire()
    time.sleep(2)
    print(arg)
    lock.release()
    if __name__ == "__main__":
    p1 = multiprocessing.Process(target=task,args=1)
    p1.start()
    p2 = multiprocessing.Process(target=task,args=1)
    p2.start()

1.进程池

  1. 基本使用

    import time
    from comcurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    # #############################线程池
    def task (arg):
    time.sleep(2)
    print(arg)
    if __name__ == "__main__":
    pool = ThreadPoolExecutor(5)
    for i in range(10):
    pool.submit(task,i)
    # #############################进程池
    def task (arg):
    time.sleep(2)
    print(arg)
    if __name__ == "__main__":
    pool = ProcessPoolExecutor(5)
    for i in range(10):
    pool.submit(task,i)

3.3 协程

  1. 进程线程,操作系统中存在

  2. 协程,是有程序员创造出来的一个不是真实存在的东西;是微线程,对一个线程进程分片,是的线程在代码块之间进行来回切换执行,而不是在原来逐行执行。

    pip install gevent
    pip install greenlet
    import greenlet
    def f1():
    print(11)
    #跳到f2执行f2函数
    gr2.switch()
    print(22)
    def f2():
    print(33)
    #跳到f1执行f1函数
    gr1.switch()
    print(44)
    #协程 gr1
    gr1 = greenlet.greenlet(f1)
    gr2 = greenlet.greenlet(f2)
    gr1.switch()
  3. IO多路复用+协程

    from gevent import monkey
    monkey.patch_all()#以后代码中遇到IO都会自动执行greenlet的switch进行切换
    import requests
    import gevent
    def get_page(url):
    ret = requests.get(url)
    print(url,ret.content)
    gevent.joinall([gevent.spawn(get_page,"https://www.python.org/")]) #一个协程
  4. 总结

    1. 什么是协程?协程也可以成为微线程,就是开发者控制线程执行流程,控制先执行某段代码然后切换到另外执行代码。

    2. 协程可以调高并发吗?执行自己本身无法实现并发,协程+IO切换性能提高。

    3. 进程、线程、协程,协程+IO切换:基于事件循环的异步非阻塞框架:Twisted、手动实现协程:yield关键字生成器

      def f1():
      print(11)
      #第一次执行到这卡住再将1赋给send
      x1 = yield 1
      #接收v1.send(999)的值赋给x1
      print(x1,22)
      #返回一个二
      yield 2
      print(33)
      def f2():
      print(44)
      yield
      print(55)
      yield
      print(66)
      #第一次执行接受yield返回的值赋给ret
      ret = v1.send(None)
      pritn(v1)
      #发送一个值,接受一个值
      v = v1.send(999)

4.快用锁呀

  1. 线程安全,多线程操作时,内部会让所有线程排队处理。如:list/dict/Queue

  2. 需求,创建100个线程:锁,把自己的添加到列表中,在读取列表的最后一个;解锁。

1 lock

  1. 基本使用

    import threading
    import time
    v = []
    #创建锁
    lock = threading.Lock()
    def func(arg):
    #上锁
    lock.acquire()
    v.append(arg)
    time.sleep(0.01)
    m = v[-1]
    print(arg,m)
    #解锁
    lock.release()
    for i in range(10):
    t = treading.Thread(target=func,args=(i,))
    t.start()

2 RLock

  1. 基本使用

    import threading
    import time
    v = []
    #创建锁
    lock = threading.RLock()
    def func(arg):
    #上锁
    lock.acquire()
    v.append(arg)
    time.sleep(0.01)
    m = v[-1]
    print(arg,m)
    #解锁
    lock.release()
    for i in range(10):
    t = treading.Thread(target=func,args=(i,))
    t.start()

3 信号量

  1. 基本使用

    import threading
    import time
    v = []
    #创建锁
    lock = threading.Lock()
    #一次执行多个
    lock = threading.BoundedSemaphore(3)
    def func(arg):
    #上锁
    lock.acquire()
    v.append(arg)
    time.sleep(0.01)
    m = v[-1]
    print(arg,m)
    #解锁
    lock.release()
    for i in range(10):
    t = treading.Thread(target=func,args=(i,))
    t.start()

4 Condition

  1. 基本使用

    import threading
    import time
    v = []
    #创建锁
    lock = threading.Condition()
    #一次执行多个
    lock = threading.BoundedSemaphore(3)
    def func(arg):
    #上锁
    lock.acquire()
    v.append(arg)
    time.sleep(0.01)
    m = v[-1]
    print(arg,m)
    for i in range(10):
    t = treading.Thread(target=func,args=(i,))
    t.start()
    while True:
    inp = int(input(">>>"))
    lock.release() #解锁
    lock.release()
    ##########################2
    #一次执行多个
    def xxx():
    print("111")
    input(">>>")
    return True
    def func(arg):
    print("222")
    lok.wait_for(xxx)#锁住了,只有条件为True,才往下走1,一个
    print(arg)
    time.sleep(1)
    for i in range(10):
    t = treading.Thread(target=func,args=(i,))
    t.start()

5 Event

  1. 基本使用

    import time
    import threading
    lock = threading.Event()
    def func(arg):
    print('线程来了')
    lock.wait() # 加锁:红灯
    print(arg)
    for i in range(10):
    t =threading.Thread(target=func,args=(i,))
    t.start()
    input(">>>>")
    lock.set() # 绿灯
    lock.clear() # 再次变红灯
    for i in range(10):
    t =threading.Thread(target=func,args=(i,))
    t.start()
    input(">>>>")
    lock.set()

4.发送请求

  1. 基本使用

    import socket
    import sequests
    #方式一
    ret = requests.get("https://www.baidu.com/s?wd=alex")
    #方式二
    cloent = socket.socket()
    #和百度创建连接:阻塞
    client.connect(("www.baidu.com",80))
    #问百度你要什么?
    client.sendall(b"GET /s?wd=alex http/1.1\r\nhost:www.baidu.com\r\n\r\n")
    #我等着百度给我的回复
    chunk_list = []
    while True:
    chunk = client.recv(1024)
    if not chunk:
    break
    chunk_list.append(chunk)
    body = b"".join(chunk_list)
    print(body.decode("utf-8"))

5.非阻塞呀

  1. 基本使用

    import socket
    #方式二
    cloent = socket.socket()
    cloent.setblocking(False)#将原来阻塞的变成非阻塞
    #和百度创建连接:阻塞
    try:
    client.connect(("www.baidu.com",80))
    #问百度你要什么?
    except BlockingIOError as e:
    pass
    client.sendall(b"GET /s?wd=alex http/1.1\r\nhost:www.baidu.com\r\n\r\n")
    #我等着百度给我的回复
    chunk_list = []
    while True:
    chunk = client.recv(1024)#将原来阻塞的变成非阻塞
    if not chunk:
    break
    chunk_list.append(chunk)
    body = b"".join(chunk_list)
    print(body.decode("utf-8"))
    ###########################################
    #单线程的并发
    import socket
    import select
    #方式二
    cloent = socket.socket()
    cloent.setblocking(False)#将原来阻塞的变成非阻塞
    #和百度创建连接:阻塞
    try:
    client.connect(("www.baidu.com",80))
    #问百度你要什么?
    except BlockingIOError as e:
    pass
    socket_list = [client1,client2,client3]
    conn_list = [client1,client2,client3]
    while True:
    rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)
    #如果有数据返回就把他放到rlist,如果有连接成功就把他放到wlist,如果有异常就把他放到[]中
    #socket_list是检测是否服务端给我返回数据了 可读
    #conn_list检测其中的所有socket是否已经和服务端连接成功 可写
    #[]获取异常
    #0.005是最多等0.005秒
    for sk in wlist:
    if sk == client1:
    sk.sendall(b"GET /s?wd=alex http/1.1\r\nhost:www.baidu.com\r\n\r\n")
    else:
    sk.sendall(b"GET /s?wd=alex http/1.1\r\nhost:www.baidu.com\r\n\r\n")
    for sk in rlist:
    chunk_list = []
    while True:
    try:
    chunk = sk.recv(8096)
    if not chunk:
    break
    chunk_list.append(chunk)
    except BlockingIOError as e:
    break
    body = b"".join(chunk_list)
    print(body.decode("utf-8"))
    sk.close()
    socket_list.remove(sk)
    if not socket_list:
    break
  2. IO多路复用作用:检测多个socket是否已经发生变化(是否已经连接完成/是否已经获取数据)(可读/可写)

  3. IO多路复用,socket的会堵塞、Twisted ,基于事件循环实现的异步非堵塞框架,异步:执行完某个人物后自动调用我给他的函数。

    #单线程的并发
    import socket
    import select
    #方式二
    cloent = socket.socket()
    cloent.setblocking(False)#将原来阻塞的变成非阻塞
    #和百度创建连接:阻塞
    try:
    client.connect(("www.baidu.com",80))
    #问百度你要什么?
    except BlockingIOError as e:
    pass
    class Foo(object):
    def __init__(self,sk):
    self.sk = sk
    def fileno(self):
    return self.sk.fileno()
    """
    1.select.select(socket_list,conn_list,[],0.005)
    select监听的socket_list/conn_list内部会调用列表中每一个值的fileno方法,获取该返回值并去系统中检测。
    2.方式一:
    select.select([client1,])
    """
    socket_list = [Foo(client1),Foo(client2),Foo(client3)]
    conn_list = [client1,client2,client3]
    while True:
    rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)
    #如果有数据返回就把他放到rlist,如果有连接成功就把他放到wlist,如果有异常就把他放到[]中
    #socket_list是检测是否服务端给我返回数据了 可读
    #conn_list检测其中的所有socket是否已经和服务端连接成功 可写
    #[]获取异常
    #0.005是最多等0.005秒
    for sk in wlist:
    if sk == client1:
    sk.sendall(b"GET /s?wd=alex http/1.1\r\nhost:www.baidu.com\r\n\r\n")
    else:
    sk.sendall(b"GET /s?wd=alex http/1.1\r\nhost:www.baidu.com\r\n\r\n")
    for sk in rlist:
    chunk_list = []
    while True:
    try:
    chunk = sk.recv(8096)
    if not chunk:
    break
    chunk_list.append(chunk)
    except BlockingIOError as e:
    break
    body = b"".join(chunk_list)
    print(body.decode("utf-8"))
    sk.close()
    socket_list.remove(sk)
    if not socket_list:
    break
  4. 实现非阻塞

    import socket
    import select
    class Req(object):
       def __init__(self,sk,func):
           self.sock = sk
           self.func = func
       def fileno(self):
           return self.sock.fileno()
    class Nb(object):
       def __init__(self):
           self.conn_list = []
           self.socket_list = []
       def add(self,url,func):
           client = socket.socket()
           client.setblocking(False)  # 非阻塞
           try:
               client.connect((url, 80))
           except BlockingIOError as e:
               pass
           obj = Req(client,func)
           self.conn_list.append(obj)
           self.socket_list.append(obj)
       def run(self):
           while True:
               rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005)
               # wlist中表示已经连接成功的req对象
               for sk in wlist:
                   # 发生变换的req对象
                   sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
                   self.conn_list.remove(sk)
               for sk in rlist:
                   chunk_list = []
                   while True:
                       try:
                           chunk = sk.sock.recv(8096)
                           if not chunk:
                               break
                           chunk_list.append(chunk)
                       except BlockingIOError as e:
                           break
                   body = b''.join(chunk_list)
                   # print(body.decode('utf-8'))
                   sk.func(body)
                   sk.sock.close()
                   self.socket_list.remove(sk)
               if not self.socket_list:
                   break
    def baidu_repsonse(body):
       print('百度下载结果:',body)
    def sogou_repsonse(body):
       print('搜狗下载结果:', body)
    def oldboyedu_repsonse(body):
       print('老男孩下载结果:', body)
    t1 = Nb()
    t1.add('www.baidu.com',baidu_repsonse)
    t1.add('www.sogou.com',sogou_repsonse)
    t1.add('www.oldboyedu.com',oldboyedu_repsonse)
    t1.run()
  5. 总结

    1. socket默认是否阻塞的?阻塞体现在哪里?

    2. 如何让socket编程非阻塞?

    3. IO多路复用作用?

      • 检测多个socket是否发生变化。

      • 操作系统检测socket是否发生变化,有三种模式:

        • select:做多1024个socket;循环去检测。

        • poll:不限制监听socket个数;循环去检测(水平触发)。

        • epoll:不限制监听socket个数;回调方式(边缘触发)。

      • python模块:select.select、select.epoll

    4. 提高并发方案:多进程、多线程、异步非堵塞模块(Twisted)scrapy框架(单线程完成并发)

9.练习题呀

  1. 解释器和编译器的区别?编译型先把代码编译成机器码 , 计算机寄存器去运行、解释型边解释边执行。

  2. py2/py3

    • 打印,py2中print " xx"、py3中print(123)

    • 继承,py2中经典类/新式类、py3中新式类

    • 编码,py2中ascii可以修改#- * - encoding:utf-8 - * -、py3中utf-8可以修改#- * - encoding:utf-8 - * -

    • 字符串,py2中unicode v = u"root" 本质上用ynicode存储、(str/bytes) v = "root" 本质用字节存储、py3中str v = "root" 本质上用ynicode存储、bytes v = b"root" 本质用字节存储

    • 输入,py2中v1 = raw_input(">>>")、py3中v1 = input(">>>")

    • 范围,py2中range/xrange、py3中range

  3. 练习

    # Client
    import socket
    import struct
    sk = socket.socket()
    sk.connect(('127.0.0.1',8008))
    while 1:
    cmd = input("请输入命令:")
    sk.send(cmd.encode('utf-8')) # 字节
    if cmd=="":
    continue
    if cmd == 'exit':
    break
    # header_pack=sk.recv(4)
    # data_length=struct.unpack("i",header_pack)[0]
    # print("data_length",data_length)
    '''
    b'xxx/xxx/xxx/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
    '''
    data_length=int(sk.recv(1024).decode("utf8"))
    print("data_length",data_length)
    recv_data_length=0
    recv_data=b""
    while recv_data_length<data_length:
    data=sk.recv(1024)
    recv_data_length+=len(data)
    recv_data+=data
    print(recv_data.decode("gbk"))
    sk.close()
    # Server
    # by luffycity.com
    import socket
    import subprocess
    server = socket.socket()
    server.bind(('127.0.0.1',8008))
    server.listen(5)
    while True:
    print("server is working.....")
    conn,addr = server.accept()
    # 字节类型
    while True:
    # 针对window系统
    try:
    cmd = conn.recv(1024).decode("utf8") # 阻塞
    if cmd == b'exit':
    break
    res=subprocess.Popen(cmd,
    shell=True,
    stderr=subprocess.PIPE,
    stdout=subprocess.PIPE,
    )
    # print("stdout",res.stdout.read())
    # print("stderr",res.stderr.read().decode("gbk"))
    out=res.stdout.read()
    err=res.stderr.read()
    print("out响应长度",len(out))
    print("err响应长度",len(err))
    if err:
    import struct
    header_pack = struct.pack("i", len(err))
    conn.send(header_pack)
    conn.send(err)
    else:
    #构建报头
    import struct
    header_pack=struct.pack("i",len(out))
    print("header_pack",header_pack)
    # # 发送报头
    conn.send(str(len(out)).encode("utf8"))
    # 发送数据
    conn.send(out)
    except Exception as e:
    break
    conn.close()
  4. 练习

    # Server
    import struct
    import socket
    import json
    import hashlib
    sock=socket.socket()
    sock.bind(('127.0.0.1',8800))
    sock.listen(5)
    while 1:
    print("server is working....")
    conn,addr= sock.accept()
    while 1:
    # 接收json的打包长度
    file_info_length_pack=conn.recv(4)
    file_info_length=struct.unpack("i",file_info_length_pack)[0]
    # 接收json字符串
    file_info_json=conn.recv(file_info_length).decode("utf8")
    file_info=json.loads(file_info_json)
    action=file_info.get("action")
    filename=file_info.get("filename")
    filesize=file_info.get("filesize")
    # 循环接收文件
    md5=hashlib.md5()
    with open("put/"+filename,"wb") as f:
    recv_data_length=0
    while recv_data_length<filesize:
    data=conn.recv(1024)
    recv_data_length+=len(data)
    f.write(data)
    # MD5摘要
    md5.update(data)
    print("文件总大小:%s,已成功接收%s"%(filesize,recv_data_length))
    print("接收成功!")
    conn.send(b"OK")
    print(md5.hexdigest())
    md5_val=md5.hexdigest()
    client_md5=conn.recv(1024).decode("utf8")
    if md5_val==client_md5:
    conn.send(b"203")
    else:
    conn.send(b"204")
    # Client
    import socket
    import os
    import json
    import struct
    import hashlib
    sock=socket.socket()
    sock.connect(("127.0.0.1",8800))
    while 1 :
    cmd=input("请输入命令:") # put 111.jpg
    action,filename=cmd.strip().split(" ")
    filesize=os.path.getsize(filename)
    file_info={
    "action": action,
    "filename": filename,
    "filesize": filesize,
    }
    file_info_json=json.dumps(file_info).encode("utf8")
    ret=struct.pack("i",len(file_info_json))
    # 发送 file_info_json的打包长度
    sock.send(ret)
    # 发送 file_info_json字节串
    sock.send(file_info_json)
    # 发送 文件数据
    md5=hashlib.md5()
    with open(filename,"rb") as f:
    for line in f:
    sock.send(line)
    md5.update(line)
    data=sock.recv(1024)
    print(md5.hexdigest())
    md5_val=md5.hexdigest()
    sock.send(md5_val.encode("utf8"))
    is_valid=sock.recv(1024).decode('utf8')
    if is_valid=="203":
    print("文件完整!")
    else:
    print("文件上传失败!")
  5. 进度条

    import time
    def func(size,total_size):
    val = int(size/total_size * 100)
    time.sleep(0.2)
    print("\r%s%%|%" %(val,"*"*val,),end="")
    for i in range(100):
    func(i,100)
  6. 读取文件大小

    import os
    size = os.stat(r"sadf\asdf\d\").st_size
    print(size)

爬虫初级

1.requests

1.1 安装

pip install requests # 爬虫

1.2 使用

  1. 获取html代码

    import requests
    response = requests.get("https://www.autobone.com.cn/news/") #获取数据代码
    response.encoding = "gbk" #指定获取后的数据编码
    print(response.text) #查看文本信息
  2. 进行数据的筛选

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(response.text,"html.parser") #解析为soup的对象
    div = soup.find(name="div",attrs={"id":"auto-channel-latyload-article"}) #筛选匹配的第一个字符
    #name,标签。attrs标签属性
    li_list = div.find_all(name="li") #筛选匹配的所有字符
    for li in li_list:
    a = li.find(name="a")
    print(a.attrs.get("href")) #获取标签指定的属性值

1.3 headers

  1. user-agent

    r1 = requests.get(
    url='https://dig.chouti.com/',
    #放置请求头的信息
    #user-agent 用户设备信息
    headers={
    'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
    }
    )

1.4 data and cookies

  1. data

    #发送post请求
    r2 = requests.post(
    url='。。。',
    #data请求体中的数据
    data={
    'phone':'xxx',
    'password':'xxx',
    },
    )
  2. cookies

    r1 = requests.get(
    url='。。。',
    )
    r2 = requests.post(
    url='https://dig.chouti.com/login',
    #存放要发送的cookies
    #get_dict()是查看cookies
    cookies=r1.cookies.get_dict()
    )

1.5 请求与参数

  1. requests的一些请求

    requests.get(url, params=None, **kwargs)
    requests.post(url, data=None, json=None, **kwargs)
    requests.put(url, data=None, **kwargs)
    requests.head(url, **kwargs)
    requests.delete(url, **kwargs)
    requests.patch(url, data=None, **kwargs)
    requests.options(url, **kwargs)
    # 以上方法均是在此方法的基础上构建
    #引号中写什么就会使用什么请求。
    requests.request(method, url, **kwargs)
  2. 参数

    def param_method_url():
    # requests.request(method='get', url='http://127.0.0.1:8000/test/')
    # requests.request(method='post', url='http://127.0.0.1:8000/test/')
    pass
    def param_param():
    #param是在url后面跟的数据
    # - 可以是字典、可以是字符串、可以是字节(ascii编码以内)
    # requests.request(method='get',url='http://127.0.0.1:8000/test/',
    # params={'k1': 'v1', 'k2': '水电费'})
    # requests.request(method='get',url='http://127.0.0.1:8000/test/',
    # params="k1=v1&k2=水电费&k3=v3&k3=vv3")
    # requests.request(method='get',url='http://127.0.0.1:8000/test/',
    # params=bytes("k1=v1&k2=k2&k3=v3&k3=vv3", encoding='utf8'))
    # 错误
    # requests.request(method='get',url='http://127.0.0.1:8000/test/',
    # params=bytes("k1=v1&k2=水电费&k3=v3&k3=vv3", encoding='utf8'))
    pass
    def param_data():#传请求体
    # 可以是字典、可以是字符串、可以是字节、可以是文件对象
    #data的请求格式
    #GET /index http1.1\r\nhost:c1.com\r\n\r\nuser=alex&pwd=123
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',
    # data={'k1': 'v1', 'k2': '水电费'})
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',
    # data="k1=v1; k2=v2; k3=v3; k3=v4")
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',
    # data="k1=v1;k2=v2;k3=v3;k3=v4",
    # headers={'Content-Type': 'application/x-www-form-urlencoded'})
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',
    # data=open('data_file.py', mode='r', encoding='utf-8'), # 文件内容是:k1=v1;k2=v2;k3=v3;k3=v4
    # headers={'Content-Type': 'application/x-www-form-urlencoded'})
    def param_json():#传请求体
    #json的请求格式,并且会多带一个Content-Type:application/json用来说明
    #GET /index http1.1\r\nhost:c1.com\r\nContent-Type:application/json\r\n\r\n{"user":"alex","pwd":123}
    # 将json中对应的数据进行序列化成一个字符串,json.dumps(...)
    # 然后发送到服务器端的body中,并且Content-Type是 {'Content-Type': 'application/json'}
    requests.request(method='POST',url='http://127.0.0.1:8000/test/',json={'k1': 'v1', 'k2': '水电费'})
    def param_headers():
    # 发送请求头到服务器端
    requests.request(method='POST',url='http://127.0.0.1:8000/test/',json={'k1': 'v1', 'k2': '水电费'},headers={'Content-Type': 'application/x-www-form-urlencoded'})
    def param_cookies():
    # 发送Cookie到服务器端
    requests.request(method='POST',url='http://127.0.0.1:8000/test/',
    data={'k1': 'v1', 'k2': 'v2'},cookies={'cook1': 'value1'},)
    # 也可以使用CookieJar(字典形式就是在此基础上封装)
    from http.cookiejar import CookieJar
    from http.cookiejar import Cookie
    obj = CookieJar()
    obj.set_cookie(Cookie(version=0, name='c1', value='v1', port=None, domain='', path='/', secure=False, expires=None,
    discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False,
    port_specified=False, domain_specified=False, domain_initial_dot=False, path_specified=False)
    )
    requests.request(method='POST',url='http://127.0.0.1:8000/test/',data={'k1': 'v1', 'k2': 'v2'},cookies=obj)
    def param_files():
    # 发送文件
    # file_dict = {'f1': open('readme', 'rb')}
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',files=file_dict)
    # 发送文件,定制文件名
    # file_dict = {'f1': ('test.txt', open('readme', 'rb'))}
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',files=file_dict)
    # 发送文件,定制文件名
    # file_dict = {'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf")}
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',files=file_dict)
    # 发送文件,定制文件名
    # file_dict = {'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf", 'application/text', {'k1': '0'})}
    # requests.request(method='POST',url='http://127.0.0.1:8000/test/',files=file_dict)
    pass
    def param_auth():
    #认证,是在发送前会通过base64加密,就需要auth来发送了。
    #Authorization: "basic base64("用户|密码")",请求头中的结构。
    from requests.auth import HTTPBasicAuth, HTTPDigestAuth
    ret = requests.get('https://api.github.com/user', auth=HTTPBasicAuth('wupeiqi', 'sdfasdfasdf'))
    ret = requests.get('http://httpbin.org/digest-auth/auth/user/pass', auth=HTTPDigestAuth('user', 'pass'))
    def param_timeout():
    #超时,第一个参数是发送超时时间,第二个参数是接受超时时间。
    # ret = requests.get('http://google.com/', timeout=1)
    # ret = requests.get('http://google.com/', timeout=(5, 1))
    def param_allow_redirects():
    #是否允许重定向,就是访问一个网站会跳到另一个网站,是否是否允许跳转。
    ret = requests.get('http://127.0.0.1:8000/test/', allow_redirects=False)
    def param_proxies():
    #代理,通过是http或https,使用不同的代理。
    # proxies = {"http": "61.172.249.96:80","https": "http://61.185.219.126:3128",}
    # ret = requests.get("http://www.proxy360.cn/Proxy", proxies=proxies)
    # print(ret.headers)
    # from requests.auth import HTTPProxyAuth
    # proxyDict = {'http': '77.75.105.165','https': '77.75.105.165'}
    # auth = HTTPProxyAuth('username', 'mypassword')
    # r = requests.get("http://www.google.com", proxies=proxyDict, auth=auth)
    pass
    def param_stream():
    #大文件下载,就是把大的文件分成多分接收。
    ret = requests.get('http://127.0.0.1:8000/test/', stream=True)
    print(ret.content)
    ret.close()
    # from contextlib import closing
    # with closing(requests.get('http://httpbin.org/get', stream=True)) as r:
    # # 在此处理响应。
    # for i in r.iter_content():
    # print(i)
    def requests_session():
    #第二次访问时就不需要携带cookie了。
    import requests
    session = requests.Session()
    ### 1、首先登陆任何页面,获取cookie
    i1 = session.get(url="http://dig.chouti.com/help/service")
    ### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
    i2 = session.post(url="http://dig.chouti.com/login",
    data={'phone': "8615131255089",'password': "xxxxxx",'oneMonth': ""})
    i3 = session.post(url="http://dig.chouti.com/link/vote?linksId=8589623",)
  3. 其他参数

    def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.
    :param method: method for the new :class:`Request` object.
    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
    :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
    :param json: (optional) json data to send in the body of the :class:`Request`.
    :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
    :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
    :param files: (optional) Dictionary of 'name': file-like-objects (or {'name': file-tuple}) for multipart encoding upload.
    file-tuple can be a 2-tuple ('filename', fileobj), 3-tuple ('filename', fileobj, 'content_type')
    or a 4-tuple ('filename', fileobj, 'content_type', custom_headers), where 'content-type' is a string
    defining the content type of the given file and custom_headers a dict-like object containing additional headers
    to add for the file.
    :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
    :param timeout: (optional) How long to wait for the server to send data
    before giving up, as a float, or a :ref:`(connect timeout, read
    timeout) <timeouts>` tuple.
    :type timeout: float or tuple
    :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
    :type allow_redirects: bool
    :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
    :param verify: (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to True.
    :param stream: (optional) if False, the response content will be immediately downloaded.
    :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    Usage::
    >>> import requests
    >>> req = requests.request('GET', 'http://httpbin.org/get')
    <Response [200]>
    """

     

2.BeautifulSoup

  1. BeautifulSoup是一个模块,该模块用于接收一个HTML或XML字符串,然后将其进行格式化,之后遍可以使用他提供的方法进行快速查找指定元素,从而使得在HTML或XML中查找指定元素变得简单。

    from bs4 import BeautifulSoup
    html_doc = """
    <html>
    <head>
    <title>The Dormouse's story</title>
    </head>
    <body>
    asdf
    <div class="title">
    <b>The Dormouse's story总共</b>
    <h1>f</h1>
    </div>
    <div class="story">Once upon a time there were three little sisters; and their names were
    <a class="sister0" id="link1">Els<span>f</span>ie</a>,
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;and they lived at the bottom of a well.
    </div>
    ad<br/>sf
    <p class="story">...</p>
    </body>
    </html>
    """
    soup = BeautifulSoup(html_doc, features="lxml")
    # 找到第一个a标签tag1 = soup.find(name='a')
    # 找到所有的a标签tag2 = soup.find_all(name='a')
    # 找到id=link2的标签 tag3 = soup.select('#link2')
  2. 安装:

    pip3 install beautifulsoup4

2.1 使用示例

from bs4 import BeautifulSoup
html_doc = """
<html>
<head>
<title>The Dormouse's story</title>
</head>
<body>
...
</body>
</html>
"""
soup = BeautifulSoup(html_doc, features="lxml")
  1. name,标签名称

    tag = soup.find('a')
    name = tag.name # 获取
    print(name)
    tag.name = 'span' # 设置
    print(soup)
  2. attr,标签属性

    tag = soup.find('a')
    attrs = tag.attrs # 获取
    print(attrs)
    tag.attrs = {'ik':123} # 设置
    tag.attrs['id'] = 'iiiii' # 设置
    print(soup)
  3. children,所有子标签

    body = soup.find('body')
    v = body.children
  4. children,所有子子孙孙标签

    body = soup.find('body')
    v = body.descendants
  5. clear,将标签的所有子标签全部清空(保留标签名)

    tag = soup.find('body')
    tag.clear()
    print(soup)
  6. decompose,递归的删除所有的标签

    body = soup.find('body')
    body.decompose()
    print(soup)
  7. extract,递归的删除所有的标签,并获取删除的标签

    body = soup.find('body')
    v = body.extract()
    print(soup)
  8. decode,转换为字符串(含当前标签);decode_contents(不含当前标签)

    body = soup.find('body')
    v = body.decode()
    v = body.decode_contents()
    print(v)
  9. encode,转换为字节(含当前标签);encode_contents(不含当前标签)

    body = soup.find('body')
    v = body.encode()
    v = body.encode_contents()
    print(v)
  10. find,获取匹配的第一个标签

    tag = soup.find('a')
    print(tag)
    tag = soup.find(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie')
    tag = soup.find(name='a', class_='sister', recursive=True, text='Lacie')
    print(tag)
  11. find_all,获取匹配的所有标签

    tags = soup.find_all('a')
    print(tags)
    tags = soup.find_all('a',limit=1)
    print(tags)
    tags = soup.find_all(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie')
    tags = soup.find(name='a', class_='sister', recursive=True, text='Lacie')
    print(tags)
    # ####### 列表 #######
    v = soup.find_all(name=['a','div'])
    print(v)
    v = soup.find_all(class_=['sister0', 'sister'])
    print(v)
    v = soup.find_all(text=['Tillie'])
    print(v, type(v[0]))
    v = soup.find_all(id=['link1','link2'])
    print(v)
    v = soup.find_all(href=['link1','link2'])
    print(v)
    # ####### 正则 #######
    import re
    rep = re.compile('p')
    rep = re.compile('^p')
    v = soup.find_all(name=rep)
    print(v)
    rep = re.compile('sister.*')
    v = soup.find_all(class_=rep)
    print(v)
    rep = re.compile('http://www.oldboy.com/static/.*')
    v = soup.find_all(href=rep)
    print(v)
    # ####### 方法筛选 #######
    def func(tag):
    return tag.has_attr('class') and tag.has_attr('id')
    v = soup.find_all(name=func)
    print(v)
    ## get,获取标签属性
    tag = soup.find('a')
    v = tag.get('id')
    print(v)
  12. has_attr,检查标签是否具有该属性

    tag = soup.find('a')
    v = tag.has_attr('id')
    print(v)
  13. get_text,获取标签内部文本内容

    tag = soup.find('a')
    v = tag.get_text('id')
    print(v)
  14. index,检查标签在某标签中的索引位置

    tag = soup.find('body')
    v = tag.index(tag.find('div'))
    print(v)
    tag = soup.find('body')
    for i,v in enumerate(tag):
    print(i,v)
  15. is_empty_element,是否是空标签(是否可以是空)或者自闭合标签,判断是否是如下标签:'br' , 'hr', 'input', 'img', 'meta','spacer', 'link', 'frame', 'base'.

    tag = soup.find('br')
    v = tag.is_empty_element
    print(v)
  16. 当前的关联标签

    soup.next
    soup.next_element
    soup.next_elements
    soup.next_sibling
    soup.next_siblings
    tag.previous
    tag.previous_element
    tag.previous_elements
    tag.previous_sibling
    tag.previous_siblings
    tag.parent
    tag.parents
  17. 查找某标签的关联标签

    tag.find_next(...)
    tag.find_all_next(...)
    tag.find_next_sibling(...)
    tag.find_next_siblings(...)
    tag.find_previous(...)
    tag.find_all_previous(...)
    tag.find_previous_sibling(...)
    tag.find_previous_siblings(...)
    tag.find_parent(...)
    tag.find_parents(...) # 参数同find_all
  18. select,select_one, CSS选择器

    soup.select("title")
    soup.select("p nth-of-type(3)")
    soup.select("body a")
    soup.select("html head title")
    tag = soup.select("span,a")
    soup.select("head > title")
    soup.select("p > a")
    soup.select("p > a:nth-of-type(2)")
    soup.select("p > #link1")
    soup.select("body > a")
    soup.select("#link1 ~ .sister")
    soup.select("#link1 + .sister")
    soup.select(".sister")
    soup.select("[class~=sister]")
    soup.select("#link1")
    soup.select("a#link2")
    soup.select('a[href]')
    soup.select('a[href="http://example.com/elsie"]')
    soup.select('a[href^="http://example.com/"]')
    soup.select('a[href$="tillie"]')
    soup.select('a[href*=".com/el"]')
    from bs4.element import Tag
    def default_candidate_generator(tag):
    for child in tag.descendants:
    if not isinstance(child, Tag):
    continue
    if not child.has_attr('href'):
    continue
    yield child
    tags = soup.find('body').select("a", _candidate_generator=default_candidate_generator)
    print(type(tags), tags)
    from bs4.element import Tag
    def default_candidate_generator(tag):
    for child in tag.descendants:
    if not isinstance (child, Tag):
    continue
    if not child.has_attr('href'):
    continue
    yield child
    tags = soup.find('body').select("a", _candidate_generator=default_candidate_generator, limit=1)
    print(type(tags), tags)
  19. 标签的内容

    tag = soup.find('span')
    print(tag.string) # 获取
    tag.string = 'new content' # 设置
    print(soup)
    tag = soup.find('body')
    print(tag.string)
    tag.string = 'xxx'
    print(soup)
    tag = soup.find('body')
    v = tag.stripped_strings # 递归内部获取所有标签的文本
    print(v)
  20. append在当前标签内部追加一个标签

    tag = soup.find('body')
    tag.append(soup.find('a'))
    print(soup)
    from bs4.element import Tag
    obj = Tag(name='i',attrs={'id': 'it'})
    obj.string = '我是一个新来的'
    tag = soup.find('body')
    tag.append(obj)
    print(soup)
  21. insert在当前标签内部指定位置插入一个标签

    from bs4.element import Tag
    obj = Tag(name='i', attrs={'id': 'it'})
    obj.string = '我是一个新来的'
    tag = soup.find('body')
    tag.insert(2, obj)
    print(soup)
  22. insert_after,insert_before 在当前标签后面或前面插入

    from bs4.element import Tag
    obj = Tag(name='i', attrs={'id': 'it'})
    obj.string = '我是一个新来的'
    tag = soup.find('body')
    tag.insert_before(obj)
    tag.insert_after(obj)
    print(soup)
  23. replace_with 在当前标签替换为指定标签

    from bs4.element import Tag
    obj = Tag(name='i', attrs={'id': 'it'})
    obj.string = '我是一个新来的'
    tag = soup.find('div')
    tag.replace_with(obj)
    print(soup)
  24. 创建标签之间的关系

    tag = soup.find('div')
    a = soup.find('a')
    tag.setup(previous_sibling=a)
    print(tag.previous_sibling)
  25. wrap,将指定标签把当前标签包裹起来

    from bs4.element import Tag
    obj1 = Tag(name='div', attrs={'id': 'it'})
    obj1.string = '我是一个新来的'
    tag = soup.find('a')
    v = tag.wrap(obj1)
    print(soup)
    tag = soup.find('a')
    v = tag.wrap(soup.find('p'))
    print(soup)
  26. unwrap,去掉当前标签,将保留其包裹的标签

    tag = soup.find('a')
    v = tag.unwrap()
    print(soup)

3.补充

3.1约束

  • 用于调用此类时需要调用此方法,不然就无法使用。

    import abc
    class Base(metaclass=abc.ABCMeta):
    @abc.abstractmethod #标记
    def send(self):pass
    def func(self):
    print("...")
    class Foo(Base): #继承此类后需要自己定义这个被标记的方法,不然无法运行。
    def send(self):
    print("...")
    obj = Foo()

     

4.练习

4.1 1.2/1~2

import requests
from bs4 import BeautifulSoup
def Reptile():
response = requests.get("https://www.autohome.com.cn/news/")
response.encoding = "gbk"
print(response.text)
soup = BeautifulSoup(response.text, "html.parser")
div = soup.find(name="div", attrs={"id": "auto-channel-lazyload-article"})
li_list = div.find_all(name="li")
for li in li_list:
title = li.find(name="h3")
if not title:
continue
p = li.find(name="p")
a = li.find(name="a")
print(title.text)
print(a.attrs.get("href"))
print(p.text)
img = li.find(name="img")
src = img.get("src")
print(src)
try:
# 再次发起请求,下载照片
file_name = src.rsplit("/", maxsplit=1)[1]
print(file_name)
ret = requests.get(src)
with open(file_name, "wb") as f:
f.write(ret.content)
except Exception as e:
continue
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
Reptile()

4.2 1.3

import requests
from bs4 import BeautifulSoup
r1 = requests.get(
url='https://dig.chouti.com/',
#放置请求头的信息
#user-agent 用户设备信息
headers={
'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
)
soup = BeautifulSoup(r1.text,'html.parser')
# 标签对象
content_list = soup.find(name='div',id='content-list')
# print(content_list)
# [标签对象,标签对象]
item_list = content_list.find_all(name='div',attrs={'class':'item'})
for item in item_list:
a = item.find(name='a',attrs={'class':'show-content color-chag'})
print(a.text.strip())
# print(a.text)

4.3 1.3|1.4

import requests
# 1. 查看首页
r1 = requests.get(
url='https://dig.chouti.com/',
headers={
'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
)
# 2. 提交用户名和密码
r2 = requests.post(
url='https://dig.chouti.com/login',
headers={
'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
},
#需要登陆请求体中需要携带,用户名和密码
data={
'phone':'8613121758648',
'password':'woshiniba',
'oneMonth':1
},
#应为抽屉的方式是通过,第一次访问在cookies里有一个随机数,只有在登陆时携带才可以。
cookies=r1.cookies.get_dict()
)
# 3. 点赞
r3 = requests.post(
url='https://dig.chouti.com/link/vote?linksId=20435396',
headers={
'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
},
#将用户登陆后的cookies携带
cookies=r1.cookies.get_dict()
)
print(r3.text)

4.4 1.2

import requests
from bs4 import BeautifulSoup
# ############## 方式一 ##############
#
# # 1. 访问登陆页面,获取 authenticity_token
i1 = requests.get('https://github.com/login')
soup1 = BeautifulSoup(i1.text, features='lxml')
tag = soup1.find(name='input', attrs={'name': 'authenticity_token'})
authenticity_token = tag.get('value')
c1 = i1.cookies.get_dict()
i1.close()
#
# # 1. 携带authenticity_token和用户名密码等信息,发送用户验证
form_data = {
"authenticity_token": authenticity_token,
   "utf8": "",
   "commit": "Sign in",
   "login": "wupeiqi@live.com",
   'password': 'xxoo'
}
#
i2 = requests.post('https://github.com/session', data=form_data, cookies=c1)
c2 = i2.cookies.get_dict()
c1.update(c2)
i3 = requests.get('https://github.com/settings/repositories', cookies=c1)
soup3 = BeautifulSoup(i3.text, features='lxml')
list_group = soup3.find(name='div', class_='listgroup')
#
from bs4.element import Tag
#
for child in list_group.children:
   if isinstance(child, Tag):
       project_tag = child.find(name='a', class_='mr-1')
       size_tag = child.find(name='small')
       temp = "项目:%s(%s); 项目路径:%s" % (project_tag.get('href'), size_tag.string, project_tag.string, )
       print(temp)

爬虫中级

1.scrapy

1.安装

  • 安装在windows环境下时,需要安装twisted、pywin32、scrapy。

    pip install twisted
    pip install pywin32
    pip install scrapy

1.1 基本认识

  1. 创建项目、创建爬虫、启动爬虫。

    #create a project and add a project name.
    scrapy startproject project_name
    #创建文件并设置姓名和要爬取网站的url“taobao.com”
    scrapy genspider name url
    #启动爬虫(加--nolog就不会显示日志)
    scrapy crawl name
    scrapy crawl name --nolog
  2. 认识默认创建文件夹的工作

    - project name
    - project name
    - spiders #爬虫文件夹
    - items.py #持久化
    - middlewares.py #中间件
    - pipelines #持久化
    - settings.py #配置文件(爬虫相关)
    - scrapy.cfg #配置文件(部署相关)
  3. 认识爬虫文件

    import scrapy
    #这是操作刚爬来的数据做操作,里面有更多方法可以使用
    from scrapy.http.response.html import HtmlResponse
    #如果运行不起来爬虫文件时,可以试试
    import sys,os,io
    sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding="gb18030")
    class ChoutiSpider(scrapy.Spider):
    name = "chouti" #这是爬虫文件的名称
    allowed_domains = ["chouti.com"] #定向爬虫,只爬这个网站,爬不到就撤退
    start_urls = ["http://chouti.com/"] #开始url,最开始要爬的url地址
    def parse(self,response): #这是默认创建的方法,response返回爬回来的数据。
    f = open("news.log",mode="a+")
    #.xpath用于筛选,//代表全页面寻找子子孙孙,.//代表当前标签寻找子子孙孙,/找自己所有的儿子。
    item_list = response.xpath('//div[@class="link-con"]/div')
    for item in item_list:
    #text()代表获取文本内容,extract_first()代表获取第一个标签,@href代表获取属性的href。
    text = item.xpath('.//div[@class="link-detail"]/a/text()').extract_first()
    href = item.xpath('.//div[@class="link-detail"]/a/@href').extract_first()
    print(text.strip())
    f.write(href+"\n")
    f.close()
    #extract()用于转为需要的数据
    page_list = response.xpath('//div[@id="dig-lcpage"]//a/@href').extract()
    for page in page_list:
    from scrapy.http import Request
    page = "https://dig.chouti.com"+page
    #将获得的url拼接并转为request,每次递归访问函数parse,再将数据交给。
    yield Request(url=page,callback=self.parse)
  4. 分工合作

    pipelines.py
    from itemadapter import ItemAdapter
    from scrapy.exceptions import DropItem
    class AaPipeline:
    def __init__(self,path):
    self.f = None
    self.path =path
    @classmethod
    def from_crawler(cls,crawler):
    """初始化时候,用于创建pipeline对象"""
    path = crawler.settings.get("HREF_FILE_PATH") #在所有配置文件中寻找
    return cls(path)
    def open_spider(self,spider):
    """爬虫开始时执行"""
    self.f = open(self.path,"a+")
    def process_item(self, item, spider): #这个方法不能直接使用,需要和items一起使用。
    self.f.write(item["url"]+"\n")
    return item #如果你不返回下一次就拿不到数据的,下一次就无法处理
    raise DropItem() #不想让他处理可以直接这样写
    def close_spider(self,spider):
    """爬虫结束时执行"""
    self.f.close()
    items.py
    import scrapy
    class AaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title= scrapy.Field() #需要将其定义
    url = scrapy.Field()
    settings.py
    ITEM_PIPELINES = { #这个默认是注释掉的,取消注释后pipelines才可以用。
    'aa.pipelines.AaPipeline': 300,
    }
    HREF_FILE_PATH = "www.log"
    spiders/chouti.py
    import scrapy
    from aaa.items import AaItem #调用那个类
    class ChoutiSpider(scrapy.Spider):
    name = "chouti"
    allowed_domains = ["chouti.com"]
    start_urls = ["http://chouti.com/"]
    def parse(self,response):
    item_list = response.xpath('//div[@class="link-con"]/div')
    for item in item_list:
    text = item.xpath('.//div[@class="link-detail"]/a/text()').extract_first()
    href = item.xpath('.//div[@class="link-detail"]/a/@href').extract_first()
    print(text.strip())
    yield AaItem(title=text,url=href) #将数据传入刚定义的方法中
    page_list = response.xpath('//div[@id="dig-lcpage"]//a/@href').extract()
    for page in page_list:
    from scrapy.http import Request
    page = "https://dig.chouti.com"+page
    yield Request(url=page,callback=self.parse)

1.2 去重和深度

  1. 在请求的时候会重复出现同一个url,scrapy默认已经提供了,我们可以做一个一样的来学习。

    settings.py
    #默认指向scrapy的,我们将其改成我们的。
    #DUPEFILTER_CLASS = "scrapy.dupefilter.RFPDupeFilter"
    DUPEFILTER_CLASS = "aaa.dupefilter.RFPDupeFilter"
    DEPTH_LIMIT = 3 #限制深度为3
    dupefilters.py
    from scrapy.dupefilter import BaseDupeFilter
    #url有的长有的短,有些url后面的值顺序调转一下md5值就不一样,我们只能用他了。
    from scrapy.utils.request import request_fingerprint
    class AaaDupeFilter(BaseDupeFilter):
    def __init__(self):
    self.visited_fd = set() #初始化一个集合
    @classmethod
    def from_settings(cls,settings):
    return cls() #调用自己
    def request_seen(self,request):
    fd = request_fingerprint(request=request) #将url转为指定长度的值
    if fd in self.visited_fd: #判断是否被请求过
    return True
    self.visited_fd.add(fd) #没有请求果就添加一下
    def open(self):
    print("开始")
    def close(self,reason):
    print("结束")
    def log(self,request,spider):
    print("日志")
    spiders/chouti.py
    import scrapy
    from aaa.items import AaItem #调用那个类
    class ChoutiSpider(scrapy.Spider):
    name = "chouti"
    allowed_domains = ["chouti.com"]
    start_urls = ["http://chouti.com/"]
    def parse(self,response):
    print(response.request.url,response.meta.get("depth",0)) #查看当前请求的url,和深度
    item_list = response.xpath('//div[@class="link-con"]/div')
    for item in item_list:
    text = item.xpath('.//div[@class="link-detail"]/a/text()').extract_first()
    href = item.xpath('.//div[@class="link-detail"]/a/@href').extract_first()
    print(text.strip())
    page_list = response.xpath('//div[@id="dig-lcpage"]//a/@href').extract()
    for page in page_list:
    from scrapy.http import Request
    page = "https://dig.chouti.com"+page
    #dont_filter=True时去去重无效
    yield Request(url=page,callback=self.parse,dont_filter=True)

1.3 携带cookie

  1. 携带cookie做抽屉登录

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.http.response.html import HtmlResponse
    from xdb.items import XdbItem
    import scrapy
    from scrapy.http.cookies import CookieJar
    from scrapy.http import Request
    from urllib.parse import urlencode
    class ChoutiSpider(scrapy.Spider):
    name = 'chouti'
    allowed_domains = ['chouti.com']
    start_urls = ['https://dig.chouti.com/']
    cookie_dict = {} #做一个全局字典
    def parse(self, response):
    # 去响应头中获取cookie,cookie保存在cookie_jar对象
    cookie_jar = CookieJar()
    cookie_jar.extract_cookies(response, response.request)
    # 去对象中将cookie解析到字典
    for k, v in cookie_jar._cookies.items():
    for i, j in v.items():
    for m, n in j.items():
    self.cookie_dict[m] = n.value
    yield Request(
    url='https://dig.chouti.com/login',
    method='POST',
    body="phone=8613121758648&password=woshiniba&oneMonth=1",
    cookies=self.cookie_dict, #将cookies携带
    headers={ #设置请求头
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    },
    callback=self.check_login #写一个回调函数
    )
    def check_login(self,response):
    print(response.text)
    yield Request(
    url='https://dig.chouti.com/all/hot/recent/1',
    cookies=self.cookie_dict,
    callback=self.index
    )
    def index(self,response): #寻找到新闻并点赞
    news_list = response.xpath('//div[@id="content-list"]/div[@class="item"]')
    for new in news_list:
    link_id = new.xpath('.//div[@class="part2"]/@share-linkid').extract_first()
    yield Request(
    url='http://dig.chouti.com/link/vote?linksId=%s' % (link_id,),
    method='POST',
    cookies=self.cookie_dict,
    callback=self.check_result
    )
    page_list = response.xpath('//div[@id="dig_lcpage"]//a/@href').extract()
    for page in page_list:
    page = "https://dig.chouti.com" + page
    yield Request(url=page, callback=self.index) # https://dig.chouti.com/all/hot/recent/2
    def check_result(self, response):
    print(response.text)

1.4 起始ruls

  1. 起始urls内部原理:scrapy引擎来爬虫中取起始url,先调用start_request并获取返回值,返回值是一个迭代器需要(iter()一下),再使用执行v.__ next __(),将其全部放到调度器中。

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.http.response.html import HtmlResponse
    from xdb.items import XdbItem
    import scrapy
    from scrapy.http.cookies import CookieJar
    from scrapy.http import Request
    from urllib.parse import urlencode
    class ChoutiSpider(scrapy.Spider):
    name = 'chouti'
    allowed_domains = ['chouti.com']
    start_urls = ['https://dig.chouti.com/']
    def start_request(self):
    for url in self.start_urls:
    yield Request(url=url)
    def parse(self, response):
    pass

1.5 代理

  1. 内置代理

    • 在scrapy内部自带了代理,代理使用过进程环境变量来传输的,通过判断是不是“_PROXY”结尾来判断是不是代理,代理需要在爬虫启动之前设置,所以我们使用start _requests方法。也可以在meta中传代理。

      # -*- coding: utf-8 -*-
      import scrapy
      from scrapy.http import Request
      class ChoutiSpider(scrapy.Spider):
      name = 'chouti'
      allowed_domains = ['chouti.com']
      start_urls = ['https://dig.chouti.com/']
      def start_request(self):
      import os
      #在环境变量中加入代理
      os.environ["HTTPS_PROXY"] = "http://root:woshiniba@192.168.11.11:9999/"
      os.environ["HTTP_PROXY"] = "192.168.11.11"
      #meta中传代理
      for url in self.start_urls:
      yield Request(url=url,meta={"proxy":"http://root:woshiniba@192.168.11.11:9999/"})
      def parse(self, response):
      pass
  2. 自定义代理

    #settings.py
    #自定义的代理需要注册才可以使用,只要注册上自带的就不会被使用了。
    DOWNLOADER_MIDDLEWARES = {
    #'xdb.middlewares.XdbDownloaderMiddleware': 543,
    'xdb.proxy.XdbProxyMiddleware':751,
    }
    #proxy.py
    #这两种都可是完成这个功能
    import base64
    import random
    from six.moves.urllib.parse import unquote
    try:
    from urllib2 import _parse_proxy
    except ImportError:
    from urllib.request import _parse_proxy
    from six.moves.urllib.parse import urlunparse
    from scrapy.utils.python import to_bytes
    class XdbProxyMiddleware(object):
    def _basic_auth_header(self, username, password):
    user_pass = to_bytes(
    '%s:%s' % (unquote(username), unquote(password)),
    encoding='latin-1')
    return base64.b64encode(user_pass).strip()
    def process_request(self, request, spider):
    PROXIES = [
    "http://root:woshiniba@192.168.11.11:9999/",
    "http://root:woshiniba@192.168.11.12:9999/",
    "http://root:woshiniba@192.168.11.13:9999/",
    "http://root:woshiniba@192.168.11.14:9999/",
    "http://root:woshiniba@192.168.11.15:9999/",
    "http://root:woshiniba@192.168.11.16:9999/",
    ]
    url = random.choice(PROXIES)
    orig_type = ""
    proxy_type, user, password, hostport = _parse_proxy(url)
    proxy_url = urlunparse((proxy_type or orig_type, hostport, '', '', '', ''))
    if user:
    creds = self._basic_auth_header(user, password)
    else:
    creds = None
    request.meta['proxy'] = proxy_url
    if creds:
    request.headers['Proxy-Authorization'] = b'Basic ' + creds
    class DdbProxyMiddleware(object):
    def process_request(self, request, spider):
    PROXIES = [
    {'ip_port': '111.11.228.75:80', 'user_pass': ''},
    {'ip_port': '120.198.243.22:80', 'user_pass': ''},
    {'ip_port': '111.8.60.9:8123', 'user_pass': ''},
    {'ip_port': '101.71.27.120:80', 'user_pass': ''},
    {'ip_port': '122.96.59.104:80', 'user_pass': ''},
    {'ip_port': '122.224.249.122:8088', 'user_pass': ''},
    ]
    proxy = random.choice(PROXIES)
    if proxy['user_pass'] is not None:
    request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
    encoded_user_pass = base64.b64encode(to_bytes(proxy['user_pass']))
    request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)
    else:
    request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])

     

1.6 解析器

  1. 解析器就是下xpath,也可以单独使用

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from scrapy.selector import Selector, HtmlXPathSelector
    from scrapy.http import HtmlResponse
    html = """<!DOCTYPE html>
    <html>
    <head lang="en">
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <ul>
    <li class="item-"><a id='i1' href="link.html">first item</a></li>
    <li class="item-0"><a id='i2' href="llink.html">first item</a></li>
    <li class="item-1"><a href="llink2.html">second item<span>vv</span></a></li>
    </ul>
    <div><a href="llink2.html">second item</a></div>
    </body>
    </html>
    """
    #伪造request
    response = HtmlResponse(url='http://example.com', body=html,encoding='utf-8')
    # hxs = HtmlXPathSelector(response)
    # a = Selector(response=response)
    # hxs = a.xpath('//a') #找a标签
    # hxs = a.xpath('//a[2]') #找a标签中的第二个
    # hxs = a.xpath('//a[@id]') #找a标签有id属性的
    # hxs = a.xpath('//a[@id="i1"]') #找a标签有id=i1的7
    # hxs = a.xpath('//a[@href="link.html"][@id="i1"]') #找a标签href=link.html并且id=i1的
    # hxs = a.xpath('//a[contains(@href, "link")]') #找a标签href属性中有link的
    # hxs = a.xpath('//a[starts-with(@href, "link")]') #找a标签href属性是否以link开头
    # hxs = a.xpath('//a[re:test(@id, "i\d+")]') #找a标签id=i\d+的
    # hxs = a.xpath('//a[re:test(@id, "i\d+")]/text()').extract() #找a标签id=i\d+的所有文本
    # hxs = a.xpath('//a[re:test(@id, "i\d+")]/@href').extract() #找a标签id=i\d+的所有href
    # hxs = a.xpath('/html/body/ul/li/a/@href').extract() #找a标签所有href
    # hxs = a.xpath('//body/ul/li/a/@href').extract_first() #找a标签第一个href
    # print(hxs)
    # ul_list = Selector(response=response).xpath('//body/ul/li')
    # for item in ul_list:
    # v = item.xpath('./a/span')
    # # 或
    # # v = item.xpath('a/span')
    # # 或
    # # v = item.xpath('*/a/span')
    # print(v)

2.算法

2.1算法基础

  • 时间复杂度:是不考虑确定时间,只考虑大概时间。

    如:
    #打印一句大概用了n,打印三句依然是n,并不会出现3,应为3是个确定数了。
    print("aaa") #O(n)
    print("bbb") #O(n)
    print("bbb")
    print("bbb")
    #数学的log就是log的几次方等于n例如:log2 64=6
    #如果每次减半就可以用他了 logn只对log2n,应为计算机中大多数都是2进制。
    while n>1: #O(logn)||O(log2n)
    print(n)
    n = n//2
      • 时间复杂度是用来估计算法运行时间大的一个式子。

      • 一般来说,时间复杂度高的算法比复杂度低的算法慢。

      • 常见的时间复杂度

  • 递归

    def func1(x): # 5,4,3,2,1
    if x>0:
    print(x)
    func1(x-1)
    def func2(x): # 1,2,3,4,5
    if x>0:
    func2(x-1)
    print(x)

2.2 查找

  1. 列表查询有顺序查找和二分查找,二分查找从有序列表的候选区,通过对待查找的值于候选区中间值的比较,可以使候选区减少一半。

    def binary_search(li,val):
    low = 0
    high = len(li) - 1
    while low <= high:
    mid = (low+high) //2
    if li[mid] > val:
    high = mid -1
    elif li[mid] < val:
    low = mid + 1
    else:
    return mid
    else:
    return -1
    li = range(0,1222222)
    print(binary_search(li,305))

2.3 排序

  1. 冒泡排序,列表每两个相邻的数,如果前面的比后面的大,那么交换这两个数。

    def bubble_sort(li):
    for i in range(0,len(li)-1):
    for j in range(0,len(li)-i-1):
    if li[j] >li[j+1]:
    li[j],li[j+1] = li[j+1],li[j]
    import random
    li = list(range(10000))
    random.shuffle(li)
    bubble_sort(li)
    print(li)
  2. 选择排序,一趟经历记录最小的数,放到第一个位置。

    def select_sort(li):
    for i in range(len(li)-1):
    min_loc = i
    for j in range(i+1,len(i)):
    if li[min_loc] > li[j]:
    min_loc = j
    li[min_loc],li[i] = li[i],li[min_loc]
    import random
    li = list(range(10000))
    random.shuffle(li)
    select_sort(li)
    print(li)
  3. 插入排序,列表被分为有序区和无序区两部分,最初有序区只有一个元素。每次从无序区中选择一个元素,插入到有序区的位置,直到无序区为空。

    def insert_sort(li):
    for i in range(1,len(li)):
    tmp = li[i]
    j = i - 1
    while j >= 0 and tmp < li[j]:
    li[j + 1] = li[j]
    j = j - 1
    li[j + 1] = tmp
    import random
    li = list(range(10000))
    random.shuffle(li)
    insert_sort(li)
    print(li)
    • 空间复杂度:O(1),时间复杂度O(n^2)

  4. 快速排序,取一个元素p(第一个元素),使元素p归位;列表被p分为两部分,左边比p小,右边比p大;然后递归完成。

    def quick_sort(li,left,right):
    if left < right:
    mid = partition(li,left,right)
    quick_sort(li,left,mid-1)
    quick_sort(li,mid+1,right)
    def partition(li,left,right):
    tmp = li[left]
    while left < right:
    while left <right and li[right] >= tmp:
    right -= 1
    while left <right and li[left] <= tmp:
    left += 1
    li[right] = li[left]
    li[left] = tmp
    return left
    import random
    li = list(range(10000))
    random.shuffle(li)
    quick_sort(li,0,len(li)-1)
    print(li)
  5. 堆排序

    1. 树是一种数据结构,是一种可以递归定义的数据结构。

    2. 二叉树是树枝不超过两个,并且分为满二叉树和完全二叉树,前者是每一层都达到最大值,后者是只能出现最下层和次下层右侧出现缺少。

    3. 二叉树顺序存储方式,根节点左侧是“2n+1”,根节点右侧是“2n+2”。

    4. 堆排序的堆有大根堆和小根堆,大根堆是根最大下一级二叉树小于上一级。小根堆是根最小下一级二叉树大于上一级。

    def sift(li,low,high):
       tmp = li[low]
       i = low
       j = 2*i+1
       while True:
           if j<high and li[j+1] > li[j]:
               j +=1
           if tmp < li[j]:
               li[i] = li[j]
               i = j
               j = 2*i+1
           else:
               li[i] = tmp
               break
       li[i] = tmp
    def heap_sort(li):
       n = len(li)
       for i in range(n // 2 -1, -1,-1):
           sift(li,i,n-1)
       for i in range(n-1,-1,-1):
           li[0],li[i] = li[i],li[0]
           sift(li,0,i-1)
    import random
    li = list(range(100000,1,-1))
    random.shuffle(li)
    heqp_sort(li)
    print(li)
    #python内部其实也带来一个堆排序
    import heapq
    li = [4,3,2,1,5,6,7,8]
    heapq.heapify(li)
    print(li)
  6. 归并排序,是将列表分到最小,再将列表有序归并到最大。

    def merge(li,low,mid,high):
       i = low
       j = mid +1
       ltmp = []
       while i <= mid and j <= high:
           if li[i] <= li[j]:
               ltmp.append(li[i])
               i += 1
           else:
               ltmp.append(li[j])
               j += 1
       while i <= mid:
           ltmp.append(li[i])
           i += 1
       while j <= high:
           ltmp.append(li[j])
           j += 1
       li[low:high + 1] = ltmp
    def merge_sort(li,low,high):
       if low < high:
           mid = (low + high) // 2
           merge_sort(li,low,mid)
           merge_sort(li,mid+1,high)
           merge(li,low,mid,high)
    li = [10,4,5,6,7,8,3,2]
    merge_sort(li,0,len(li)-1)
    print(li)

爬虫高级

1.Middleware and making orders

1.1 download Middleware

  1. 下载中间件主要用于有,request的加工。

    # settings.py
    #注册中间件
    DOWNLOADER_MIDDLEWARES = {
      #'xdb.middlewares.XdbDownloaderMiddleware': 543,
       'xdb.md.Md1':666,
    }
    # md.py
    from scrapy.http import HtmlResponse
    from scrapy.http import Request
    class Md1(object):
       @classmethod
       def from_crawler(cls, crawler):
           # This method is used by Scrapy to create your spiders.
           s = cls()
           return s
       def process_request(self, request, spider):
           # Called for each request that goes through the downloader
           # middleware.
           # Must either:
           # - return None: continue processing this request
           # - or return a Response object
           # - or return a Request object
           # - or raise IgnoreRequest: process_exception() methods of
           #   installed downloader middleware will be called
           print('md1.process_request',request)
           # 1. 返回Response对象,你返回什么,爬虫文件中就拿到什么
           # import requests
           # result = requests.get(request.url)
           # return HtmlResponse(url=request.url, status=200, headers=None, body=result.content)
           # 2. 返回Request,会一直去比对,并不会返回
           # return Request('https://dig.chouti.com/r/tec/hot/1')
           # 3. 抛出异常
           # from scrapy.exceptions import IgnoreRequest
           # raise IgnoreRequest
           # 4. 对请求进行加工(*)
           # request.headers['user-agent'] = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
           pass
       def process_response(self, request, response, spider):
           # Called with the response returned from the downloader.
           # Must either;
           # - return a Response object
           # - return a Request object
           # - or raise IgnoreRequest
           print('m1.process_response',request,response)
           return response
       def process_exception(self, request, exception, spider):
           #使用该方法去接收错误
           # Called when a download handler or a process_request()
           # (from other downloader middleware) raises an exception.
           # Must either:
           # - return None: continue processing this exception
           # - return a Response object: stops process_exception() chain
           # - return a Request object: stops process_exception() chain
           pass

1.2 Reptile Middleware

  1. 爬虫中间件,用的不多,就做大概了解即可。

    #settings.py
    #设置爬虫中间件
    SPIDER_MIDDLEWARES = {
       'xdb.sd.Sd1': 666,
    }
    class Sd1(object):
       # Not all methods need to be defined. If a method is not defined,
       # scrapy acts as if the spider middleware does not modify the
       # passed objects.
       @classmethod
       def from_crawler(cls, crawler):
           # This method is used by Scrapy to create your spiders.
           s = cls()
           return s
       def process_spider_input(self, response, spider):
           # Called for each response that goes through the spider
           # middleware and into the spider.
           # Should return None or raise an exception.
           return None
       def process_spider_output(self, response, result, spider):
           # Called with the results returned from the Spider, after
           # it has processed the response.
           # Must return an iterable of Request, dict or Item objects.
           for i in result:
               yield i
       def process_spider_exception(self, response, exception, spider):
           # Called when a spider or process_spider_input() method
           # (from other spider middleware) raises an exception.
           # Should return either None or an iterable of Response, dict
           # or Item objects.
           pass
       # 只在爬虫启动时,执行一次。从爬虫文件的start_request的返回回到这里的
       def process_start_requests(self, start_requests, spider):
           # Called with the start requests of the spider, and works
           # similarly to the process_spider_output() method, except
           # that it doesn’t have a response associated.
           # Must return only requests (not items).
           for r in start_requests:
               yield r

1.3 making orders

  1. 单爬虫运行,放在执行命令的同目录即可。

    import sys
    from scrapy.cmdline import execute
    if __name__ == '__main__':
    execute(["scrapy","crawl","chouti","--nolog"])
  2. 多爬虫运行

    1.在spiders同级创建任意目录,如:commands
    2.在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)
    3.在settings.py 中添加配置 COMMANDS_MODULE = '项目名称.目录名称'
    4.在项目目录执行命令:scrapy crawlall
    #crawlall.py
    from scrapy.commands import ScrapyCommand
    from scrapy.utils.project import get_project_settings
    class Command(ScrapyCommand):
    requires_project = True
    def syntax(self):
    return '[options]'
    def short_desc(self):
    return 'Runs all of the spiders'
    def run(self, args, opts):
    spider_list = self.crawler_process.spiders.list()
    for name in spider_list:
    self.crawler_process.crawl(name, **opts.__dict__)
    self.crawler_process.start()

2.信号

2.1 Django

  1. Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。

    Model signals
    pre_init # django的modal执行其构造方法前,自动触发
    post_init # django的modal执行其构造方法后,自动触发
    pre_save # django的modal对象保存前,自动触发
    post_save # django的modal对象保存后,自动触发
    pre_delete # django的modal对象删除前,自动触发
    post_delete # django的modal对象删除后,自动触发
    m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
    class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
    Management signals
    pre_migrate # 执行migrate命令前,自动触发
    post_migrate # 执行migrate命令后,自动触发
    Request/response signals
    request_started # 请求到来前,自动触发
    request_finished # 请求结束后,自动触发
    got_request_exception # 请求异常后,自动触发
    Test signals
    setting_changed # 使用test测试修改配置文件时,自动触发
    template_rendered # 使用test测试渲染模板时,自动触发
    Database Wrappers
    connection_created # 创建数据库连接时,自动触发
  2. 对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:

    from django.core.signals import request_finished
    from django.core.signals import request_started
    from django.core.signals import got_request_exception
    from django.db.models.signals import class_prepared
    from django.db.models.signals import pre_init, post_init
    from django.db.models.signals import pre_save, post_save
    from django.db.models.signals import pre_delete, post_delete
    from django.db.models.signals import m2m_changed
    from django.db.models.signals import pre_migrate, post_migrate
    from django.test.signals import setting_changed
    from django.test.signals import template_rendered
    from django.db.backends.signals import connection_created
    def callback(sender, **kwargs):
    print("xxoo_callback")
    print(sender,kwargs)
    xxoo.connect(callback)
    # xxoo指上述导入的内容
  3. 示例

    # xx/app/models.py
    from django.db import models
    class User(models.Model):
    title = models.CharField(max_length=32)
    #xx/xx/init.py
    from django.db.models import signals
    def before_save1(*args,**kwargs):
    print('有车来了,我要服务了--》',args,kwargs)
    def after_save1(*args,**kwargs):
    print('有车来了,完事了--》',args,kwargs)
    signals.pre_save.connect(before_save1)
    signals.post_save.connect(after_save1)
    # xx/app/view.py
    from django.shortcuts import render,HttpResponse
    from app01 import models
    def func1(request):
    # models.User.objects.create(title='老男孩')
    return HttpResponse('创建成功')

2.2 Scrapy

  1. scrapy使用型号也是一样,需要创建函数的。

    # ext.py
    # by luffycity.com
    from scrapy import signals
    class MyExtend(object):
       def __init__(self):
           pass
       @classmethod
       def from_crawler(cls, crawler):
           self = cls()
    #信号的设置
           crawler.signals.connect(self.x1, signal=signals.spider_opened)
           crawler.signals.connect(self.x2, signal=signals.spider_closed)
           return self
       def x1(self, spider):
           print('open')
    def x2(self, spider):
    print('close')
    # settings.py
    EXTENSIONS = {
       'xdb.ext.MyExtend':666,
    }

3.Scrapy redis

  • 分布式爬虫组件

3.1 去重

  1. redis去重,去重可以自定义,但是已经有人写好了,所以我们使用写好的(写好的使用的是时间戳,我们需要写死)。

  2. 自定义

    # settings.py
    DUPEFILTER_CLASS = 'dbd.xxx.DupFilter'
    # xxx.py
    from scrapy.dupefilter import BaseDupeFilter
    import redis
    from scrapy.utils.request import request_fingerprint
    class DupFilter(BaseDupeFilter):
    def __init__(self):
    self.conn = redis.Redis(host='140.143.227.206',port=8888,password='beta')
    def request_seen(self, request):
    """
    检测当前请求是否已经被访问过
    :param request:
    :return: True表示已经访问过;False表示未访问过
    """
    fid = request_fingerprint(request)
    result = self.conn.sadd('visited_urls', fid)
    if result == 1:
    return False
    return True
  3. copy system

    # settings.py
    # ############### scrapy redis连接 ####################
    REDIS_HOST = '140.143.227.206'                            # 主机名
    REDIS_PORT = 8888                                   # 端口
    REDIS_PARAMS = {'password':'beta'}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
    REDIS_ENCODING = "utf-8"                            # redis编码类型             默认:'utf-8'
    # REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
    # ############### scrapy redis去重 ####################
    DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
    #自带的
    # DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
    #复制的
    DUPEFILTER_CLASS = 'dbd.xxx.RedisDupeFilter'
    # xxx.py
    from scrapy_redis.dupefilter import RFPDupeFilter
    from scrapy_redis.connection import get_redis_from_settings
    from scrapy_redis import defaults
    class RedisDupeFilter(RFPDupeFilter):
       @classmethod
       def from_settings(cls, settings):
           """Returns an instance from given settings.
          This uses by default the key ``dupefilter:<timestamp>``. When using the
          ``scrapy_redis.scheduler.Scheduler`` class, this method is not used as
          it needs to pass the spider name in the key.
          Parameters
          ----------
          settings : scrapy.settings.Settings
          Returns
          -------
          RFPDupeFilter
              A RFPDupeFilter instance.
          """
           server = get_redis_from_settings(settings)
           # XXX: This creates one-time key. needed to support to use this
           # class as standalone dupefilter with scrapy's default scheduler
           # if scrapy passes spider on open() method this wouldn't be needed
           # TODO: Use SCRAPY_JOB env as default and fallback to timestamp.
           key = defaults.DUPEFILTER_KEY % {'timestamp': 'xiaodongbei'}
           debug = settings.getbool('DUPEFILTER_DEBUG')
           return cls(server, key=key, debug=debug)

3.2 队列

  1. 队列有三种,先进先出、后进先出、优先级。

  2. 先进先出

    import scrapy_redis
    import redis
    class FifoQueue(object):
    def __init__(self):
    self.server = redis.Redis(host='140.143.227.206',port=8888,password='beta')
    def push(self, request):
    """Push a request"""
    self.server.lpush('USERS', request)
    def pop(self, timeout=0):
    """Pop a request"""
    data = self.server.rpop('USERS')
    return data
    # [33,22,11]
    q = FifoQueue()
    q.push(11)
    q.push(22)
    q.push(33)
    print(q.pop())
    print(q.pop())
    print(q.pop())
  3. 后进先出

    import redis
    class LifoQueue(object):
       """Per-spider LIFO queue."""
       def __init__(self):
           self.server = redis.Redis(host='140.143.227.206',port=8888,password='beta')
       def push(self, request):
           """Push a request"""
           self.server.lpush("USERS", request)
       def pop(self, timeout=0):
           """Pop a request"""
           data = self.server.lpop('USERS')
           return data
    # [33,22,11]
    q = FifoQueue()
    q.push(11)
    q.push(22)
    q.push(33)
    print(q.pop())
    print(q.pop())
    print(q.pop())
  4. 优先级

    import redis
    class PriorityQueue(object):
       """Per-spider priority queue abstraction using redis' sorted set"""
       def __init__(self):
           self.server = redis.Redis(host='140.143.227.206',port=8888,password='beta')
       def push(self, request,score):
           """Push a request"""
           # data = self._encode_request(request)
           # score = -request.priority
           # We don't use zadd method as the order of arguments change depending on
           # whether the class is Redis or StrictRedis, and the option of using
           # kwargs only accepts strings, not bytes.
           self.server.execute_command('ZADD', 'xxxxxx', score, request)
       def pop(self, timeout=0):
           """
          Pop a request
          timeout not support in this queue class
          """
           # use atomic range/remove using multi/exec
           pipe = self.server.pipeline()
           pipe.multi()
           pipe.zrange('xxxxxx', 0, 0).zremrangebyrank('xxxxxx', 0, 0)
           results, count = pipe.execute()
           if results:
               return results[0]
    q = PriorityQueue()
    q.push('alex',99)
    q.push('oldboy',56)
    q.push('eric',77)
    v1 = q.pop()
    print(v1)
    v2 = q.pop()
    print(v2)
    v3 = q.pop()
    print(v3)

3.3 调度器

  1. 调度器本质就是存储和删除,所以我们使用scrapy redis来做调度器。

  2. scrapy crawl chouti --nolog执行后,会先去settings中找SCHEDULER并执行Scheduler.from_crawler。再执行Scheduler.from_settings去读取设置的配置文件,最后再去循环读取Scrapy的连接。

    # settings.py
    # ############### scrapy redis连接 ####################
    REDIS_HOST = '140.143.227.206'                      # 主机名
    REDIS_PORT = 8888                                   # 端口
    REDIS_PARAMS = {'password':'beta'}                 # Redis连接参数            
    # 默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
    REDIS_ENCODING = "utf-8"                            # redis编码类型             默认:'utf-8'
    # REDIS_URL = 'redis://user:pass@hostname:9001'     # 连接URL(优先于以上配置)
    ################ 去重 ######################
    DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
    DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
    # ###################### 调度器 ######################
    from scrapy_redis.scheduler import Scheduler
    # 由scrapy_redis的调度器来进行负责调配
    # enqueue_request: 向调度器中添加任务
    # next_request: 去调度器中获取一个任务
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 规定任务存放的顺序
    # 优先级 DEPTH_PRIORITY只在优先级中有用
    DEPTH_PRIORITY = 1  # 广度优先
    # DEPTH_PRIORITY = -1 # 深度优先
    # 默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'  
    # 广度优先
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'  
    # 深度优先
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
    SCHEDULER_QUEUE_KEY = '%(spider)s:requests'  # 调度器中请求存放在redis中的key
    SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  # 对保存到redis中的数据进行序列化,默认使用pickle
    SCHEDULER_PERSIST = False  # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
    SCHEDULER_FLUSH_ON_START = True  # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
    # SCHEDULER_IDLE_BEFORE_CLOSE = 10 # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。
    SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'  # 去重规则,在redis中保存时对应的key
    SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'  # 去重规则对应处理的类

3.4 start url

  1. 起始url可以设置在Scrapy redis中,这样程序没任务时会等待,并不会结束进程。

    # setting.py
    START_URLS_KEY = '%(name)s:start_urls'
    #False是从列表拿,True是从集合中拿
    REDIS_START_URLS_AS_SET = False
    # spiders/chouti.py
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.http import Request
    import scrapy_redis
    from scrapy_redis.spiders import RedisSpider
    class ChoutiSpider(RedisSpider):
    name = 'chouti'
    allowed_domains = ['chouti.com']
    def parse(self, response):
    print(response)
    # Server file for the scrapy redis.
    # by luffycity.com
    import redis
    conn = redis.Redis(host='140.143.227.206',port=8888,password='beta')
    #这里放什么,爬虫文件中就下载什么。
    conn.lpush('chouti:start_urls','https://dig.chouti.com/r/pic/hot/1')

     

4.Settings file for the start

  1. 配置文件的一下配置的说明。

    # -*- coding: utf-8 -*-
    # project name
    BOT_NAME = 'dbd'
    # reptile file path
    SPIDER_MODULES = ['dbd.spiders']
    NEWSPIDER_MODULE = 'dbd.spiders'
    # Crawl responsibly by identifying yourself (and your website) on the user-agent
    # USER_AGENT = 'dbd (+http://www.yourdomain.com)'
    USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
    #爬取别人的网站是违法的,他们会规定就在url+robots.txt中,所以这条配置就是是否去robots中查找。它允许你就可以爬取,他不允许就不可以爬取。
    # Obey robots.txt rules
    ROBOTSTXT_OBEY = False
    # 这是并发默认是一个线程16个协程,你也可以设置的,这里的并不均匀,有可能一个多一个少
    # Configure maximum concurrent requests performed by Scrapy (default: 16)
    #CONCURRENT_REQUESTS = 32
    # Configure a delay for requests for the same website (default: 0)
    # See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
    # See also autothrottle settings and docs
    # 延迟三秒
    #DOWNLOAD_DELAY = 3
    # The download delay setting will honor only one of:
    # 每个网页并发16个
    #CONCURRENT_REQUESTS_PER_DOMAIN = 16
    # 每个ip并发16个
    #CONCURRENT_REQUESTS_PER_IP = 16
    # 内部帮你操作cookies
    # Disable cookies (enabled by default)
    #COOKIES_ENABLED = False
    # Disable Telnet Console (enabled by default)
    # from scrapy.extensions.telnet import TelnetConsole
    # telnet 127.0.0.1 6023
    # engine.pause()
    # engine.unpause()
    # 如果是True的话加上ip和端口可以,向电脑发送停止、继续、终止等指令。
    # TELNETCONSOLE_ENABLED = True
    # TELNETCONSOLE_HOST = '127.0.0.1'
    # TELNETCONSOLE_PORT = [6023,]
    # 默认的给所有请求头加上的请求
    # Override the default request headers:
    #DEFAULT_REQUEST_HEADERS = {
    #   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    #   'Accept-Language': 'en',
    #}
    # Enable or disable spider middlewares
    # See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
    # 爬虫中间件
    #SPIDER_MIDDLEWARES = {
    #   'dbd.middlewares.DbdSpiderMiddleware': 543,
    #}
    # Enable or disable downloader middlewares
    # See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
    # 下载中间件
    #DOWNLOADER_MIDDLEWARES = {
    #   'dbd.middlewares.DbdDownloaderMiddleware': 543,
    #}
    # Enable or disable extensions
    # See https://doc.scrapy.org/en/latest/topics/extensions.html
    # 信号注册
    #EXTENSIONS = {
    #   'scrapy.extensions.telnet.TelnetConsole': None,
    #}
    # Configure item pipelines
    # See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
    # pipelines
    #ITEM_PIPELINES = {
    #   'dbd.pipelines.DbdPipeline': 300,
    #}
    # Enable and configure the AutoThrottle extension (disabled by default)
    # See https://doc.scrapy.org/en/latest/topics/autothrottle.html
    from scrapy.contrib.throttle import AutoThrottle
    """
    17. 自动限速算法
      from scrapy.contrib.throttle import AutoThrottle
      自动限速设置
      1. 获取最小延迟 DOWNLOAD_DELAY
      2. 获取最大延迟 AUTOTHROTTLE_MAX_DELAY
      3. 设置初始下载延迟 AUTOTHROTTLE_START_DELAY
      4. 当请求下载完成后,获取其"连接"时间 latency,即:请求连接到接受到响应头之间的时间
      5. 用于计算的... AUTOTHROTTLE_TARGET_CONCURRENCY
      target_delay = latency / self.target_concurrency
      new_delay = (slot.delay + target_delay) / 2.0 # 表示上一次的延迟时间
      new_delay = max(target_delay, new_delay)
      new_delay = min(max(self.mindelay, new_delay), self.maxdelay)
      slot.delay = new_delay
    """
    # 自动限速相关的配置,上面有算法和用到的地方可以看看。
    #AUTOTHROTTLE_ENABLED = True
    # The initial download delay
    #AUTOTHROTTLE_START_DELAY = 5
    # The maximum download delay to be set in case of high latencies
    #AUTOTHROTTLE_MAX_DELAY = 60
    # The average number of requests Scrapy should be sending in parallel to
    # each remote server
    #AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
    # Enable showing throttling stats for every response received:
    #AUTOTHROTTLE_DEBUG = False
    # Enable and configure HTTP caching (disabled by default)
    # See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
    # 如果配置上他们后,就是从本地的缓存中去,读取网页了。这样没网的时候也可以练习了。
    # HTTPCACHE_ENABLED = True
    # HTTPCACHE_EXPIRATION_SECS = 0
    # HTTPCACHE_DIR = 'httpcache'
    # HTTPCACHE_IGNORE_HTTP_CODES = []
    # HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

5.提高性能

  • 他们都是使用单线程实现并发的发出请求。

5.1 协程

  • 协程是使用了gevent模块实现的,python你不并没有协程可说。

    """
    协程+IO切换
    pip3 install gevent
    gevent内部调用greenlet(实现了协程)。
    """
    from gevent import monkey; monkey.patch_all()
    import gevent
    import requests
    def func(url):
       response = requests.get(url)
       print(response)
    urls = [
       'http://www.baidu.com/',
       'https://www.cnblogs.com/',
       'https://www.cnblogs.com/news/',
       'https://cn.bing.com/',
       'https://stackoverflow.com/',
    ]
    spawn_list = []
    for url in urls:
       #循环将任务制定
       spawn_list.append(gevent.spawn(func, url))
    #回收任务
    gevent.joinall(spawn_list)

5.2 异步非阻塞

  • 异步非阻塞也是用了twisted模块实现的。

    """
    基于事件循环的异步非阻塞模块:Twisted
    """
    from twisted.web.client import getPage, defer
    from twisted.internet import reactor
    def stop_loop(arg):
       #这条命令是终止循环
       reactor.stop()
    def get_response(contents):
       print(contents)
    deferred_list = []
    url_list = [
       'http://www.baidu.com/',
       'https://www.cnblogs.com/',
       'https://www.cnblogs.com/news/',
       'https://cn.bing.com/',
       'https://stackoverflow.com/',
    ]
    for url in url_list:
       #制定任务
       deferred = getPage(bytes(url, encoding='utf8'))
       #内部自动发请求,请求成功后自动调用这个函数
       deferred.addCallback(get_response)
       #将任务存放到列表中
       deferred_list.append(deferred)
    # 监听列表,如果都完成了就往下走
    dlist = defer.DeferredList(deferred_list)
    # 任务完成后调用这个函数
    dlist.addBoth(stop_loop)
    # run代表开始去爬取了
    reactor.run()

5.3 自定义异步非阻塞

  • 自定义的只为学习和了解,如果使用上面的那两个足够了。

    # chun.py
    import socket
    import select
    class ChunSheng(object):
       def __init__(self):
           self.socket_list = []
           self.conn_list = []
           self.conn_func_dict = {}
       def add_request(self,url_func):
           conn = socket.socket()
           conn.setblocking(False)
           try:
               conn.connect((url_func[0],80))
           except BlockingIOError as e:
               pass
           self.conn_func_dict[conn] = url_func[1]
           self.socket_list.append(conn)
           self.conn_list.append(conn)
       def run(self):
           """
          检测self.socket_list中的5个socket对象是否连接成功
          :return:
          """
           while True:
               #   select.select
               #   第一个参数: 用于检测其中socket是否已经获取到响应内容
               #   第二个参数: 用于检测其中socket是否已经连接成功
               # 第一个返回值 r:具体是那一个socket获取到结果
               # 第二个返回值 r:具体是那一个socket连接成功
               r,w,e = select.select(self.socket_list,self.conn_list,[],0.05)
               for sock in w: # [socket1,socket2]
                   sock.send(b'GET / http1.1\r\nhost:xxxx.com\r\n\r\n')
                   self.conn_list.remove(sock)
               for sock in r:
                   data = sock.recv(8096)
                   func = self.conn_func_dict[sock]
                   func(data)
                   sock.close()
                   self.socket_list.remove(sock)
               if not self.socket_list:
                   break
                   
    # xx.py
    from chun import ChunSheng
    def callback1(data):
       print('下载完成',data)
    def callback2(data):
       print('下载完成',data)
    chun = ChunSheng()
    urls = [
      ('www.baidu.com',callback1),
      ('www.cnblogs.com',callback1),
      ('www.pythonav.com',callback2),
      ('www.bing.com',callback2),
      ('www.stackoverflow.com',callback2),
    ]
    for url in urls:
       chun.add_request(url)
    chun.run()
  • select,只能监听1024个socket,内部会循环所有的socket去检测;(windows使用)

  • poll,无个数限制,内部会循环所有的socket去检测;(mac,linux使用)

  • epoll,无个数限制,回调。(mac,linux使用)

Flask初级

  1. 短小精悍,可扩展强的一个web框架。上下文管理

  2. 安装:pip3 install Flask,我们做web开发时,是站在两大东西之上做的web框架和wsgi,Flask和Django中都是应用的并不是自己写的。Flask中werkzurg就是。

  3. web服务网关接口,wsgi是一个协议,实现该写一个的模块:wsgiref|werkzeug,实现其协议的模块本质上就是socket服务端用于接收用户请求,并处理。一般web框架基于wsgi实现,这样实现关注点分离。

  4. flask就是基于Werkzurg搭建起来的

    from werkzeug.wrappers import Request, Response
    from werkzeug.serving import run_simple
    @Request.application
    def run(environ, start_response):
       return Response("Hello World!") # 返回字符串
    if __name__=="__main__":
       run_simple("localhost", 4000, run) # 监听本机的4000端口,如果访问就执行run函数
  5. 一个最简单的Flask

    from flask import Flask
    app = Flask(__name__) # flask名称,一般这样写。
    @app.route("/index") # url路径
    def index(): #所调用的函数
    return "Hello World!"
    if __name__ == "__main__":
    app.run() # 执行口
  6. Flask实现用户登录程序

    from flask import Flask, render_template, request, redirect, session
    app = Flask(__name__, template_folder="templates", static_folder="static")
    app.secret_key = "asdf" # session加盐
    # request.form # 请求体
    # request.args # 请求头
    # request.method # 请求类型
    @app.route("/login", methods=["GET", "POST"])
    def login():
       if request.method == "GET":
           return render_template("login.html")
      user = request.from.get("user")
      pwd= request.from.get("pwd")
       if user == "alex" and pwd == "666":
           session["user"] = user
           return redirect("/index")
       return render_template("login.html", error="用户名或密码错误")
    @app.route("/index")
    def index():
       user = session.get("user")
       if not user:
           return redirect("/login")
       return render_template("index.html")
    if __name__ == "__main__":
       app.run()

1.配置文件

  1. settings

    class Config(object):
       DEBUG = False
       TESTING = False
       DATABASE_URL = "sqlite://:memory:"
    class ProductionConfig(Config):
       DATABASE_URL = "mysql://user@localhost/foo"
       
    class DevelopmentConfig(Config):
       DEBUT = True
       
    class TestingConfig(Config):
       TESTING = True
       
    flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
    {
       'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
       'TESTING':                              False,                          是否开启测试模式
       'PROPAGATE_EXCEPTIONS':                 None,                          
       'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
       'SECRET_KEY':                           None,
       'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
       'USE_X_SENDFILE':                       False,
       'LOGGER_NAME':                          None,
       'LOGGER_HANDLER_POLICY':               'always',
       'SERVER_NAME':                          None,
       'APPLICATION_ROOT':                     None,
       'SESSION_COOKIE_NAME':                  'session',
       'SESSION_COOKIE_DOMAIN':                None,
       'SESSION_COOKIE_PATH':                  None,
       'SESSION_COOKIE_HTTPONLY':              True,
       'SESSION_COOKIE_SECURE':                False,
       'SESSION_REFRESH_EACH_REQUEST':         True,
       'MAX_CONTENT_LENGTH':                   None,
       'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
       'TRAP_BAD_REQUEST_ERRORS':              False,
       'TRAP_HTTP_EXCEPTIONS':                 False,
       'EXPLAIN_TEMPLATE_LOADING':             False,
       'PREFERRED_URL_SCHEME':                 'http',
       'JSON_AS_ASCII':                        True,
       'JSON_SORT_KEYS':                       True,
       'JSONIFY_PRETTYPRINT_REGULAR':          True,
       'JSONIFY_MIMETYPE':                     'application/json',
       'TEMPLATES_AUTO_RELOAD':                None,
    }
  2. xx

    # 如何通过代码找到指定的类
    import importlib
    path = "settings.Foo"
    p, c = path.rsplit(".", maxsplit=1)
    m = importlib.import_module(p)
    cls = getattr(m, c)
    # 找出类中的所有静态变量
    for key in dir(cls):
    if key.isupper():
    print(key,getattr(cls, key))
  3. Flask中有一个配置文件

    from flask import Flask, render_template, request, redirect, session
    app = Flask(__name__)
    print(app.config) # 这是flask提供的配置文件
    # app.config.["DEBUG"] = True # 我们可以通过这样来修改配置,但是flask也提供了另一种方式
    app.config.from_object("sttings.DevelopmentConfig") # 这种方式就是自己创建一个文件和类,你在类中写的所有配置都会被应用。
    if __name__ == "__main__":
    app.run()

     

2.路由和视图

  1. flask的路由系统

    from flask import Flask, render_template, request, redirect, session, url_for
    app = Flask(__name__)
    # int 是值的类型,nid是名称:/login/1234
    """
    # 这是常用的类型,不加类型默认是字符串类型
    DEFAULT_CONVERTERS = {
    'default': UnicodeConverter,
    'string': UnicodeConverter,
    'any': AnyConverter,
    'path': PathConverter,
    'int': IntegerConverter,
    'float': FloatConverter,
    'uuid': UUIDConverter,
    }
    """
    @app.route("/login/<int:nid>", endpoint="n1") # endpoint就是设置名字,不写默认是函数名
    def login():
    url_for("n1", nid=122) # 反向查找url,nid是携带的值
    if __name__ == "__main__":
    app.run()
  2. 路由的两种方式

    @app.route("/xxx")
    def index():
    return "index"
    # 其实到内部调用时就是使用了app.add_url_rule来实现的,为了方便所以在外面套了个装饰器。
    def index():
    return "index"
    app.add_url_rule("/xxx", None, index)
    # 注意事项:
    # - 不用让endpoint重名
    # - 如果重名函数也一定要相同。
  3. app.route和app.add_url_rule参数

    @app.route和app.add_url_rule参数:
    rule,                       URL规则
    view_func,                  视图函数名称
       defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
    endpoint=None,              名称,用于反向生成URL,即: url_for('名称')
    methods=None,               允许的请求方式,如:["GET","POST"]
               
    strict_slashes=None,        对URL最后的 / 符号是否严格要求,
    如:
    @app.route('/index',strict_slashes=False),
    访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可
               @app.route('/index',strict_slashes=True)
                   仅访问 http://www.xx.com/index
    redirect_to=None,           重定向到指定地址
    如:
    @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
               或
               def func(adapter, nid):
    return "/home/888"
               @app.route('/index/<int:nid>', redirect_to=func)
    subdomain=None,             子域名访问
    from flask import Flask, views, url_for
           app = Flask(import_name=__name__)
           app.config['SERVER_NAME'] = 'wupeiqi.com:5000'
           @app.route("/", subdomain="admin")
           def static_index():
          """Flask supports static subdomains
              This is available at static.your-domain.tld"""
               return "static.your-domain.tld"
           @app.route("/dynamic", subdomain="<username>")
           def username_index(username):
               """Dynamic subdomains are also supported
              Try going to user1.your-domain.tld/dynamic"""
               return username + ".your-domain.tld"
           if __name__ == '__main__':
           app.run()
  4. CBV

    import functools
    from flask import Flask,views
    app = Flask(__name__)
    def wrapper(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
    return func(*args,**kwargs)
    return inner
    class UserView(views.MethodView):
    methods = ['GET']
    decorators = [wrapper,]
    def get(self,*args,**kwargs):
    return 'GET'
    def post(self,*args,**kwargs):
    return 'POST'
    app.add_url_rule('/user',None,UserView.as_view('uuuu'))
    if __name__ == '__main__':
    app.run()
  5. 自定义正则

    from flask import Flask,url_for
    app = Flask(__name__)
    # 步骤一:定制类
    from werkzeug.routing import BaseConverter
    class RegexConverter(BaseConverter):
    """
    自定义URL匹配正则表达式
    """
    def __init__(self, map, regex):
    super(RegexConverter, self).__init__(map)
    self.regex = regex
    def to_python(self, value):
    """
    路由匹配时,匹配成功后传递给视图函数中参数的值
    :param value:
    :return:
    """
    return int(value)
    def to_url(self, value):
    """
    使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
    :param value:
    :return:
    """
    val = super(RegexConverter, self).to_url(value)
    return val
    # 步骤二:添加到转换器
    app.url_map.converters['reg'] = RegexConverter
    """
    1. 用户发送请求
    2. flask内部进行正则匹配
    3. 调用to_python(正则匹配的结果)方法
    4. to_python方法的返回值会交给视图函数的参数
    """
    # 步骤三:使用自定义正则
    @app.route('/index/<reg("\d+"):nid>')
    def index(nid):
    print(nid,type(nid))
    print(url_for('index',nid=987))
    return "index"
    if __name__ == '__main__':
    app.run()

     

3.请求和响应

  1. 请求

    # request.method
    # request.args
    # request.form
    # request.values
    # request.cookies
    # request.headers
    # request.path
    # request.full_path
    # request.script_root
    # request.url
    # request.base_url
    # request.url_root
    # request.host_url
    # request.host
    # request.files
    # obj = request.files['the_file_name']
    # obj.save('/var/www/uploads/' + secure_filename(f.filename))
  2. 响应

    from flask import Flask, render_template, request, redirect, session, url_for, jsonify
    # 响应相关信息
    # return "字符串"
    # return render_template('html模板路径',**{})
    # return redirect('/index.html')
    # return jsonify({"k1":"v1"})
    # response = make_response(render_template('index.html'))
    # response是flask.wrappers.Response类型
    # response.delete_cookie('key')
    # response.set_cookie('key', 'value')
    # response.headers['X-Something'] = 'A value'
    # return response
  3. 实现登录后才可查看的效果

    # 版本一:在每个函数中都加入判断
    @app.route('/index')
    def index():
    if not session.get('user'):
    return redirect(url_for('login'))
    return render_template('index.html',stu_dic=STUDENT_DICT)
    # 版本二:使用装饰器,但是需要用到functools的wraps,应为你写的装饰器肯定要先执行,不然无法被应用,但是你写的装饰器到最后被调用的函数是inner,但是我们需要自己的属性才可以实现。wraps的功能就是将inner赋予调用函数的所有属性和特征。
    import functools
    def auth(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
    if not session.get('user'):
    return redirect(url_for('login'))
    ret = func(*args,**kwargs)
    return ret
    return inner
    @app.route('/index')
    @auth
    def index():
    return render_template('index.html',stu_dic=STUDENT_DICT)
    # 应用场景:比较少的函数中需要额外添加功能。
    # 版本三:before_request,在执行函数时会先执行before_request所定义的函数,返回None就会向后执行,如果返回数据就会直接将其返回到前端了。
    @app.before_request
    def xxxxxx():
    if request.path == '/login':
    return None
    if session.get('user'):
    return None
    return redirect('/login')

     

4.模板渲染

  1. 基本数据类型:可以执行python语法,如:dict.get() list['xx']

  2. 传入函数:django,自动执行\flask,不自动执行

  3. 全局定义函数

    @app.template_global()
    def sb(a1, a2):
    # {{sb(1,9)}}
    return a1 + a2
    @app.template_filter()
    def db(a1, a2, a3):
    # {{ 1|db(2,3) }}
    return a1 + a2 + a3
  4. 模板继承

    layout.html
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    <h1>模板</h1>
    {% block content %}{% endblock %}
    </body>
    </html>
    tpl.html
    {% extends "layout.html"%}
    {% block content %}
    {{users.0}}
    {% endblock %}
  5. include

    {% include "form.html" %}
    form.html
    <form>
    asdfasdf
    asdfasdf
    asdf
    asdf
    </form>
  6. {% macro ccccc(name, type='text', value='') %}
    <h1>宏</h1>
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
    <input type="submit" value="提交">
    {% endmacro %}
    {{ ccccc('n1') }}
    {{ ccccc('n2') }}
  7. flask默认时设置了xss的后端将html代码返回到前端时需要使用安全MarkUp("<div>asdf</div>"),前端也可以直接将其应用{{u|safe}}

5.session

  1. 当请求刚到来:flask读取cookie中session对应的值:eyJrMiI6NDU2LCJ1c2VyIjoib2xkYm95,将该值解密并反序列化成字典,放入内存以便视图函数使用。

    @app.route('/ses')
    def ses():
    session['k1'] = 123
    session['k2'] = 456
    del session['k1']
    return "Session"
    session['xxx'] = 123
    session['xxx']
  2. 当请求结束时,flask会读取内存中字典的值,进行序列化+加密,写入到用户cookie中。

  3. threading.local【和flask无任何关系】

    1. 为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。

      import threading
      from threading import local
      import time
      obj = local()
      def task(i):
      obj.xxxxx = i
      time.sleep(2)
      print(obj.xxxxx,i)
      for i in range(10):
      t = threading.Thread(target=task,args=(i,))
      t.start()
    2. 根据字典自定义一个类似于threading.local功能?

      import time
      import threading
      DIC = {}
      def task(i):
      ident = threading.get_ident()
      if ident in DIC:
      DIC[ident]['xxxxx'] = i
      else:
      DIC[ident] = {'xxxxx':i }
      time.sleep(2)
      print(DIC[ident]['xxxxx'],i)
      for i in range(10):
      t = threading.Thread(target=task,args=(i,))
      t.start()
    3. 通过getattr/setattr 构造出来 threading.local的加强版(协程)

      import time
      import threading
      try:
      import greenlet
      get_ident = greenlet.getcurrent # 获取协程唯一标识
      except Exception as e:
      get_ident = threading.get_ident # 获取线程唯一标识
      class Local(object):
      DIC = {}
      def __getattr__(self, item): # obj.xxx时触发,item等于xxx
      ident = get_ident()
      if ident in self.DIC:
      return self.DIC[ident].get(item)
      return None
      def __setattr__(self, key, value): # obj.xxx = "123"时触发,key等于xxx,value等于123
      ident = get_ident()
      if ident in self.DIC:
      self.DIC[ident][key] = value
      else:
      self.DIC[ident] = {key:value}
      obj = Local()
      def task(i):
      obj.xxxxx = i
      time.sleep(2)
      print(obj.xxxxx,i)
      for i in range(10):
      t = threading.Thread(target=task,args=(i,))
      t.start()

5.1 flask-session

  1. 安装:pip3 install flask-session

  2. 使用

    import redis
    from flask import Flask,request,session
    from flask.sessions import SecureCookieSessionInterface
    from flask_session import Session
    app = Flask(__name__)
    # app.session_interface = SecureCookieSessionInterface()
    # app.session_interface = RedisSessionInterface()
    app.config['SESSION_TYPE'] = 'redis'
    app.config['SESSION_REDIS'] = redis.Redis(host='140.143.227.206',port=6379,password='1234')
    Session(app)
    @app.route('/login')
    def login():
    session['user'] = 'alex'
    return 'asdfasfd'
    @app.route('/home')
    def index():
    print(session.get('user'))
    return '...'
    if __name__ == '__main__':
    app.run()

     

6.闪现和中间件

  1. 闪现,在session中存储一个数据,读取时通过pop将数据移除。

    from flask import Flask,flash,get_flashed_messages
    @app.route('/page1')
    def page1():
    flash('临时数据存储','error')
    flash('sdfsdf234234','error')
    flash('adasdfasdf','info')
    return "Session"
    @app.route('/page2')
    def page2():
    print(get_flashed_messages(category_filter=['error']))
    return "Session"
  2. 中间件

    call方法什么时候出发?
    用户发起请求时,才执行。
    任务:在执行call方法之前,做一个操作,call方法执行之后做一个操作。
    class Middleware(object):
    def __init__(self,old):
    self.old = old
    def __call__(self, *args, **kwargs):
    ret = self.old(*args, **kwargs)
    return ret
    if __name__ == '__main__':
    app.wsgi_app = Middleware(app.wsgi_app)
    app.run()

7.蓝图

  1. 完整目录图

    ProjectName
    -ProjectName
    --static
    --templates
    --views
    ---account.py
    from flask import Blueprint,render_template
    # 创建蓝图对象
    ac = Blueprint('ac',__name__)
    # 只有调用该文件中的页面时才会触发
    @ac.before_request
    def x1():
    print('app.before_request')
    @ac.route('/login')
    def login():
    return render_template('login.html')
    @ac.route('/logout')
    def logout():
    return 'Logout'
    --__init__.py
    from flask import Flask
    from .views.account import ac
    from .views.user import uc
    def create_app():
    app = Flask(__name__)
    # @app.before_request
    # def x1():
    # print('app.before_request')
    # 注册蓝图
    app.register_blueprint(ac)
    # 注册蓝图和编写url前缀
    app.register_blueprint(uc,url_prefix='/api')
    return app
    -manage.py
    from crm import create_app
    app = create_app()
    if __name__ == '__main__':
    app.run()

8.特殊装饰器

  1. before_request and after_request

    from flask import Flask
    app = Flask(__name__)
    @app.before_request
    def x1():
    print('before:x1')
    return '滚'
    @app.before_request
    def xx1():
    print('before:xx1')
    @app.after_request
    def x2(response):
    print('after:x2')
    return response
    @app.after_request
    def xx2(response):
    print('after:xx2')
    return response
    @app.route('/index')
    def index():
    print('index')
    return "Index"
    @app.route('/order')
    def order():
    print('order')
    return "order"
    if __name__ == '__main__':
    app.run()
    # 执行结果是,before的返回会将其返回到页面,但是after还是会执行,并不像django返回后后面的都不会执行
    before:x1
    before:xx1
    request
    after:xx2
    after:x2
  2. before_first_request,只有第一次时才会被触发

    from flask import Flask
    app = Flask(__name__)
    @app.before_first_request
    def x1():
    print('123123')
    @app.route('/index')
    def index():
    print('index')
    return "Index"
    @app.route('/order')
    def order():
    print('order')
    return "order"
    if __name__ == '__main__':
    app.run()
  3. 页面渲染的全局装饰器

    @app.template_global()
    def sb(a1, a2):
    # {{sb(1,9)}}
    return a1 + a2
    @app.template_filter()
    def db(a1, a2, a3):
    # {{ 1|db(2,3) }}
    return a1 + a2 + a3
  4. errorhandler,当报指定的错误时会触发。

    @app.errorhandler(404)
    def not_found(arg):
    print(arg)
    return "没找到"

9.上下文管理

  1. 偏函数

    import functools
    def index(a1,a2):
    return a1 + a2
    # 原来的调用方式
    # ret = index(1,23)
    # print(ret)
    # 偏函数,帮助开发者自动传递参数
    new_func = functools.partial(index,666)
    ret = new_func(1)
    print(ret)
  2. 父类方法的执行

    """
    class Base(object):
    def func(self):
    print('Base.func')
    class Foo(Base):
    def func(self):
    # 方式一:根据mro的顺序执行方法
    # super(Foo,self).func()
    # 方式二:主动执行Base类的方法
    # Base.func(self)
    print('Foo.func')
    obj = Foo()
    obj.func()
    """
    ####################################
    class Base(object):
    def func(self):
    super(Base, self).func()
    print('Base.func')
    class Bar(object):
    def func(self):
    print('Bar.func')
    class Foo(Base,Bar):
    pass
    # 示例一
    # obj = Foo()
    # obj.func()
    # print(Foo.__mro__)
    # 示例二
    # obj = Base()
    # obj.func()
  3. # by luffycity.com
    class Stack(object):
    def __init__(self):
    self.data = []
    def push(self,val):
    self.data.append(val)
    def pop(self):
    return self.data.pop()
    def top(self):
    return self.data[-1]
    _stack = Stack()
    _stack.push('佳俊')
    _stack.push('咸鱼')
    print(_stack.pop())
    print(_stack.pop())
  4. Local

    """
    {
    1232:{k:v}
    }
    """
    try:
    from greenlet import getcurrent as get_ident
    except:
    from threading import get_ident
    """
    class Local(object):
    def __init__(self):
    object.__setattr__(self,'storage',{})
    def __setattr__(self, key, value):
    ident = get_ident()
    if ident not in self.storage:
    self.storage[ident] = {key:value}
    else:
    self.storage[ident][key] = value
    def __getattr__(self, item):
    ident = get_ident()
    if ident in self.storage:
    return self.storage[ident].get(item)
    """
    class Local(object):
    __slots__ = ('__storage__', '__ident_func__')
    def __init__(self):
    # __storage__ = {1231:{'stack':[]}}
    object.__setattr__(self, '__storage__', {})
    object.__setattr__(self, '__ident_func__', get_ident)
    def __getattr__(self, name):
    try:
    return self.__storage__[self.__ident_func__()][name]
    except KeyError:
    raise AttributeError(name)
    def __setattr__(self, name, value):
    ident = self.__ident_func__()
    storage = self.__storage__
    try:
    storage[ident][name] = value
    except KeyError:
    storage[ident] = {name: value}
    def __delattr__(self, name):
    try:
    del self.__storage__[self.__ident_func__()][name]
    except KeyError:
    raise AttributeError(name)
    obj = Local()
    obj.stack = []
    obj.stack.append('佳俊')
    obj.stack.append('咸鱼')
    print(obj.stack)
    print(obj.stack.pop())
    print(obj.stack)
  5. LocalStack

    import functools
    try:
    from greenlet import getcurrent as get_ident
    except:
    from threading import get_ident
    class Local(object):
    __slots__ = ('__storage__', '__ident_func__')
    def __init__(self):
    # __storage__ = {1231:{'stack':[]}}
    object.__setattr__(self, '__storage__', {})
    object.__setattr__(self, '__ident_func__', get_ident)
    def __getattr__(self, name):
    try:
    return self.__storage__[self.__ident_func__()][name]
    except KeyError:
    raise AttributeError(name)
    def __setattr__(self, name, value):
    # name=stack
    # value=[]
    ident = self.__ident_func__()
    storage = self.__storage__
    try:
    storage[ident][name] = value
    except KeyError:
    storage[ident] = {name: value}
    def __delattr__(self, name):
    try:
    del self.__storage__[self.__ident_func__()][name]
    except KeyError:
    raise AttributeError(name)
    """
    __storage__ = {
    12312: {stack:[ctx(session/request) ,]}
    }
    """
    # obj = Local()
    # obj.stack = []
    # obj.stack.append('佳俊')
    # obj.stack.append('咸鱼')
    # print(obj.stack)
    # print(obj.stack.pop())
    # print(obj.stack)
    class LocalStack(object):
    def __init__(self):
    self._local = Local()
    def push(self,value):
    rv = getattr(self._local, 'stack', None) # self._local.stack =>local.getattr
    if rv is None:
    self._local.stack = rv = [] # self._local.stack =>local.setattr
    rv.append(value) # self._local.stack.append(666)
    return rv
    def pop(self):
    """Removes the topmost item from the stack, will return the
    old value or `None` if the stack was already empty.
    """
    stack = getattr(self._local, 'stack', None)
    if stack is None:
    return None
    elif len(stack) == 1:
    return stack[-1]
    else:
    return stack.pop()
    def top(self):
    try:
    return self._local.stack[-1]
    except (AttributeError, IndexError):
    return None
    class RequestContext(object):
    def __init__(self):
    self.request = "xx"
    self.session = 'oo'
    _request_ctx_stack = LocalStack()
    _request_ctx_stack.push(RequestContext())
    def _lookup_req_object(arg):
    ctx = _request_ctx_stack.top()
    return getattr(ctx,arg) # ctx.request / ctx.session
    request = functools.partial(_lookup_req_object,'request')
    session = functools.partial(_lookup_req_object,'session')
    print(request())
    print(session())
  6. slots

    class Foo(object):
    __slots__ = ('name',)
    def __init__(self):
    self.name = 'alex'
    # self.age = 18
    obj = Foo()
    print(obj.name)
    # print(obj.age)
  7. 请求上下文管理(ctx):request,session

    - 请求到来之后wsgi会触发__call__方法,由__call__方法再次调用wsgi_app方法
    - 在wsgi_app方法中:
    - 首先将 请求相关+空session 封装到一个RequestContext对象中,即:ctx。
    - 将ctx交给LocalStack对象,再由LocalStack将ctx添加到Local中,Local结构:
    __storage__ = {
    1231:{stack:[ctx,] }
    }
    - 根据请求中的cookie中提取名称为sessionid对应的值,对cookie进行加密+反序列化,再次赋值给ctx中的session
    -> 视图函数
    - 把session中的数据再次写入到cookie中。
    - 将ctx删除
    - 结果返回给用户浏览器
    - 断开socket连接
  8. LocalProxy

    from flask import Flask,request,session
    app = Flask(__name__)
    @app.route('/index')
    def index():
    # 1. request是LocalProxy对象
    # 2. 对象中有method、执行__getattr__
    print(request.method)
    # request['method']
    # request + 1
    # 1. session是LocalProxy对象
    # 2. LocalProxy对象的__setitem__
    session['x'] = 123
    return "Index"
    if __name__ == '__main__':
    app.run()
    # app.__call__
    # app.wsgi_app
    """
    第一阶段:请求到来
    将request和Session相关数据封装到ctx=RequestContext对象中。
    再通过LocalStack将ctx添加到Local中。
    __storage__ = {
    1231:{'stack':[ctx(request,session)]}
    }
    第二阶段:视图函数中获取request或session
    方式一:直接找LocalStack获取
    from flask.globals import _request_ctx_stack
    print(_request_ctx_stack.top.request.method)
    方式二:通过代理LocalProxy(小东北)获取
    from flask import Flask,request
    print(request.method)
    """
  9. g只存在在一次请求中当你return时g就会被清除。

10.文件的上传和解压

  1. view.py

    from flask import Blueprint, render_template, Flask, request, redirect,session
    import os
    import uuid
    from ..utils import helper
    ind = Blueprint('ind', __name__)
    ind.config["MAX_CONTENT_LENGTH"] = = 1024 * 1024 * 7
    @ind.before_request
    def process_request():
    if not session.get("user_info"):
    return redirect("/login")
    return None
    @ind.route('/home')
    def home():
    return render_template('home.html')
    @ind.route('/user_list')
    def user_list():
    # import pymysql
    # conn = pymysql.Connect(host='127.0.0.1', user='root', password='123456', database='s9day118', charset='utf8')
    # cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    # cursor.execute("SELECT id,user,nickname FROM userinfo")
    # data_list = cursor.fetchall()
    # cursor.close()
    # conn.close()
    data_list = helper.fetch_all("SELECT id,user,nickname FROM userinfo",[])
    return render_template('user_list.html',data_list=data_list)
    @ind.route('/detail/<int:nid>')
    def detail(nid):
    # import pymysql
    # conn = Config.POOL.connection()
    # cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    # cursor.execute("SELECT id,line,ctime FROM record where user_id=%s",(nid,))
    # record_list = cursor.fetchall()
    # cursor.close()
    # conn.close()
    record_list = helper.fetch_all("SELECT id,line,ctime FROM record where user_id=%s",(nid,))
    return render_template('detail.html',record_list=record_list)
    @ind.route('/upload',methods=['GET','POST'])
    def upload():
    if request.method == "GET":
    return render_template('upload.html')
    from werkzeug.datastructures import FileStorage
    file_obj = request.files.get('code')
    # 1. 检查上传文件后缀名
    name_ext = file_obj.filename.rsplit('.',maxsplit=1)
    if len(name_ext) != 2:
    return "请上传zip压缩文件"
    if name_ext[1] != 'zip':
    return "请上传zip压缩文件"
    """
    # 2. 接收用户上传文件,并写入到服务器本地.
    file_path = os.path.join("files",file_obj.filename)
    # 从file_obj.stream中读取内容,写入到文件
    file_obj.save(file_path)
    # 3. 解压zip文件
    import shutil
    # 通过open打开压缩文件,读取内容再进行解压。
    shutil._unpack_zipfile(file_path,'xsadfasdfasdf')
    """
    # 2+3, 接收用户上传文件,并解压到指定目录
    import shutil
    target_path = os.path.join('files',str(uuid.uuid4()))
    shutil._unpack_zipfile(file_obj.stream,target_path)
    # 4. 遍历某目录下的所有文件
    # for item in os.listdir(target_path):
    # print(item)
    total_num = 0
    for base_path,folder_list,file_list in os.walk(target_path):
    for file_name in file_list:
    file_path = os.path.join(base_path,file_name)
    file_ext = file_path.rsplit('.',maxsplit=1)
    if len(file_ext) != 2:
    continue
    if file_ext[1] != 'py':
    continue
    file_num = 0
    with open(file_path,'rb') as f:
    for line in f:
    line = line.strip()
    if not line:
    continue
    if line.startswith(b'#'):
    continue
    file_num += 1
    total_num += file_num
    # 获取当前时间
    import datetime
    ctime = datetime.date.today()
    print(total_num,ctime,session['user_info']['id'])
    # import pymysql
    # conn = pymysql.Connect(host='127.0.0.1', user='root', password='123456', database='s9day118', charset='utf8')
    # cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    # cursor.execute("select id from record where ctime=%s and user_id=%s",(ctime,session['user_info']['id']))
    # data = cursor.fetchone()
    # cursor.close()
    # conn.close()
    data = helper.fetch_one("select id from record where ctime=%s and user_id=%s",(ctime,session['user_info']['id']))
    if data:
    return "今天已经上传"
    # import pymysql
    # conn = pymysql.Connect(host='127.0.0.1', user='root', password='123456', database='s9day118', charset='utf8')
    # cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    # cursor.execute("insert into record(line,ctime,user_id)values(%s,%s,%s)",(total_num,ctime,session['user_info']['id']))
    # conn.commit()
    # cursor.close()
    # conn.close()
    helper.insert("insert into record(line,ctime,user_id)values(%s,%s,%s)",(total_num,ctime,session['user_info']['id']))
    return "上传成功"
  2. html.py

    {% extends "layout.html"%}
    {% block content %}
    <h1>代码上传</h1>
    <form method="post" enctype="multipart/form-data">
    <input type="file" name="code">
    <input type="submit" value="上传">
    </form>
    {% endblock %}

11.Database Join 池

  1. 安装pip3 install DBUtils

  2. 创建连接池可以共享出去,谁要用就来获取一个连接,这样可以管理连接的数量,提高链接的利用率。

    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    import pymysql
    POOL = PooledDB(
           creator=pymysql,  # 使用链接数据库的模块
           maxconnections=6,  # 连接池允许的最大连接数,0None表示不限制连接数
           mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
           maxcached=5,  # 链接池中最多闲置的链接,0None不限制
           maxshared=3,
           # 链接池中最多共享的链接数量,0None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
           blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
           maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
           setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
           ping=0,
           # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
           host='127.0.0.1',
           port=3306,
           user='root',
           password='123456',
           database='s9day118',
           charset='utf8'
      )
    # 获取连接
    conn = POOL.connection()
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    cursor.execute("select * from tb1")
    result = cursor.fetchall()
    cursor.close()
    # 归还连接
    conn.close()

12.wtforms

  1. WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。

  2. 安装pip3 install wtforms

12.1 用户登录注册示例

  1. 用户登录

    1. 当用户登录时候,需要对用户提交的用户名和密码进行多种格式校验。如:

    2. 用户不能为空;用户长度必须大于6;

    3. 密码不能为空;密码长度必须大于12;密码必须包含 字母、数字、特殊字符等(自定义正则);

      #!/usr/bin/env python
      # -*- coding:utf-8 -*-
      from flask import Flask, render_template, request, redirect
      from wtforms import Form
      from wtforms.fields import core
      from wtforms.fields import html5
      from wtforms.fields import simple
      from wtforms import validators
      from wtforms import widgets
      app = Flask(__name__, template_folder='templates')
      app.debug = True
      class LoginForm(Form):
      name = simple.StringField(
      label='用户名',
      validators=[
      validators.DataRequired(message='用户名不能为空.'),
      validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
      ],
      widget=widgets.TextInput(),
      render_kw={'class': 'form-control'}
      )
      pwd = simple.PasswordField(
      label='密码',
      validators=[
      validators.DataRequired(message='密码不能为空.'),
      validators.Length(min=8, message='用户名长度必须大于%(min)d'),
      validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
      message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
      ],
      widget=widgets.PasswordInput(),
      render_kw={'class': 'form-control'}
      )
      @app.route('/login', methods=['GET', 'POST'])
      def login():
      if request.method == 'GET':
      form = LoginForm()
      return render_template('login.html', form=form)
      else:
      form = LoginForm(formdata=request.form)
      if form.validate():
      print('用户提交数据通过格式验证,提交的值为:', form.data)
      else:
      print(form.errors)
      return render_template('login.html', form=form)
      if __name__ == '__main__':
      app.run()
      #######################################################
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>Title</title>
      </head>
      <body>
      <h1>登录</h1>
      <form method="post">
      <!--<input type="text" name="name">-->
      <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
      <!--<input type="password" name="pwd">-->
      <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
      <input type="submit" value="提交">
      </form>
      </body>
      </html>
  2. 用户注册

    1. 注册页面需要让用户输入:用户名、密码、密码重复、性别、爱好等。

      from flask import Flask, render_template, request, redirect
      from wtforms import Form
      from wtforms.fields import core
      from wtforms.fields import html5
      from wtforms.fields import simple
      from wtforms import validators
      from wtforms import widgets
      app = Flask(__name__, template_folder='templates')
      app.debug = True
      class RegisterForm(Form):
      name = simple.StringField(
      label='用户名',
      validators=[
      validators.DataRequired()
      ],
      widget=widgets.TextInput(),
      render_kw={'class': 'form-control'},
      default='alex'
      )
      pwd = simple.PasswordField(
      label='密码',
      validators=[
      validators.DataRequired(message='密码不能为空.')
      ],
      widget=widgets.PasswordInput(),
      render_kw={'class': 'form-control'}
      )
      pwd_confirm = simple.PasswordField(
      label='重复密码',
      validators=[
      validators.DataRequired(message='重复密码不能为空.'),
      validators.EqualTo('pwd', message="两次密码输入不一致")
      ],
      widget=widgets.PasswordInput(),
      render_kw={'class': 'form-control'}
      )
      email = html5.EmailField(
      label='邮箱',
      validators=[
      validators.DataRequired(message='邮箱不能为空.'),
      validators.Email(message='邮箱格式错误')
      ],
      widget=widgets.TextInput(input_type='email'),
      render_kw={'class': 'form-control'}
      )
      gender = core.RadioField(
      label='性别',
      choices=(
      (1, '男'),
      (2, '女'),
      ),
      coerce=int
      )
      city = core.SelectField(
      label='城市',
      choices=(
      ('bj', '北京'),
      ('sh', '上海'),
      )
      )
      hobby = core.SelectMultipleField(
      label='爱好',
      choices=(
      (1, '篮球'),
      (2, '足球'),
      ),
      coerce=int
      )
      favor = core.SelectMultipleField(
      label='喜好',
      choices=(
      (1, '篮球'),
      (2, '足球'),
      ),
      widget=widgets.ListWidget(prefix_label=False),
      option_widget=widgets.CheckboxInput(),
      coerce=int,
      default=[1, 2]
      )
      def __init__(self, *args, **kwargs):
      super(RegisterForm, self).__init__(*args, **kwargs)
      self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
      def validate_pwd_confirm(self, field):
      """
      自定义pwd_confirm字段规则,例:与pwd字段是否一致
      :param field:
      :return:
      """
      # 最开始初始化时,self.data中已经有所有的值
      if field.data != self.data['pwd']:
      # raise validators.ValidationError("密码不一致") # 继续后续验证
      raise validators.StopValidation("密码不一致") # 不再继续后续验证
      @app.route('/register', methods=['GET', 'POST'])
      def register():
      if request.method == 'GET':
      form = RegisterForm(data={'gender': 1})
      return render_template('register.html', form=form)
      else:
      form = RegisterForm(formdata=request.form)
      if form.validate():
      print('用户提交数据通过格式验证,提交的值为:', form.data)
      else:
      print(form.errors)
      return render_template('register.html', form=form)
      if __name__ == '__main__':
      app.run()
      ############################################################
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>Title</title>
      </head>
      <body>
      <h1>用户注册</h1>
      <form method="post" novalidate style="padding:0 50px">
      {% for item in form %}
      <p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
      {% endfor %}
      <input type="submit" value="提交">
      </form>
      </body>
      </html>
  3. 动态获取数据库数据

    from flask import Flask,request,render_template,session,current_app,g,redirect
    from wtforms import Form
    from wtforms.fields import simple
    from wtforms.fields import html5
    from wtforms.fields import core
    from wtforms import widgets
    from wtforms import validators
    app = Flask(__name__)
    class LoginForm(Form):
    name = simple.StringField(
    validators=[
    validators.DataRequired(message='用户名不能为空.'),
    # validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
    ],
    widget=widgets.TextInput(),
    render_kw={'placeholder':'请输入用户名'}
    )
    pwd = simple.PasswordField(
    validators=[
    validators.DataRequired(message='密码不能为空.'),
    # validators.Length(min=8, message='用户名长度必须大于%(min)d'),
    # validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
    # message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
    ],
    render_kw={'placeholder':'请输入密码'}
    )
    @app.route('/login',methods=['GET','POST'])
    def login():
    if request.method == "GET":
    form = LoginForm()
    # print(form.name,type(form.name)) # form.name是StringField()对象, StringField().__str__
    # print(form.pwd,type(form.pwd)) # form.pwd是PasswordField()对象,PasswordField().__str__
    return render_template('login.html',form=form)
    form = LoginForm(formdata=request.form)
    if form.validate():
    print(form.data)
    return redirect('https://www.luffycity.com/home')
    else:
    # print(form.errors)
    return render_template('login.html', form=form)
    import helper
    class UserForm(Form):
    city = core.SelectField(
    label='城市',
    choices=(),
    coerce=int # 从数据库获取的数据返回后是字符串类型的,把他写上后会先将其转为int后再返回到前端
    )
    name = simple.StringField(label='姓名')
    # 当你定义成静态字段是,只有启动时才会加载一次,但是从数据库需要实时拿到数据,下面是解决方法。你应该可以看懂。
    def __init__(self,*args,**kwargs):
    super(UserForm,self).__init__(*args,**kwargs)
    self.city.choices=helper.fetch_all('select id,name from tb1',[],type=None)
    @app.route('/user')
    def user():
    if request.method == "GET":
    #form = UserForm(data={'name':'alex','city':3})
    form = UserForm()
    return render_template('user.html',form=form)
    if __name__ == '__main__':
    app.run()

     

  4. meta

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Flask, render_template, request, redirect, session
    from wtforms import Form
    from wtforms.csrf.core import CSRF
    from wtforms.fields import core
    from wtforms.fields import html5
    from wtforms.fields import simple
    from wtforms import validators
    from wtforms import widgets
    from hashlib import md5
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    class MyCSRF(CSRF):
       """
      Generate a CSRF token based on the user's IP. I am probably not very
      secure, so don't use me.
      """
       def setup_form(self, form):
           self.csrf_context = form.meta.csrf_context()
           self.csrf_secret = form.meta.csrf_secret
           return super(MyCSRF, self).setup_form(form)
       def generate_csrf_token(self, csrf_token):
           gid = self.csrf_secret + self.csrf_context
           token = md5(gid.encode('utf-8')).hexdigest()
           return token
       def validate_csrf_token(self, form, field):
           print(field.data, field.current_token)
           if field.data != field.current_token:
               raise ValueError('Invalid CSRF')
    class TestForm(Form):
       name = html5.EmailField(label='用户名')
       pwd = simple.StringField(label='密码')
       class Meta:
           # -- CSRF
           # 是否自动生成CSRF标签
           csrf = True
           # 生成CSRF标签name
           csrf_field_name = 'csrf_token'
           # 自动生成标签的值,加密用的csrf_secret
           csrf_secret = 'xxxxxx'
           # 自动生成标签的值,加密用的csrf_context
           csrf_context = lambda x: request.url
           # 生成和比较csrf标签
           csrf_class = MyCSRF
           # -- i18n
           # 是否支持本地化
           # locales = False
           locales = ('zh', 'en')
           # 是否对本地化进行缓存
           cache_translations = True
           # 保存本地化缓存信息的字段
           translations_cache = {}
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
       if request.method == 'GET':
           form = TestForm()
       else:
           form = TestForm(formdata=request.form)
           if form.validate():
               print(form)
       return render_template('index.html', form=form)
    if __name__ == '__main__':
       app.run()

12.2 其他

  1. metaclass

    # new方法的返回值决定对象到底是什么?
    class Bar(object):
    pass
    class Foo(object):
    def __new__(cls, *args, **kwargs):
    # return super(Foo,cls).__new__(cls,*args, **kwargs)
    return Bar()
    obj = Foo()
    print(obj)
    # 当Foo()时先执行__new__方法,返回一个Bar,obj就等于Bar对象
    ##########################################################################
    class MyType(type):
    def __init__(self, *args, **kwargs):
    print('MyType创建类',self)
    super(MyType, self).__init__(*args, **kwargs)
    def __call__(self, *args, **kwargs):
    obj = super(MyType, self).__call__(*args, **kwargs)
    print('类创建对象', self, obj)
    return obj
    class Foo(object,metaclass=MyType):
    user = 'wupeiqi'
    age = 18
    obj = Foo()
    # 在创建Foo对象前先执行mytype的__call__方法,__call__方法后才会执行Foo。不写metaclass默认是由type类来做的。
    # 是先执行父类的__call__后再执行自己的__init__方法。
    """
    创建类时,先执行type的__init__。
    类的实例化时,执行type的__call__,__call__方法的的返回值就是实例化的对象。
    __call__内部调用:
    类.__new__,创建对象
    类.__init__,对象的初始化
    """
  2. 实例化流程分析

    # 源码流程
    1. 执行type的 __call__ 方法,读取字段到静态字段 cls._unbound_fields 中; meta类读取到cls._wtforms_meta中
    2. 执行构造方法
    a. 循环cls._unbound_fields中的字段,并执行字段的bind方法,然后将返回值添加到 self._fields[name] 中。
    即:
    _fields = {
    name: wtforms.fields.core.StringField(),
    }
    PS:由于字段中的__new__方法,实例化时:name = simple.StringField(label='用户名'),创建的是UnboundField(cls, *args, **kwargs),当执行完bind之后,才变成执行 wtforms.fields.core.StringField()
    b. 循环_fields,为对象设置属性
    for name, field in iteritems(self._fields):
    # Set all the fields to attributes so that they obscure the class
    # attributes with the same names.
    setattr(self, name, field)
    c. 执行process,为字段设置默认值:self.process(formdata, obj, data=data, **kwargs)
    优先级:obj,data,formdata;
    再循环执行每个字段的process方法,为每个字段设置值:
    for name, field, in iteritems(self._fields):
    if obj is not None and hasattr(obj, name):
    field.process(formdata, getattr(obj, name))
    elif name in kwargs:
    field.process(formdata, kwargs[name])
    else:
    field.process(formdata)
    执行每个字段的process方法,为字段的data和字段的raw_data赋值
    def process(self, formdata, data=unset_value):
    self.process_errors = []
    if data is unset_value:
    try:
    data = self.default()
    except TypeError:
    data = self.default
    self.object_data = data
    try:
    self.process_data(data)
    except ValueError as e:
    self.process_errors.append(e.args[0])
    if formdata:
    try:
    if self.name in formdata:
    self.raw_data = formdata.getlist(self.name)
    else:
    self.raw_data = []
    self.process_formdata(self.raw_data)
    except ValueError as e:
    self.process_errors.append(e.args[0])
    try:
    for filter in self.filters:
    self.data = filter(self.data)
    except ValueError as e:
    self.process_errors.append(e.args[0])
    d. 页面上执行print(form.name) 时,打印标签
    因为执行了:
    字段的 __str__ 方法
    字符的 __call__ 方法
    self.meta.render_field(self, kwargs)
    def render_field(self, field, render_kw):
    other_kw = getattr(field, 'render_kw', None)
    if other_kw is not None:
    render_kw = dict(other_kw, **render_kw)
    return field.widget(field, **render_kw)
    执行字段的插件对象的 __call__ 方法,返回标签字符串
  3. 验证流程分析

    a. 执行form的validate方法,获取钩子方法
    def validate(self):
    extra = {}
    for name in self._fields:
    inline = getattr(self.__class__, 'validate_%s' % name, None)
    if inline is not None:
    extra[name] = [inline]
    return super(Form, self).validate(extra)
    b. 循环每一个字段,执行字段的 validate 方法进行校验(参数传递了钩子函数)
    def validate(self, extra_validators=None):
    self._errors = None
    success = True
    for name, field in iteritems(self._fields):
    if extra_validators is not None and name in extra_validators:
    extra = extra_validators[name]
    else:
    extra = tuple()
    if not field.validate(self, extra):
    success = False
    return success
    c. 每个字段进行验证时候
    字段的pre_validate 【预留的扩展】
    字段的_run_validation_chain,对正则和字段的钩子函数进行校验
    字段的post_validate【预留的扩展】

13.SQLAlchemy

  1. MySQLdb,只支持python2.0的,而pymysql是支持python2.0和3.0的。

    import MySQLdb
    conn = MySQLdb.connect(host='127.0.0.1',user='root',passwd='1234',db='mydb')
    cur = conn.cursor()
    reCount = cur.execute('insert into UserInfo(Name,Address) values(%s,%s)',('alex','usa'))
    # reCount = cur.execute('insert into UserInfo(Name,Address) values(%(id)s, %(name)s)',{'id':12345,'name':'wupeiqi'})
    conn.commit()
    cur.close()
    conn.close()
    print reCount
  2. SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。

  3. 安装pip3 install sqlalchemy

  4. 组成部分:

    • Engine,框架的引擎

    • Connection Pooling ,数据库连接池

    • Dialect,选择连接数据库的DB API种类

    • Schema/Types,架构和类型

    • SQL Exprression Language,SQL表达式语言

  5. SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

    MySQL-Python
    mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
    pymysql
    mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
    MySQL-Connector
    mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
    cx_Oracle
    oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
    更多:http://docs.sqlalchemy.org/en/latest/dialects/index.html
  6. 执行原生SQL语句

    import time
    import threading
    import sqlalchemy
    from sqlalchemy import create_engine
    from sqlalchemy.engine.base import Engine
    engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/t1?charset=utf8",
    max_overflow=0, # 超过连接池大小外最多创建的连接
    pool_size=5, # 连接池大小
    pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    def task(arg):
    conn = engine.raw_connection()
    cursor = conn.cursor()
    cursor.execute(
    "select * from t1"
    )
    result = cursor.fetchall()
    cursor.close()
    conn.close()
    for i in range(20):
    t = threading.Thread(target=task, args=(i,))
    t.start()
    ########################################################################################
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    import sqlalchemy
    from sqlalchemy import create_engine
    from sqlalchemy.engine.base import Engine
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=0, pool_size=5)
    def task(arg):
    conn = engine.contextual_connect()
    with conn:
    cur = conn.execute(
    "select * from t1"
    )
    result = cur.fetchall()
    print(result)
    for i in range(20):
    t = threading.Thread(target=task, args=(i,))
    t.start()
    ########################################################################################
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    import sqlalchemy
    from sqlalchemy import create_engine
    from sqlalchemy.engine.base import Engine
    from sqlalchemy.engine.result import ResultProxy
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=0, pool_size=5)
    def task(arg):
    cur = engine.execute("select * from t1")
    result = cur.fetchall()
    cur.close()
    print(result)
    for i in range(20):
    t = threading.Thread(target=task, args=(i,))
    t.start()
    # 注意: 查看连接 show status like 'Threads%';

13.1 ORM

  1. 创建单表

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import datetime
    from sqlalchemy import create_engine
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
    Base = declarative_base()
    class Users(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), index=True, nullable=False)
    # email = Column(String(32), unique=True)
    # ctime = Column(DateTime, default=datetime.datetime.now)
    # extra = Column(Text, nullable=True)
    __table_args__ = (
    # UniqueConstraint('id', 'name', name='uix_id_name'),
    # Index('ix_id_name', 'name', 'email'),
    )
    def init_db():
    """
    根据类创建数据库表
    :return:
    """
    engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8",
    max_overflow=0, # 超过连接池大小外最多创建的连接
    pool_size=5, # 连接池大小
    pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.create_all(engine)
    def drop_db():
    """
    根据类删除数据库表
    :return:
    """
    engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8",
    max_overflow=0, # 超过连接池大小外最多创建的连接
    pool_size=5, # 连接池大小
    pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.drop_all(engine)
    if __name__ == '__main__':
    drop_db()
    init_db()
  2. 创建多个表并包含Fk、M2M关系

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import datetime
    from sqlalchemy import create_engine
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
    from sqlalchemy.orm import relationship
    Base = declarative_base()
    # ##################### 单表示例 #########################
    class Users(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), index=True)
    age = Column(Integer, default=18)
    email = Column(String(32), unique=True)
    ctime = Column(DateTime, default=datetime.datetime.now)
    extra = Column(Text, nullable=True)
    __table_args__ = (
    # UniqueConstraint('id', 'name', name='uix_id_name'),
    # Index('ix_id_name', 'name', 'extra'),
    )
    class Hosts(Base):
    __tablename__ = 'hosts'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), index=True)
    ctime = Column(DateTime, default=datetime.datetime.now)
    # ##################### 一对多示例 #########################
    class Hobby(Base):
    __tablename__ = 'hobby'
    id = Column(Integer, primary_key=True)
    caption = Column(String(50), default='篮球')
    class Person(Base):
    __tablename__ = 'person'
    nid = Column(Integer, primary_key=True)
    name = Column(String(32), index=True, nullable=True)
    hobby_id = Column(Integer, ForeignKey("hobby.id"))
    # 与生成表结构无关,仅用于查询方便
    hobby = relationship("Hobby", backref='pers')
    # ##################### 多对多示例 #########################
    class Server2Group(Base):
    __tablename__ = 'server2group'
    id = Column(Integer, primary_key=True, autoincrement=True)
    server_id = Column(Integer, ForeignKey('server.id'))
    group_id = Column(Integer, ForeignKey('group.id'))
    class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True, nullable=False)
    # 与生成表结构无关,仅用于查询方便
    servers = relationship('Server', secondary='server2group', backref='groups')
    class Server(Base):
    __tablename__ = 'server'
    id = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(64), unique=True, nullable=False)
    def init_db():
    """
    根据类创建数据库表
    :return:
    """
    engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8",
    max_overflow=0, # 超过连接池大小外最多创建的连接
    pool_size=5, # 连接池大小
    pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.create_all(engine)
    def drop_db():
    """
    根据类删除数据库表
    :return:
    """
    engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8",
    max_overflow=0, # 超过连接池大小外最多创建的连接
    pool_size=5, # 连接池大小
    pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.drop_all(engine)
    if __name__ == '__main__':
    drop_db()
    init_db()
  3. 操作数据库表

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy import create_engine
    from models import Users
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    # 每次执行数据库操作时,都需要创建一个session
    session = Session()
    # ############# 执行ORM操作 #############
    obj1 = Users(name="alex1")
    session.add(obj1)
    # 提交事务
    session.commit()
    # 关闭session
    session.close()
  4. 基于scoped_session实现线程安全

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy import create_engine
    from sqlalchemy.orm import scoped_session
    from models import Users
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    """
    # 线程安全,基于本地线程实现每个线程用同一个session
    # 特殊的:scoped_session中有原来方法的Session中的一下方法:
    public_methods = (
    '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested',
    'close', 'commit', 'connection', 'delete', 'execute', 'expire',
    'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind',
    'is_modified', 'bulk_save_objects', 'bulk_insert_mappings',
    'bulk_update_mappings',
    'merge', 'query', 'refresh', 'rollback',
    'scalar'
    )
    """
    session = scoped_session(Session)
    # ############# 执行ORM操作 #############
    obj1 = Users(name="alex1")
    session.add(obj1)
    # 提交事务
    session.commit()
    # 关闭session
    session.close()
  5. 多线程执行示例

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    from db import Users
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    def task(arg):
    session = Session()
    obj1 = Users(name="alex1")
    session.add(obj1)
    session.commit()
    for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start()
  6. 基本增删改查示例

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    from sqlalchemy.sql import text
    from db import Users, Hosts
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    session = Session()
    # ################ 添加 ################
    """
    obj1 = Users(name="wupeiqi")
    session.add(obj1)
    session.add_all([
    Users(name="wupeiqi"),
    Users(name="alex"),
    Hosts(name="c1.com"),
    ])
    session.commit()
    """
    # ################ 删除 ################
    """
    session.query(Users).filter(Users.id > 2).delete()
    session.commit()
    """
    # ################ 修改 ################
    """
    session.query(Users).filter(Users.id > 0).update({"name" : "099"})
    session.query(Users).filter(Users.id > 0).update({Users.name: Users.name + "099"}, synchronize_session=False)
    session.query(Users).filter(Users.id > 0).update({"age": Users.age + 1}, synchronize_session="evaluate")
    session.commit()
    """
    # ################ 查询 ################
    """
    r1 = session.query(Users).all()
    r2 = session.query(Users.name.label('xx'), Users.age).all()
    r3 = session.query(Users).filter(Users.name == "alex").all()
    r4 = session.query(Users).filter_by(name='alex').all()
    r5 = session.query(Users).filter_by(name='alex').first()
    r6 = session.query(Users).filter(text("id<:value and name=:name")).params(value=224, name='fred').order_by(Users.id).all()
    r7 = session.query(Users).from_statement(text("SELECT * FROM users where name=:name")).params(name='ed').all()
    """
    session.close()
  7. 常用操作

    # 条件
    ret = session.query(Users).filter_by(name='alex').all()
    ret = session.query(Users).filter(Users.id > 1, Users.name == 'eric').all()
    ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'eric').all()
    ret = session.query(Users).filter(Users.id.in_([1,3,4])).all()
    ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all()
    ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='eric'))).all()
    from sqlalchemy import and_, or_
    ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all()
    ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all()
    ret = session.query(Users).filter(
    or_(
    Users.id < 2,
    and_(Users.name == 'eric', Users.id > 3),
    Users.extra != ""
    )).all()
    # 通配符
    ret = session.query(Users).filter(Users.name.like('e%')).all()
    ret = session.query(Users).filter(~Users.name.like('e%')).all()
    # 限制
    ret = session.query(Users)[1:2]
    # 排序
    ret = session.query(Users).order_by(Users.name.desc()).all()
    ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all()
    # 分组
    from sqlalchemy.sql import func
    ret = session.query(Users).group_by(Users.extra).all()
    ret = session.query(
    func.max(Users.id),
    func.sum(Users.id),
    func.min(Users.id)).group_by(Users.name).all()
    ret = session.query(
    func.max(Users.id),
    func.sum(Users.id),
    func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) >2).all()
    # 连表
    ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all()
    ret = session.query(Person).join(Favor).all()
    ret = session.query(Person).join(Favor, isouter=True).all()
    # 组合
    q1 = session.query(Users.name).filter(Users.id > 2)
    q2 = session.query(Favor.caption).filter(Favor.nid < 2)
    ret = q1.union(q2).all()
    q1 = session.query(Users.name).filter(Users.id > 2)
    q2 = session.query(Favor.caption).filter(Favor.nid < 2)
    ret = q1.union_all(q2).all()
  8. 原生SQL语句

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    from sqlalchemy.sql import text
    from sqlalchemy.engine.result import ResultProxy
    from db import Users, Hosts
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    session = Session()
    # 查询
    # cursor = session.execute('select * from users')
    # result = cursor.fetchall()
    # 添加
    cursor = session.execute('insert into users(name) values(:value)',params={"value":'wupeiqi'})
    session.commit()
    print(cursor.lastrowid)
    session.close()
  9. 基于relationship操作ForeignKey

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    from sqlalchemy.sql import text
    from sqlalchemy.engine.result import ResultProxy
    from db import Users, Hosts, Hobby, Person
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    session = Session()
    # 添加
    """
    session.add_all([
    Hobby(caption='乒乓球'),
    Hobby(caption='羽毛球'),
    Person(name='张三', hobby_id=3),
    Person(name='李四', hobby_id=4),
    ])
    person = Person(name='张九', hobby=Hobby(caption='姑娘'))
    session.add(person)
    hb = Hobby(caption='人妖')
    hb.pers = [Person(name='文飞'), Person(name='博雅')]
    session.add(hb)
    session.commit()
    """
    # 使用relationship正向查询
    """
    v = session.query(Person).first()
    print(v.name)
    print(v.hobby.caption)
    """
    # 使用relationship反向查询
    """
    v = session.query(Hobby).first()
    print(v.caption)
    print(v.pers)
    """
    session.close()
  10. 基于relationship操作m2m

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    from sqlalchemy.sql import text
    from sqlalchemy.engine.result import ResultProxy
    from db import Users, Hosts, Hobby, Person, Group, Server, Server2Group
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    session = Session()
    # 添加
    """
    session.add_all([
      Server(hostname='c1.com'),
      Server(hostname='c2.com'),
      Group(name='A组'),
      Group(name='B组'),
    ])
    session.commit()
    s2g = Server2Group(server_id=1, group_id=1)
    session.add(s2g)
    session.commit()
    gp = Group(name='C组')
    gp.servers = [Server(hostname='c3.com'),Server(hostname='c4.com')]
    session.add(gp)
    session.commit()
    ser = Server(hostname='c6.com')
    ser.groups = [Group(name='F组'),Group(name='G组')]
    session.add(ser)
    session.commit()
    """
    # 使用relationship正向查询
    """
    v = session.query(Group).first()
    print(v.name)
    print(v.servers)
    """
    # 使用relationship反向查询
    """
    v = session.query(Server).first()
    print(v.hostname)
    print(v.groups)
    """
    session.close()
  11. 其他

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import threading
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    from sqlalchemy.sql import text, func
    from sqlalchemy.engine.result import ResultProxy
    from db import Users, Hosts, Hobby, Person, Group, Server, Server2Group
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8", max_overflow=0, pool_size=5)
    Session = sessionmaker(bind=engine)
    session = Session()
    # 关联子查询
    subqry = session.query(func.count(Server.id).label("sid")).filter(Server.id == Group.id).correlate(Group).as_scalar()
    result = session.query(Group.name, subqry)
    """
    SELECT `group`.name AS group_name, (SELECT count(server.id) AS sid
    FROM server
    WHERE server.id = `group`.id) AS anon_1
    FROM `group`
    """
    # 原生SQL
    """
    # 查询
    cursor = session.execute('select * from users')
    result = cursor.fetchall()
    # 添加
    cursor = session.execute('insert into users(name) values(:value)',params={"value":'wupeiqi'})
    session.commit()
    print(cursor.lastrowid)
    """
    session.close()

14.flask武装

  1. Flask_SQLAlchemy

    a. 下载安装
    pip3 install flask-sqlalchemy
    b. chun.__init__.py
    导入并实例化SQLAlchemy
    from flask_sqlalchemy import SQLAlchemy
    db = SQLAlchemy()
    注意事项:
    - 必须在导入蓝图之前
    - 必须导入models.py
    c. 初始化
    db.init_app(app)
    d. 在配置文件中写入配置
    # ##### SQLALchemy配置文件 #####
    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:123456@127.0.0.1:3306/s9day122?charset=utf8"
    SQLALCHEMY_POOL_SIZE = 10
    SQLALCHEMY_MAX_OVERFLOW = 5
    e. 创建models.py中的类(对应数据库表)
    chun/models.py
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column
    from sqlalchemy import Integer,String,Text,Date,DateTime
    from sqlalchemy import create_engine
    from chun import db
    class Users(db.Model):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), index=True, nullable=False)
    depart_id = Column(Integer)
    f. 生成表(使用app上下文)
    from chun import db,create_app
    app = create_app()
    app_ctx = app.app_context() # app_ctx = app/g
    with app_ctx: # __enter__,通过LocalStack放入Local
    db.create_all() # 调用LocalStack放入Local中获取app,再去app中获取配置
    g. 基于ORM对数据库进行操作。
    from flask import Blueprint
    from chun import db
    from chun import models
    us = Blueprint('us',__name__)
    @us.route('/index')
    def index():
    # 使用SQLAlchemy在数据库中插入一条数据
    # db.session.add(models.Users(name='高件套',depart_id=1))
    # db.session.commit()
    # db.session.remove()
    result = db.session.query(models.Users).all()
    print(result)
    db.session.remove()
    return 'Index'
  2. flask-script

    install the module "pip3 install flask-script "
    a. 增加 runserver
       from chun import create_app
       from flask_script import Manager
       app = create_app()
       manager = Manager(app)
       if __name__ == '__main__':
           # app.run()
           manager.run()
           
    b. 位置传参
       from chun import create_app
       from flask_script import Manager
       app = create_app()
       manager = Manager(app)
       @manager.command
       def custom(arg):
           """
          自定义命令
          python manage.py custom 123
          :param arg:
          :return:
          """
           print(arg)
       if __name__ == '__main__':
           # app.run()
           manager.run()
    c. 关键字传参
       from chun import create_app
       from flask_script import Manager
       app = create_app()
       manager = Manager(app)
       @manager.option('-n', '--name', dest='name')
       @manager.option('-u', '--url', dest='url')
       def cmd(name, url):
           """
          自定义命令
          执行: python manage.py cmd -n wupeiqi -u http://www.oldboyedu.com
          :param name:
          :param url:
          :return:
          """
           print(name, url)
       if __name__ == '__main__':
           # app.run()
           manager.run()
  3. flask-migrate

    install the module "pip3 install flask-migrate"
    flask-migrate rely on flask-script.
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from sansa import create_app
    from sansa import db
    from flask_script import Manager
    from flask_migrate import Migrate, MigrateCommand
    app = create_app()
    manager = Manager(app)
    Migrate(app, db)
    """
    # 数据库迁移命名
      python manage.py db init
      python manage.py db migrate
      python manage.py db upgrade
    """
    manager.add_command('db', MigrateCommand)
    if __name__ == '__main__':
       manager.run()
       # app.run()
  4. 找到项目使用的所有组件和版本。

    install the module "pip install pipreqs"
    # 终端输入后就可以找到所有的依赖
    pipreqs ./ --encoding=utf-8
  5. python虚拟环境

    install module "pip3 install virtualenv"
    create "virtualenv env1 --no-site-packages"
    找到虚拟环境的script后输入"activate"激活或"deactivate"退出。
posted @   無敗の草  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界
点击右上角即可分享
微信分享提示