By Wei Meng-xin(RIEM,SWUFE)
“ Everybody in this country should learn how to program a computer…
because it teaches you how to think. “
—— Steve Jobs
” ‘城里人’应该学点编程,因为这样可以变成‘城会玩’。”
写在前面的肺腑之言:
- Python不是一个别人口中“简单的编程语言”
- 尝试去了解硬件!硬件!硬件!
- 去学点C语言和Java
Day 1:欢迎走进Python的世界
0.导言 —— 这里可能是今天分享最具价值的部分
0.0 计算机系统
0.0.1 硬件:计算机硬件的工作原理
- CPU —— 不了解一点这个你还想玩转编程?:
- 超线程技术(Hyper-Threading)
- 并发和并行
- 进程(Process)和线程(Thread)
- Python GIL(Global Interpreter Lock)是个什么鬼?
- 对于任何Python程序,不管什么处理器,任何时候都总是只有一个线程在执行。
- “没有比解释器全局锁(GIL)让Python新手和专家更有挫折感或者更有好奇心。”
拓展性介绍
0. 今年是intel的X86架构发布40周年,Intel发布了全球限量的i7-8086K纪念版处理器
1. Intel为什么被称为“牙膏厂”?
2. AMD为什么被称为“农企”?
*3. 牙膏厂和农企的爱恨情仇真是一出大戏,鉴于Intel第8代酷睿处理器在性能上的巨大提 升,我们应该高喊一声:AMD,yes! Ryzen,yes!
- RAM(Random Access Memory)—— 永远不嫌容量大的新时代“理财产品”:
- Random的威力
- RAM地址
- 任何数据、变量、指针甚至是函数,都是一块一块的内存。对他们取址,就是取得这块内存的首地址。
- 在Python中,我们只能通过变量名引用数据!
- RAM和数据类型
- 内存从本质上来讲都是一样的,所以理论上你只要有权限,那就可以在任何一块内存上存放任意的数据。那我们如何定义数据类型?
- 数据类型其实是一份协议,一个约定,是我们在向申请内存块的时候跟系统约好了如何使用这块内存的协议。**
值得庆幸的是,Python有比较完善的内存管理机制和垃圾回收机制,但是这不意味着你可以完全不管理内存。
- GPU —— 并行计算的昂贵玩具:
- Nvidea的CUDA技术:理解 GPU 和 CPU 之间区别的一种简单方式是比较它们如何处理任务。CPU由专为顺序串行处理而优化的几个核心组成,而 GPU 则拥有一个由数以千计的更小、更高效的核心(专为同时处理多重任务而设计)组成的大规模并行计算架构。不明白没关系,看看这个
搞懂CUDA,也许能向老板申请买一块Nvidea GTX Titan V,
- ROM —— 想让计算机跑的更快?买块支持NVME协议的M.2接口的SSD吧 :)
0.0.2 软件:操作系统的本质——硬件管理员
- Unix —— 盘古开天地
- Linux —— 百花齐放
- MacOS —— 稳定、高效、有逼格
- BSD —— 金融业的最爱
- Windows —— 除了游戏,其余的都是渣渣
0.0.3 编程语言简史 —— 这是一群天才遇见“偶然”的故事
- “语言”的不严谨分类:
- 机器语言:01010010….
- 汇编语言:Assembly Language
- “低级”语言:C、Java…
- “高级”语言:Python,PHP,Perl,Ruby…
- 自然语言:汉语、英语、日语….
这个世界总是维持着微妙的平衡,在编程语言中也是如此,性能和可读性是一对矛盾。想要高性能,就必须牺牲可读性,比如使用C语言。AI技术为何如此重要?应为她是解决这一矛盾的钥匙。
0.0.4 二进制:真正的计算机语言
优势:计算速度极快,拥有一些高级操作(如:位运算)
劣势:人类很难理解,同时在表示小数时,存在误差
1.Python之初见篇
1.1 Python概览
1.1.1 目前Python的“江湖地位”
- 应用情况:TIOBE排名
- 流行度:
1.1.2 Python的设计理念:
“ Life is short,You need Python ”
—— Bruce Eckel
ANSI C++ Comitee member,
author of «Thinking in C++» and «Thinking in Java»
1.1.3 为什么选择Python?
用的人多这一条理由足矣! :)
1.1.4 Python(主要)用来做什么?
1.1.4 爬虫又能做什么?
“如果编程是一种魔法,爬虫就是一种巫术”
1.2 Python编程环境配置(Windows为例)
- Python 3.7.0新特性(点击这里)
- Python 3.7.0安装
- pip安装
- IPython安装
- 选择一个你喜欢的IDE or 文本编辑器
- IDLE (Python的IDE为什么叫IDLE?)
- Pycharm(专业神器)
- Anaconda(偏科学计算)
- Sublime Text (强大的付费编辑器)
- Visual Studio Code (Microsoft开发的编辑器)
- Atom (GitHub开源编辑器)
- 通过pip安装Python库
- Python 3 and Python 2
# 也许是你第一条Python代码:
import this
拓展性介绍:
0.OOP(面向对象编程)之我见
1.再送大家一个小“彩蛋”
2.Python之入门篇
在这之前,我先要认识到所谓“程序”,就是“算法”和“数据”的集合。
什么是算法?我觉得就你思考问题、解决问题的步骤。当一个“萌新”遇到性能问题是,一般都是算法使用不当造成的。
思考题:如果有1280字母,如何快速按字母表顺序排序?
我们看个视频
2.1 最最最基本语法
2.1.1 标识符
- 三大法宝,两项纪录
- 以字母、数字、下划线组成
- 不能以数字开头(想过为什么吗?),区分大小写
- ““ 和 “_“ 开头的标识符一般具有特殊意义
2.1.2 关键字(Keywords)
- 又被称为“保留字”,不能将其定义为标识符
你知道Python总共有多少关键字吗?
2.1.3 缩进
- Python 的代码块不使用{ }来控制类,函数以及其他逻辑判断,而使用缩进来写模块。
- Python对缩进有严格要求,所有Python也被戏称为“游标卡尺语言”
- 标准语法没有要求缩进的具体大小,只要求在一个程序中保持同样的缩进即可
- 但是,PEP 8要求保持4个空格的缩进
for i in range(1, 101):
if i % 2 == 0:
print(i)
PEP(Python Enhance Proposal)
“This document gives coding conventions for the Python code comprising the standard library in the main Python distribution”
PEP 8 — Style Guide for Python Code
2.1.4 注释
- 单行注释:#
- 多行注释:‘’‘ ’‘’
- 特殊注释:函数中可查看注释
2.1.5 多行语句
PEP 8要求一行不多于79个字符
1 + \
2 + \
3
2.2 标准数据类型
2.2.1 int类型
- Python 2中int占32个位,long占64个位
- Python 3在整数不再有位的限制,现在知道为什么Python在处理大数据有巨大优势了吧
大多数语言int型最大64位,超过了64位会产生“内存溢出”,非常致命
2.2.2 float类型
- 1.1 + 2.2 为什么不等于 3.3?
- 浮点数的真相,点击这里
2.2.3 complex number
- 复数使用的比较少
2.2.4 字符串
- 字符的编码是一个十分头疼的问题:ASCII、GBK、GB2312、UTF8
2.2.5 Boolean(布尔型)
- 逻辑判断的主力军:True、False
2.2.6 List & String
- ”增删查改“
- 切片(slice)
- pop()
2.2.7 Dictionary
- ”增删查改“
2.2.8 Tuple
2.3.9 Set
2.2.10 数据类型的转换
Day 2:Python进阶知识、初识API爬虫
Wei Meng-Xin,2018.7.6 Liulin Campus, SWUFE
今日计划如下,也是内容满满:
0. 帮助大家彻底解决Python开发环境配置的问题:现场演示
1. 上次课程核心内容复习:CPU、RAM、变量的本质、基本数据结构及其方法
- CPU及Python GIL的再解释:
进程、线程
Cpython的GIL有时会限制性能,为什么还要使用她?又如何绕过GIL的限制?
Notice:多进程、多线程不是万能的,有些情况下反而拖慢程序运行
为什么多进程、多线程能显著提高爬虫的效率?- RAM的重要性:变量的本质、数据类型的本质
栈区(Stack):存放函数的参数值,局部变量的值等
堆区(Heap):在Python中当Reference Counting为0时,由OS回收
全局区(Static):储存全局变量和静态变量
文字常量区:常量字符串就是放在这里
程序代码区:存放函数体的二进制代码
什么是不可变数据类型?( 你想过为什么要求不可变吗?)- Python为什么要设计不同的数据类型,如何在list、dict、tuple中做选择?
- 什么是数据类型的方法(Method)?其本质又是什么?
2. 简要说明什么是OOP(面向对象编程)
3. Python中的运算符:初见OOP中的 “多态性(Polymorphic)”
4. 流程控制语句:实现算法的支柱- 顺序执行
- 选择执行
- 循环执行
5. Python中的函数(基础篇):初见OOP中的 “封装(encapsulation)”
6. Python Library的安装和调用,为了实现今天要展示的例子,我们需要调用:
requests库:第三方开源库,需要通过pip安装
JSON库:Python的Built-in库,无需安装
7. Python中的异常处理
8. API(Application Programming Interface)是个什么鬼?如何使用?
API是个好同志:数据规整,获取方便,还免费,不用简直对不起自己
9. 举个“栗子”:**如何通过API获取数据(以百度API为例)
2.3 运算符
OOP(面向对象编程)的3大特性:多态、继承、封装
2.3.1 算术运算符
- ”+“,”*“的多态性
- % and //
2.3.2 比较运算符
- 两种不同的”不等于“:”!=“,”<>“
2.3.3 赋值运算符
- = and == 的区别
- “+=”是个什么鬼?
2.3.4 逻辑运算符
- and,or,not
2.3.5 成员运算符
- in,not in
2.3.6 身份运算符
- is,is not
- 你知道 is 和 ==的区别吗?
2.3.7 位运算符
- 二进制特有的运算符,充分体现了二进制的强大:&,| ,^,<<,>>
2.3.8 运算符的优先级
- 完全没必要记住优先级,用括号()指定优先级即可
2.4 流程控制语句
Python不是一种“编译型语言”,而是通过“解释器”解释运行,解释一行运行一行,所以code的顺序就是程序运行的顺序,除了顺序外,程序还有选择执行、循环执行
- 顺序执行( 兼论Python代码的执行过程 )
- 选择执行
- 循环执行
2.4.1 选择执行(条件语句)
If,else
num = int(input("请输入一个正整数:"))
if num % 2 == 0:
print("这是一个偶数!")
else:
print("这是一个奇数!")
2.4.2 循环执行(循环语句)
while,for
- while使用的时候要格外谨慎,不要写出死循环
- for循环也有else
# 例1:
num = [12, 37, 5, 42, 8, 3]
even = []
odd = []
while len(num) > 0:
number = num.pop()
if number % 2 == 0:
even.append(number)
else:
odd.append(number)
# 例2:
nums = [1, 2, 4, 99, 1.3, "SWUFE", [1, 2, 3], "RIEM"]
i = 0
while i < len(nums):
print(nums[i])
i += 1
# 例1:
for i in nums:
print(i)
# 例2:
for i in nums:
print(i)
else:
print("over")
# 例3:
nums = [1, 2, 4, 99, 1.3, "SWUFE", [1, 2, 3], "RIEM"]
import time
start = time.time()
i = 0
while i < len(nums):
print(nums[i])
i += 1
final = time.time()
print("运行总共耗时:%f" % (final - start))
nums = [1, 2, 4, 99, 1.3, "SWUFE", [1, 2, 3], "RIEM"]
import time
start = time.time()
for i in nums:
print(i)
final = time.time()
print("运行总共耗时:%f" % (final - start))
2.4.3 for … in … 的本质
(难点,不能理解也罢)
可迭代对象(Iterable)和迭代器
- 什么是 “可迭代对象” :可直接作用于 for 循环的对象,可以用 isinstance() 函数判断是否是可迭代对象(更严谨的定义是:如果一个对象拥有iter方法,其是可迭代对象):
- 数据集合类:list、dict、tuple、set、string
- generator类:生成器、带 yield 的 generator function
- 什么是“迭代器”:可作用于 for 循环,且可被 next() 函数不断调用,并返回下一个值,直到引发 Error: StopIteration 为止的 “可迭代对象”
from collections import Iterable
from collections import Iterator
# 可迭代对象
print(isinstance([], Iterable))
print(isinstance((), Iterable))
print(isinstance({}, Iterable))
print(isinstance("", Iterable))
print(isinstance((i for i in range(10)), Iterable))
print(isinstance(99, I5terable))
# 迭代器
print(isinstance([], Iterator))
print(isinstance((), Iterator))
print(isinstance({}, Iterator))
print(isinstance("", Iterator))
print(isinstance((i for i in range(10)), Iterator))
print(isinstance(99, Iterator))
- 重要的问题是 “迭代器” 有什么用?
- 核心就8个字:节约内存、提高性能
2.3.4.流程嵌套
所有流程都是可以相互嵌套的,不举例了,好吧还是举个例子(兼论如何优化算法):
# 寻找10000以内的完美数(Perfect number) 1
import time
start = time.time()
lis = []
nums = []
while True:
if len(lis) >= 4:
print("--------------------------")
print("找到10000以内的完美数共4个:" )
print(lis)
break
else:
for i in range(2, 1000, 21):
print(" ")
print("正在验证%d是否是完美数:" % i)
for n in range(1, i):
if i % n == 0:
print("--->找到一个%d的因子:%d" % (i, n))
nums.append(n)
if sum(nums) == i:
print("Notice:找到一个完美数:%d" % i)
lis.append(i)
nums.clear()
end = time.time()
print("总共用时:%f" % (end - start))
# 寻找10000以内的完美数(Perfect number) 2
import time
start = time.time()
lis = []
nums = []
for i in range(2, 10001, 2):
print(" ")
print("正在验证%d是否是完美数:" % i)
for n in range(1, i):
if i % n == 0:
print("--->找到一个%d的因子:%d" % (i, n))
nums.append(n)
if sum(nums) == i:
print("Notice:找到一个完美数:%d" % i)
lis.append(i)
nums.clear()
f len(lis) >= 4:
print("--------------------------")
print("找到10000以内的完美数共4个:" )
print(lis)
break
end = time.time()
print("总共用时:%f" % (end - start))
2.3.5 break语句
如果你想让循环在某一条件下彻底停止,可以在循环中声明 break 语句
while True:
stuff = input("请输入一个单词 [输入q退出]: ")
if stuff == "q":
break
print(stuff.capitalize())
2.3.6 continue语句
有时我们并不想结束整个循环,仅仅想跳到下一轮循环的开始,就要使用continue语句
while True:
value = input("输入一个整数[输入q退出]:")
if value == "q":
break
num = int(value)
if num % 2 == 0:
continue
print(num,"的平方是",num * num)
思考题:
如何剔除list中的重复值?(代码越简单越好)
# 例如将:
list_example = [1, 1, 2, 3, 3, 3, 99, 99, "SWUFE", "SWUFE", "China"]
# 变为:
list_new = [1, 2, 3, 99, "SWUFE", "China"]
2.5 print函数及格式化
- print函数在日常编程中有极为重要的作用(想想有哪些作用?)
- 格式化对表:
# 例子:
print ("His name is %s" % ("Wei"))
print ("He is %d years old" % (25))
print ("His height is %f m" % (1.83))
print ("Name:%10s Age:%8d Height:%8.2f" % ("Wei", 25, 1.83))
3.Python之进阶篇
3.1 Python中的函数(基础篇)
我们为什么需要函数?函数化编程有诸多优势,那么有没有缺陷呢?
3.1.1 定义函数:
def 函数名(参数列表):
语句
return 表达式
- 函数名的命名规则和变量相同
- PEP建议不要使用大写字母( 想一想为什么?)
3.1.2 函数引例:
# 定义函数的基本方法:
def func():
pass
# 例1:(如何改进这个程序?)
print("西")
print("南")
print("财")
print("经")
print("大")
print("学")
print("经")
print("济")
print("与")
print("管")
print("理")
print("研")
print("究")
print("院")
# 定义有参数的函数:
def func(paras):
pass
def sum_nums(a, b):
result = a + b
print("%d + %d = %d" % (a, b, result) )
sum_nums(b=12, a=89)
3.1.3 调用函数
- 函数是如何运行的?
- 形参和实参
- 函数调用的本质是,实参给形参赋值
def test(para1, para2, para3):
print(para1)
print(para2)
print(para3)
- 返回值
# 编写程序。获得两个数的和
def mysum(num1, num2):
num1 + num2
多个 return 语句会怎么样?
def test():
a = 1
b = 2
c = 3
return a
return b
return c
- 4种函数:无参数无返回值,有参数无返回值,无参数有返回值,有参数有返回值
- 值传递和引用传递**(难点)**
- 值传递:不可变对象,函数命名空间内变量改变,不改变全局变量
- 引用传递:可变对象,函数命名空间内变量改变,改变全局变量
# Case 1: 值传递
def func_1(num):
num = 10
change = 99
func_1(change)
print(change)
# Case 2:
def func_1(num):
num = 10
print(num)
func_1(change)
print(change)
# Case 3:
def func_1(num):
print(id(num))
num = 10
print(num)
print(id(num))
change = 99
print(id(change))
func_1(change)
print(change)
# Case 4:引用传递
def func_2(lis):
lis[0] = 99
lis_1 = [1, 2, 3, 4, 5]
func_2(lis_1)
print(lis_1)
3.1.4 嵌套调用
函数页是可以嵌套调用的,不举例了,很容易理解
Notice:Python中的函数远没有讲完,目前的内容是基础部分,下一次课补充中高级内容:
- LEGB准则
- 高阶函数
- 匿名函数(Lambda Function)
- 闭包(Closure)
- 装饰器(Decorator)
3.2 模块和import语句
3.2.1 什么是import
import 是调用Python程序的一种方法,目前只需要了解 import 是用来插入Python模块即可,当然通过import调用程序和直接运行程序存在差别
import request
timport numpy as np
from collections import Generator
如果想了解 import 和直接运行程序的差异,你需要了解 if name == “main“ 的真正意义
3.2.2 if name == “main“
(难点,不理解无妨)
如何通俗的理解 name == ‘main‘ ?假如你叫小明.py,在朋友眼中,你是小明(name == ‘小明’);在你自己眼中,你是你自己(name == ‘main‘)。
所以if name == ‘main‘的意思是:
- 当.py文件直接运行时, if name == ‘main‘之下的代码块将被运行
- 当.py文件以模块被导入时,if name == ‘main‘之下的代码块不被运行
真正理解上述问题,你需要了解什么是“程序入口”:
对于很多编程语言来说,程序都必须要有一个入口,如果你接触过其他语言,对于程序入口这个概念应该很好理解:
- C、C++需要有一个main函数作为程序的入口,也就是程序的运行会从main函数开始
- Java、C#必须要有一个包含Main方法的主类作为程序入口
而Python则不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。 也就是从脚本第一行开始运行,没有统一的入口。一个Python源码文件(.py)除了可以被直接运行外,还可以作为模块被其他.py文件通过import导入。不管是直接运行还是被导入py文件的最顶层代码都会被运行,而当一个py文件作为模块被导入时,我们可能不希望一部分代码被运行,这时我们就需要 if name == ‘main‘
3.3. 异常处理
3.3.1 为什么需要异常处理?
异常处理的好坏将直接影响到你的程序运行是否Robustness,如何理解这句话呢?
- Python的 “traceback” 机制在程序遇到错误时,执行回滚操作,以保证数据一致性
- However,我们需要在程序出错时,继续执行(即不触发“traceback”),此时我们就要进行 “异常处理 ”
# 引例:
a = 5
b = 6
c = "12
d = 8
# 基本结构:
try:
语句
except SomeError:
语句
# 例子:
try:
ase = dfg
except NameError:
print("存在一个错误")
a = 9
print(a)
3.3.2 编写你自己的异常
这里涉及Python中的高级内容 —“类”(Class),这里只是告诉大家,除了Python内置(Built-in)的异常,我们可以制作自己的“异常”(想一想:什么时候需要编写自己的异常?)
# 例子:
class UppercaseException(Exception):
pass
words = ['eeenie', 'meenie', 'miny', 'MO']
for word in words:
if word.isupper():
raise UppercaseException(word)
- 通过API获取数据 —— API爬虫
API:应用程序接口,我们每天都有接触,只是你没有意识到她的存在
- 微信、微博登录
- 美团内置的地图
- 快递查询
- 诸多APP中的“转发到朋友圈”
“ API为不同的应用提供了方便友好的接口。不同的开发者用不同的架构,甚至不同的语言编写软件都没问题——因为 API 设计的目的就是要成为一种通用语言,让不同的软件进行信息共享。” 一般情况下,程序员可以用HTTP协议向API发起请求以获取某种信息,API会用XML(eXtensible Markup Language,可扩展标记语言)或JSON(JavaScript Object Notation,JavaScript对象表示)格式返回服务器响应的信息,目前大多数大型网站的API都提供XML和JSON两种数据格式。尽管大多数API仍然在用 XML,但是JSON正在快速成为数据编码格式的主流选择(为什么呢?)
4.1 实例:百度地图API调用
(现场演示)
Day 3:Python高级内容、网络协议及Web基础
by Wei Meng-Xin
Time: 18:30
Location:7. 11、H411、 Liulin Campus、SWUFE
0. shang’ceshang’ce上次课程复习及答部分同学的疑问:
- OOP(面向对象编程)的三大特性 —— 多态、封装、继承
- 运算符 —— “多态性”的体现
- 流程控制
- Python中的函数 —— 程序的封装
关键字参数
什么是函数的 *args 参数?
什么是函数的 kwargs 参数?- Python Libraries的安装
Python的build-in(内置)库
第三方库
1. Python高级内容补充性介绍:- 命名空间(Namespace)—— LEGB准则
- Python函数的闭包(Closure)(难点)
- Python中的装饰器(Decorator)(难点)
- Python中的类(Class)—— 程序的继承(难点)
———————————— 以上4项内容不是爬虫必须知识 ————————————- 数据文件操作和导出
数据库知识
CSV文件操作
JSON文件操作
2. 网络协议、web基础知识:**- FTP
- HTTP/HTTPS
- 网页的框架 —— HTML简介
- 动态加载的网页怎么处理? —— Ajax技术简介
- Python如何处理HTML?—— BeautifulSoup库介绍
3. 真实的网页:- 一起看看真实的网页是怎样的
- 实例:获取京东商品评论数据
5.Python高级内容补充性介绍
以下高级内容是对Python知识体系的补充,如果不能理解不影响你写爬虫程序
5.1 Python中的命名空间及LEGB准则
直白一点讲:命名空间是对变量名的分组划分。不同组的相同名称的变量视为两个独立的变量,因此隶属于不同分组(即命名空间)的变量名可以重复。命名空间可以存在多个,使用命名空间,表示在该命名空间中查找当前名称。
从本质上看,Python的命名空间是一个字典,字典内保存了变量名称与对象之间的映射关系
5.1.1 命名空间的生命周期
所有的命名空间都是有生命周期的,对于python内建的命名空间,python解析器启动时创建,一直保留直至直python解析器退出时才消亡。而对于函数的local命名空间是在函数每次被调用的时候创建,调用完成函数返回时消亡,而对于模块的global命名空间是在该模块被import的时候创建,解析器退出时消亡
5.1.2 作用域
一个作用域是指一段程序的正文区域,可以是一个函数或一段代码。一个变量的作用域是指该变量的有效范围。Python的作用域是静态作用域,因为它是由代码中得位置决定的,而命名空间就是作用域的动态表现。
5.1.3 什么是LEGB准则?
简单来说,LEGB规定了查找一个名称的顺序为:local —> enclosing —> global —> built-in
LEGB含义解释:
L-Local(function):函数内的命名空间
E-Enclosing function locals:外部嵌套函数的命名空间(例如closure)
G-Global(module):函数定义所在模块(文件)的命名空间
B-Builtin(Python):Python内置模块的命名空间
# 例1
x = 1
def foo():
x = 2
def innerfoo():
x = 3
print('locals ',x)
innerfoo()
print('enclosing function locals ',x)
foo()
print('global ', x)
Notice:
Python 的一个特别之处在于其赋值操作总是在最里层的作用域。赋值不会复制数据 —— 只是将命名绑定到对象。事实上,所有引入新命名的操作都作用于局部作用域(函数内部对变量赋值进行修改后,该变量就会被Python解释器认为是局部变量而非全局变量)
i=1
def func():
i += 1
func()
5.1.4 如何访问命名空间?
局部命名空间可以 locals() 来访问
def func1(i, str ):
x = 12345
print(locals())
func1(1 , "first")
全局 (模块级别)命名空间可以通过 globals() 来访问
import copy
from copy import deepcopy
gstr = "global string"
def func1(i, info):
x = 12345
print(locals())
func1(1 , "first")
if __name__ == "__main__":
print("the current scope's global variables:")
dictionary=globals()
print(dictionary)
5.1.5 命名空间的补充内容
- 内置命名也同样被包含在一个模块中,它被称作 builtin
- 模块的名字空间不仅仅包含模块级的变量和常量,还包括所有在模块中定义的函数和类
- from module import 和 import module 之间的不同:
import module模块自身被导入,但是它保持着自已的名字空间,这就是为什么需要使用模块名来访问它的函数或属性(module.function)*
使用 from module import function,实际上是从另一个模块中将指定的函数和属性导入到自己的命名空间,这就是为什么可以直接访问它们却不需要引用它们所来源的模块*- locals 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行改变对局部名字空间中的变量值并无影响
- globals 返回实际的全局名字空间,而不是一个拷贝。所以对 globals 所返回的dictionary 的任何的改动都会直接影响到全局变量。
5.2 Python函数中的闭包(Closure)
(难点)
闭包:首先必须是内部定义的函数,该函数包含对外部作用域而不是全局作用域名字的引用
定义:内部函数的代码包含对外部函数的代码的引用,但一定不是对全局作用域的引用
在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。
—— 维基百科
5.2.1 闭包的基本形式:
在函数F1中,定义F2,F2只能引用F1定义的变量,之后F1函数返回F2的函数名
def func_1(msg):
def func_2():
print(msg) # 夹带外部变量
return func_2 # 返回夹带私货的函数
func = func_1("SWUFE")
func()
passlines = 60
def func(score):
if score >= passlines:
print("pass")
else:
print("failed")
def in_func():
print(score)
in_func()
func(99)
passlines = 60
def func(score):
if score >= passlines:
print("pass")
else:
print("failed")
def in_func():
print(score)
in_func()
return in_func
f = func(99)
f()
def func_150(score):
passline = 90
if score >= passline:
print("pass")
else:
print("failed")
def func_100(score):
passline = 60
if score >= passline:
print("pass")
else:
print("failed")
func_150(89)
func_100(89)
# 写成闭包
def set_passline(passline):
def cmp(score):
if score >= passline:
print("pass")
else:
print("failed")
return cmp
f_100 = set_passline(60)
print(type(f_100))
print(f_100.__closure__)
f_100(90)
f_100(59)
# 例2:
def get_sum(*arg):
return sum(arg)
def get_average(*arg):
return sum(arg)/len(arg)
print(get_sum(1,2,3,4,5))
# 这样的函数是否存在问题?
# 显然不稳健,无法处理空参数和字符串
# 调整如下:
def get_sum(*arg):
if len(arg) == 0:
return 0
for i in arg:
if not isinstance(i, int):
return 0
return sum(arg)
def get_average(*arg):
if len(arg) == 0:
return 0
for i in arg:
if not isinstance(i, int):
return 0
return sum(arg)/len(arg)
# 上述代码存在大量重复内容
# 更专业的做法如下:
def get_sum(*arg):
return sum(arg)
def get_average(*arg):
return sum(arg)/len(arg)
def is_type(func):
def in_is_type(*arg):
if len(arg) == 0:
return 0
for i in arg:
if not isinstance(i, int):
return 0
return func(*arg)
return in_is_type
get_sum = is_type(get_sum)
5.2.2 为什么需要闭包
闭包存在的意义就是它夹带了外部变量,如果它不夹带外部变量,它和普通的函数就没有任何区别。实际上,同一个的函数夹带了不同的外部变量,就可以实现不同的功能
闭包类似于轻量级的接口封装,其优势在于:
- 代码封装
- 实现代码的复用
5.3 装饰器(Decorator)
(难点)
Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里。
—— GitHub某Python程序员
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。 它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
5.3.1 初见装饰器
# Step 1:
def say_hello():
print("hello!")
def say_goodbye():
print("goodbye!")
if __name__ == '__main__':
say_hello()
say_goodbye()
# Step 2:
def say_hello():
print("[DEBUG]: enter say_hello()")
print("hello!")
def say_goodbye():
print("[DEBUG]: enter say_goodbye()")
print("hello!")
if __name__ == '__main__':
say_hello()
say_goodbye()
# step 3:
def debug():
import inspect
caller_name = inspect.stack()[1][3]
print("[DEBUG]: enter {}()".format(caller_name))
def say_hello():
debug()
print("hello!")
def say_goodbye():
debug()
print("goodbye!")
if __name__ == '__main__':
say_hello()
say_goodbye()
# Step 4:
def debug(func):
def wrapper():
print("[DEBUG]: enter {}()".format(func.__name__))
return func()
return wrapper
def say_hello():
print("hello!")
say_hello = debug(say_hello) # 添加功能并保持原函数名不变
# Step 5:
def debug(func):
def wrapper():
print("[DEBUG]: enter {}()".format(func.__name__))
return func()
return wrapper
@debug # Python新版本中的语法糖
def say_hello():
print("hello!")
5.3.2 带参数的函数如何调用装饰器?
- 上节所示的是最简单的装饰器,但是有一个问题,如果被装饰的函数需要传入参数,那么这个装饰器就不能用了,因为返回的函数并不能接受参数。此时,你可以指定装饰器函数wrapper接受和原函数一样的参数
def debug(func):
def wrapper(something): # 指定一样的参数
print("[DEBUG]: enter {}()".format(func.__name__))
return func(something)
return wrapper # 返回包装过函数
@debug
def say(something):
print("hello {}!".format(something))
- 但是这依然没有一劳永逸的解决问题 —— 面对形形色色的函数,每个都这样调整还是太麻烦。所以,别忘了我们还有args,*kwargs这两个利器
def debug(func):
def wrapper(*args, **kwargs): # 一劳永逸
print("[DEBUG]: enter {}()".format(func.__name__))
return func(*args, **kwargs)
return wrapper
@debug
def say(something):
print("hello {}!".format(something))
5.3.3 多个装饰器的调用顺序
- 装饰器是可以叠加使用的,那么这是就涉及到装饰器调用顺序了。对于Python中的”@”语法糖,装饰器的调用顺序与使用 @ 语法糖声明的顺序相反。
def deco_1(func):
print("Enter into deco_1")
def wrapper(a, b):
print("Enter intp deco_1_wrapper")
return func(a, b)
return wrapper
def deco_2(func):
print("Enter into deco_2")
def wrapper(a, b):
print("Enter intp deco_2_wrapper")
return func(a, b)
return wrapper
@deco_1
@deco_2
def add_func(a, b):
print("Result is %d" % (a+b))
add_func(1, 9)
5.3.4 装饰器高级用法
(将在Python的类中阐述)
- Python内置的装饰器:@staticmethod、@classmethod、property
- 类装饰器
5.4 Python中的类
(难点)
详细内容见其他文档
6.Python处理文件对象
6.1 CSV文件
CSV(Comma-Separated Values)即逗号分隔值,可以用Excel打开查看。由于是纯文本,任何编辑器也都可打开。与Excel文件不同,CSV文件中:
- 值没有类型,所有值都是字符串
- 不能指定字体颜色等样式
- 不能指定单元格的宽高,不能合并单元格
- 没有多个工作表
- 不能嵌入图像图表
CSV库的官方文档:点击这里
6.1.1 读取CSV文件
这个实在没什么好说的,直接看代码
def read_csv():
'''读取CSV文件数据'''
import csv
csv_file = csv.reader(
open("/Users/viemaxwei/Downloads/website.csv", "r"))
object_website = []
for i in csv_file:
object_website.append(i)
print(i)
return object_website
6.1.2 将数据导出为CSV文件
import csv
# 使用数字和字符串的数字都可以
datas = [['name', 'age', 'gender'],
['Bob', 14, 'male'],
['Tom', 23, 'male'],
['Jerry', 18, 'female']]
with open(
'/Users/viemaxwei/Downloads/example.csv', 'w', newline='') as f:
writer = csv.writer(f)
for row in datas:
writer.writerow(row)
6.1.3 更好的导出CSV文件的方法
利用pandas库的to_csv方法能更方便的导出数据
def output_data(all_data, output_file_name):
'''该函数将最终数据输出为CSV文件'''
import pandas as pd
name = ["代码", "名称", "网址", "政务公开",
"政务信息", "信息公开", "政务公告", "公示公告", "公告公示"]
table = pd.DataFrame(columns=name, data=all_data)
table.to_csv(
"/Users/viemaxwei/Downloads/" + output_file_name + ".csv")
return table
6.2 JSON文件
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言
- JSON 具有自我描述性,更易理解
JSON官网:点击这里
6.2.1 Python如何处理JSON文件
- json.dumps(): 对数据进行编码
- json.loads(): 对数据进行解码
JSON库官方文档:点击这里
import json
# Python 字典类型转换为 JSON 对象
data = {
'no' : 1,
'name' : 'swufe',
'url' : 'http://www.swufe.edu.cn'
}
json_str = json.dumps(data)
print ("Python 原始数据:", repr(data))
print ("JSON 对象:", json_str)
type(data)
type(json_str) # 注意json_str的类型
在实践中JSON文件是比较复杂的,我们可能需要手动调整JSON文件,或结合其他技术来解析JSON数据
6.3 HTML文件
- Flash V.S. HTML5
- HTML解析神器 —— BeautifulSoup4
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/warandpeace.html")
bsObj = BeautifulSoup(html)
nameList = bsObj.findAll("span", {"class":"green"})
for name in nameList:
print(name.get_text())
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
import datetime
import random
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
random.seed(datetime.datetime.now())
pages = set()
random.seed(datetime.datetime.now())
# 获取页面所有内链的列表
def getInternalLinks(bsObj, includeUrl):
internalLinks = []
# 找出所有以"/"开头的链接
for link in bsObj.findAll("a", href=re.compile("^(/|.*"+includeUrl+")")):
if link.attrs['href'] is not None:
if link.attrs['href'] not in internalLinks:internalLinks.append(link.attrs['href'])
return internalLinks
# 获取页面所有外链的列表
def getExternalLinks(bsObj, excludeUrl):
externalLinks = []
#找出所有以"http"或"www"开头且不包含当前URL的链接 for link in bsObj.findAll("a",
for link in bsObj.findAll("a",href=re.compile("^(http|www)((?!"+excludeUrl+").)*$")):
if link.attrs['href'] is not None:
if link.attrs['href'] not in externalLinks:\
externalLinks.append(link.attrs['href'])
return externalLinks
def splitAddress(address):
addressParts = address.replace("http://", "").split("/")
return addressParts
def getRandomExternalLink(startingPage):
html = urlopen(startingPage)
bsObj = BeautifulSoup(html)
externalLinks = getExternalLinks(bsObj, splitAddress(startingPage)[0])
if len(externalLinks) == 0:
internalLinks = getInternalLinks(startingPage)
return getNextExternalLink(internalLinks[random.randint(0,len(internalLinks)-1)])
else:
return externalLinks[random.randint(0, len(externalLinks)-1)]
def followExternalOnly(startingSite):
externalLink = getRandomExternalLink("http://oreilly.com")
print("随机外链是:"+externalLink)
followExternalOnly(externalLink)
followExternalOnly(“http://oreilly.com“)
Day 4: 数据库、网络基础及初级爬虫实例
by Wei Meng-Xin, 18:30 7.18
H411 Room, Yide Building, Liulin Campus, SWUFE
0. requests库及其应用
- requests库与Python自带urllib库的比较
1. JSON库及其应用
2. 用Python操作数据库- MySQL Database
- 什么是NoSQL?
3. 计算机网络基础:
4. 用JSON解析动态网页上的数据:(大型网站)- 实例1:获取京东商品评论数据
- 实例2:获取豆瓣电影评分数据
- 实例3:获取各省历年高考分数线数据
5. 用BeautifulSoup4库解析网页HTML数据:(中小型网站)- 实例4:用百度百科验证社会学 “ 六度分割理论 “
- 实例5:批量获取县级政府首页”政务公开”栏网址
6. 第5次课程引论(1):如何获取网页上的 “ 查询数据 “(中级爬虫)- selenium库介绍
- headless浏览器
7. 第5次课程引论(2):如何处理获取的 “ 非结构化数据 “:- 正则表达式(Regular Expression)介绍
- 中文分词工具介绍:jieba库
7.用Python操作数据库
7.1 关系型数据库
之所以被称为关系型(relational)是因为数据库展现了表单(table)形式的不同类型数据 之间的关系。例如之前菜单的例子中,每一项和它的价格是有对应关系的
- 多用户同时访问数据
- 用户使用数据的保护
- 高效地存储和检索数据
- 数据被模式定义以及被约束限制
- Joins 通过连接发现不同数据之间的关系
- 声明式(非命令式)查询语言,SQL (Structured Query Language)
7.2 DB-API
应用程序编程接口(API)是访问某些服务的函数集合。DB-API是 Python 中访问关系型数据库的标准 API。使用它可以编写简单的程 序来处理多种类型的关系型数据库,不需要为每种数据库编写独立的程序,类似于 Java 的 JDBC 或者 Perl 的 DBI
- connect() 连接数据库,包含参数用户名、密码、服务器地址,等等
- cursor() 创建一个 cursor 对象来管理查询
- execute() 和 executemany() 对数据库执行一个或多个 SQL 命令
- fetchone()、fetchmany() 和 fetchall() 得到 execute 之后的结果
import pymysql
conn = pymysql.connect(host='127.0.0.1', unix_socket='/tmp/mysql.sock',user='root', passwd='adwahads6136879', db='mysql')
cur = conn.cursor()
cur.execute("USE scraping")
cur.execute("SELECT * FROM pages WHERE id=1")
print(cur.fetchone())
cur.close()
conn.close()
7.2.1 MySQL
7.2.2 MariaSQL
7.2.3 PostgreSQL
在很多方面超过 MySQL,为什么没有MySQL火呢?
7.3 非关系型数据库(NoSQL)
7.3.1 memcached
一种快速的、内存键值对象的缓存服务器
7.3.2 Redis
和Python的数据结构配合的非常好
7.4 我们为什么需要专业数据库?
8.网络基础知识
8.1 TCP/IP
TCP/IP协议(传输控制协议)由网络层的IP协议和传输层的TCP协议组成
- IP层负责网络主机的定位,由IP地址确定唯一的Internet的一台主机
- TCP层负责面向应用的可靠的或非可靠的数据传输机制,这是网络编程的主要对象
TCP — 传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。 理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求
8.2 HTTP/HTTPS
HTTP协议是建立在TCP协议之上的一种应用,HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立TCP连接,而且需要客户端向服务器发出请求后,请求中包含请求方法、URI、协议版本以及相关的MIME样式的消息,服务器端才能回复数据
- get
- post
http/https不兼容中文,但是 ….
WWW中服务器和浏览器最主要的沟通方式
8.3 FTP
FTP 是File Transfer Protocol (文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上控制文件的双向传输。
8.4 URL
统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它
url简单说就是网址,URL(Uniform Resource Locator)中文名称是「全球资源定位器」,也被翻译做「通用资源位标」,是也就是一个位址,可单独识别网际网路上的电脑、目录或档案位置,也可以指定通讯协定,例如 Gopher、HTTP 等等。URL能让在Internet上的所有资源都能透过此的方法而找到其位置
8.5 Ajax技术
Ajax(Asynchronous JavaScript and XML)描述了一种主要使用脚本(JS)操纵HTTP的web应用架构,它的主要特点是使用脚本操纵HTTP和web服务器进行数据交换,不会导致页面重载。Ajax的核心是JS的XMLHttpRequest构造函数,它定义了用脚本操纵HTTP的API
Day 5:高级爬虫、正则表达式和Scrapy框架
by Wei Meng-Xin, 18:30 7.24
1211 Room, Gezhi Building, Liulin Campus, SWUFE
0. 第4次课程回顾:
- requests库与Python自带urllib库的比较
- 实例1:获取京东商品评论数据
- 实例2:获取豆瓣电影评分数据
- 实例3:获取各省历年高考分数线数据
- 实例4:用百度百科验证社会学 “ 六度分割理论 “
1. 跨网页爬虫:- 实例5:批量获取中国县级政府首页”政务公开”栏网址
2. 如何获取网页上的 “ 查询数据 “(高级爬虫):- Selenium库介绍
- Headless浏览器
- 实例6:遍历上海图书馆家谱数据库
3. 如何处理获取的 “ 非结构化数据 “:- 正则表达式(Regular Expression)介绍
- 中文分词工具介绍:Jieba库
4. Scrapy框架:
** 一个为了爬取网站数据,提取结构性数据而编写的应用框架
9.正则表达式——字符串解析神器
re库
9.1 主要函数
- re.match(pattern, string, flags=0)
- re.search(pattern, string, flags=0)
- re.findall(pattern, string, flags=0)
- re.finditer(pattern, string, flags=0)
- pattern:正则表达式
- string:需要匹配的字符串对象
- flags:匹配模式,默认为0
| flags可选参数 | 功能 |
|:——:|:——|
| re.I | 匹配忽略字母大小写 |
| re.L | 影响 “w, “W, “b, 和 “B,这取决于当前的本地化设置 |
| re.M | ‘^’和‘$’匹配行首和行尾时,会增加换行符之前和之后的位置 |
| re.S | 使 “.” 特殊字符完全匹配任何字符,包括换行;没有这个标志, “.” 匹配除了换行符外的任何字符 |
| re.X | 当该标志被指定时,在 RE 字符串中的空白符被忽略,除非该空白符在字符类中或在反斜杠之后 |
9.2 元字符
9.2.1 单个字符匹配
[0123456789] | |
---|---|
[0-9] | |
[a-z] | |
[A-Z] | |
[0-9a-zA-Z] | |
[^0-9] | |
\d | 匹配数字 |
\D | 匹配非数字 |
\w | 匹配数字、字母、下划线 |
\W | 匹配非数字、字母、下划线 |
\s | 匹配空白符(空格、tab、换行…) |
\S | 匹配非空白符 |
9.2.2 锚字符
^ | 行首匹配 |
---|---|
$ | 行尾匹配 |
\A | 段首匹配 |
\Z | 段尾匹配 |
\b | 单词边界 |
\B | 非单词边界 |
9.2.3 多字符匹配
(xyz) | xyz作为整体匹配 |
---|---|
x? | 匹配0个或1个x(非贪婪) |
x* | 匹配0个或任意多个x(贪婪) |
x+ | 匹配至少一个x |
x{n} | 匹配n个x |
x{n, m} | 匹配至少n个,至多m个x |
x{n, } | 匹配至少n个x |
x|y | 匹配x或y |
9.2.4 非贪婪匹配
*? | |
---|---|
+? |
9.3 字符串切割
re.split
9.4 字符串的修改和替换
- re.sub(pattern, rep, string, count=0, flags=0)
- re.subn(pattern, rep, string, count=0, flags=0)
- pattern:正则表达式
- rep:用来替换的字符串
- string:目标字符串
- count:最多替换次数,默认为0
9.5 分组
()常常用来提取分组数据
num = "028-68369854-01"
import re
pattern = re.match(r"(\d{3})-(\d{8})-(\d{2})", num)
print(pattern)
print(pattern.group(0))
print(pattern.group(1))
print(pattern.group(2))
print(pattern.group(3))
print(pattern.group())
num = "028-68369854-01"
import re
pattern = re.match(r"(?P<first>\d{3})-(?P<second>\d{8})-(?P<last>\d{2})", num)
print(pattern.group("first"))
print(pattern.group("second"))
print(pattern.group("last"))
9.6 正则表达式编译
- 正则表达式 ——> 正则表达式对象
- re.compile(pattern, flags=0)
num = "028-68369854-01"
import re
pattern = re.compile(r"(\d{3})-(\d{8})-(\d{2})")
print(pattern.match(num))