# python-note-hero **Repository Path**: lafen/python-note-hero ## Basic Information - **Project Name**: python-note-hero - **Description**: python小栗子,仅供学习 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-06-20 - **Last Updated**: 2022-05-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: Python ## README # python 语法练习 demo 1. 环境准备 [Python 3.9.5](https://www.python.org/downloads/windows/) ```shell python -V > Python 3.9.5 ``` [PyCharm](https://www.jetbrains.com/pycharm/download/#section=windows) Community 社区版(免费) 2. 基本语法 - 交互式编程 ```shell # 安装完python,配置好环境路径后,在命令窗口中输入 python 即可开始 python 编程 python >>>print('here') here >>> ``` - 注释 ```python # 单行注释 使用 # ''' 多行注释 使用 ''' 或 """ ''' ``` - 打印 ```python print('hello world') ``` - 缩进 ```python ''' python 中使用缩进表示代码块,不需要使用 {} ,同一代码块缩进必须相同,函数间一般空出两行,一行语句太长可以使用反斜杠 \ 实现多行语句 ''' if True: print('Ops~~ True') else: print('No~~ False') ``` - 6 种标准数据类型 > 在 python2 中是没有 bool 类型,使用 1 和 0 来表示 True 和 False,但在 python3 中将其定为 True 和 False,但他们的值还是 1 和 0,可以和数字进行相加 ```python ''' Number String List(列表) Tuple(元组) Set(集合) Dictionary(字典) ''' # 不可变数据3个:Number、String、Tuple # 可变数据3个:List、Dictionary、Set ``` - 类型判断 type(a)、isinstance(a, int) ```python # type(a) == int、isinstance(a, int) a = 20 b = '20', c = True print(type(a), type(b), type(c)) # # # # 其余类型 # # # # # ``` 两种区别: - type() 不会认为子类是一种父类类型; - isinstance() 会认为子类是一种父类类型 ```python # pass 为占位语句,不做任何事情 class A: pass class B(A): pass isinstance(A(), A) # True type(A()) == A # True isintance(B(), A) # True type(B()) == A # False ``` - 变量定义 - python 中变量无需声明关键字,直接赋值使用。 - python 中变量就是变量,没有类型,类型 指的是 变量所指向 的内存中 对象 的类型。 ```python # 字符串 foo = 'foo' # 数字:int、bool、float、complex # int(整数):1 # bool(布尔):True # float(浮点数):1.23、3E-2 # complex(复数):1 + 2j num = 1 # 一起赋值 a, b, c = 1, 2, 'sss' ``` - 函数定义 - 函数定义使用 def 关键字 - 无 return 则相当于返回 None - python 中参数传递 始终是 引用 传递,因为 python 中变量 是引用地址 ```python def func(a): print(id(a) # 与外面 的 相同 a = '10' print(id(a)) # 重新指向了另一个地址 # 传入不可变数据,则指向 的是同一个地址,但是重新赋值后则改变; # 传入可变参数则都会改变,指向任一致 a = 'python' print(id(a)) func(a) ``` - 必需参数(声明与调用函数顺序一致) ```python def func(s): pass func('ss') ``` - 关键字参数(声明与调用可不一致,但传入时需使用关键字做参数) ```python def func(a, s): pass # 指定字段,不用按顺序 func(s = 'sss', a = 123) ``` - 默认参数(声明时给定默认参数) ```python # s 不传入 则 为 10 def func(s = 10): pass func() ``` - 不定长参数(加 * 的参数会以元组形式传入,加 ** 则会以字典形式传入,单独出现 * ,则后面的参数必须使用关键字传入) ```python # 以元组形式传入 def func(a, *vartuple): print(a, vartuple) func(1, 2, 3, 6, 8) # 以字典形式传入 def func2(a, **vardict): print(a, vardict) func2(1, a=2, b=3, c=8) # * 后面必须以关键字传入 def f(a, b, *, c): pass f(1, 3, c=99) ``` - 强制位置参数 - 匿名函数 ```python # 使用 lambda来创建匿名函数,只是一个表达式 sum = lambda a1, a2: a1 + a2 ``` - 命名空间和作用域 - 命名空间查找顺序 局部命名空间 --> 全局命名空间 --> 内置命名空间 - 生命周期 取决于对象的作用域,对新那个执行完成,则该命名空间的生命周期结束,故无法从外部命名空间访问内部命名空间 ```python # var1 是全局名称 var1 = 5 def some_func(): # var2 是局部名称 var2 = 6 def some_inner_func(): # var3 是内嵌的局部名称 var3 = 7 ``` - 作用域 四种作用域,从局部作用域依次往外查找 - L(Local):最内层 - E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量 - G(Global):当前模块的全局变量 - B(Built-in):包含了内建的变量、关键字等,最后才被搜索到 python 中只有模块(module)、类(class)、函数(def、lambda)才会引入新的作用域,其他代码块则不会,如( if/elif/else/、try/except、for/while等 ),这些语句中定义的变量,外部也可访问 ```python if True: msg = 'TTTT' print(msg) ``` - global、nonlocal 关键字 ```python # 修改全局变量 num = 1 def func(): global num num = 2 func() # 修改嵌套作用域的变量 def outer(): num = 10 def inner(): nonlocal num # nonlocal关键字声明 num = 100 print(num) inner() print(num) outer() ``` - 异常处理、抛出异常 ```python # 异常捕获 try: pass except Exception: print('...error...') else: print('...well done...') finally: print('...always run...') # 抛出异常 raise Exception('抛出一个异常') ``` - 常用方法 - 字符串、列表截取 ```python # 变量[头下标: 尾下标] # 左开右闭合 # """ 三引号定义多行字符串 s = 'python' ss = r'\nsssss' # r 表示 raw string,\n 会被当作字符串输出 s[0:] # 全部 s[0:1] s[2:3] s[0:-1] print("我是 %s 今年 %d 岁" % ('小明', 18)) # 格式化输出 f'这是字符串 {s}' # f-string 格式化,f开头,{} 中为变量 # 列表 l = [1, 2, 5, 7, 0] l[0:] # 全部 l[0:1] l[2:3] l[0:-1] l[0::1] # 第三个参数表示截取的步长,为负则表示逆向 ``` - 列表方法 ```python .append() # 类似 push 末尾追加 .pop() # 末尾移除 .count() # 某元素出现次数 .copy() .remove() .sort() .index() .reverse() len(arr) # 获取列表长度 ``` - 元组 ```python # 使用 () 定义 t = (3, '888', True, 1.1) # 截取 t[0:-1] # 空元组 t1 = () # 一个元素,需要在 后面添加一个 , 用于区分语句 t2 = (1,) # 元组元素不允许修改,可相加 拼接、整个删除 # 所谓元组不可变 指的是所指向的内存中 的内容不可变,但是可以重新 赋值 ``` - 集合 ```python a = { 1, 3, '999' } b = set({ 1, 5, '00' }) a.add('9090') # .update() a.remove(1) # .discard() a.pop() # 随机删除一个元素 a.clear() a - b # 差集 a | b # 并集 a & b # 交集 a ^ b # a、b 中不同时存在的元素 ``` - 字典 ```python d = { 'a': 123, 'b': '111' } d['a'] d['b'] # 创建 dd = dict([('aa', 123), ('bb', 111)]) # 推导式 { x: x**2 for x in (2, 4, 6) } # {2: 4, 4: 16, 6: 36} # 构造函数 ddd = dict(a=1, b=2, c=3) d.keys() # key d.values() # value d.items() # [(key, value)] ``` - 类型转换 直接使用数据类型作为函数名即可,如 ```python int(x) str(x) tuple(x) list(x) set(x) # ... ``` - 逻辑运算符 - and - or - not - 成员运算符 - in - not in - 身份运算符 - is(类似 id(a) == id(b),id 函数用于获取对象内存地址;与 == 区别在于,== 是判断引用变量的值是否相等 ) - is not - 条件语句 ```python x = 1 if x == 1: print('--if--') else: print('--else--') ``` - 循环 ```python while x < 10: x += 5 else: print('while可接else') # 遍历 for i in [1,2,4]: print(i) # 遍历区间,指定步长 for i in range(1, 7, 2): print(i) ``` - import 、from...import 导入 python 模块 ```python # 导入 os 模块 import os # 从 bs4 中导入 BeautifulSoup 函数 from bs4 import BeautifulSoup ``` - `__name__` 属性 一个模块被另一个程序所引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序不执行,可以使用 `__name__` 属性来使该模块仅在自身模块运行时执行。 ```python if __name__ == '__main__': print('程序自身在运行') else: print('我来自另一个模块') ``` - 安装模块 ```shell # shell 命令 pip 安装 pip install module_name # PyCharm 安装 # 设置 - 项目名 - python 解释器 - 安装到项目中 ``` - 读写文件 open with 语句能保障文件之类的对象在使用完后一定会正确的执行他的清理关闭方法 ```python # 读写 with 语句,推荐使用 with open('text.txt', 'w') as f: pass # 或者直接打开,但是记得关闭 f = open(file, mode='r') f.read() f.readline() f.readlines() # 记得关闭 f.close() ``` - 模块介绍 - os ```python import os os.getcwd() # 返回当前工作目录 os.path.exists(path) # 判断目录是否存在 os.mkdir(path) # 创建文件夹 os.rename(old, new) # 重命名目录、文件 os.rmdir(path) # 删除空目录 os.chdir(path) # 改变当前工作目录 os.chmod(path, mode) # 更改权限 ``` - requests 发起请求 ```python import requests # get res = requests.get(url, headers) ``` - bs4 解析器 ```python import requests from bs4 import BeautifulSoup # get res = requests.get(url, headers) # 解析美化 soup = BeautifulSoup(res.text, 'lxml') # BeautifulSoup 返回的是包装对象,每一个标签都是都可以通过 tag 来获取,并且支持css选择器 str_text = soup.p.string # 转换为 json obj_json = json.loads(str_json) ``` - json ```python import json # json 字符串 转换为 json obj_json = json.loads(str_json) ``` - urllib ```python from urllib import parse # parse 解码 a = parse.unquote(x) ``` 3. 项目分析 爬取 王者荣耀 官网 英雄壁纸 、皮肤图片 目标 网页网址 ```python https://pvp.qq.com/web201605/wallpaper.shtml### ``` 往下可以看到【高清壁纸】,壁纸有多种规格大图可供选择下载 - 高清壁纸 url 切换分页时,控制台 js 栏有 jsonp 的请求 ```js https://apps.game.qq.com/cgi-bin/ams/module/ishow/V1.0/query/workList_inc.cgi?activityId=2735&sVerifyCode=ABCD&sDataType=JSON&iListNum=20&totalpage=0&page=0&iOrder=0&iSortNumClose=1&jsoncallback=jQuery17107970490009139568_1624438815767&iAMSActivityId=51991&_everyRead=true&iTypeId=2&iFlowId=267733&iActId=2735&iModuleId=2735&_=1624438816043 ``` 返回结果: ```js jQuery17107970490009139568_1624438815767({ list: [{ ... }], iBltFlag: "0" iCache: "1" iRet: "0" iTotalLines: "499" iTotalPages: "25" sMsg: "Successful" }) ``` 查看返回的 list 中就包含有 当前 页 的图片地址,复制其中一个地址 出来 解码一下得到的是缩略图地址 ```js // jsonp 中返回的缩略图地址 http://shp.qpic.cn/ishow/2735062314/1624429444_84828260_3367_sProdImgNo_8.jpg/200 // 打开高清大图地址 http://shp.qpic.cn/ishow/2735062314/1624429444_84828260_3367_sProdImgNo_8.jpg/0 ``` 打开是一张缩略图,与浏览器中直接打开的 高清大图 对比 ,发现,只是后缀 200 改成了 0 至此,大致思路可以梳理如下: - 分析请求参数,使用 requests 发起请求 - 获取返回 的 结果,解析出图片 list,将图片后缀修改成 大图 的后缀,并保存 大图 url - 自增请求参数的页码,循环获取其他页码的大图地址 并保存 - 下载大图到本地文件夹中 - 皮肤图片 另外还有根据英雄,下载皮肤图片,打开目标网页 ```js https://pvp.qq.com/web201605/herolist.shtml ``` 点击英雄 进入 英雄介绍,可以看到英雄皮肤图片为背景图,背景图即是目标,背景图 url 为 ```js https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/538/538-bigskin-1.jpg https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/538/538-bigskin-2.jpg ``` 以上格式为 ```js https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/ + {英雄id} + /{英雄id}-bigskin-{num}.jpg ``` 英雄id 和 皮肤 数量可以 接口中获得 - 英雄列表 json 获取,可在浏览器控制台找到 ```python # 英雄列表 json hero_url = 'https://pvp.qq.com/web201605/js/herolist.json' ``` 返回值 得到 ```js [ { cname: '...', ename: 538, hero_type: 3, new_type: 0, skin_name: 'xxx|yyy', title: '...' } ] ``` 可以看到的是,cname 表示英雄名字,skin_name 表示的是皮肤名字。 那么可以得出大致思路是: > 根据英雄列表 json 数据,拿到 英雄 ename 去拼接皮肤地址,即可下载每个英雄的 皮肤图片