步骤/目录:
1.环境搭建
2.python基础
    (1)常量与变量
    (2)面向对象
    (3)缩进
    (4)操作符与表达式
    (5)if、for、while
    (6)函数
3.python进阶
    (1)模块
    (2)数据结构
    (3)面向对象进阶
    (4)文件读取
    (5)异常
4.总结

本文首发于个人博客https://lisper517.top/index.php/archives/38/,转载请注明出处。
本文的目的是水一篇博文,有意义的部分可能只有环境搭建。
本文写作日期为2022年8月27日。使用的平台为win10,编辑器为VS code。

python是一种十分便捷的编程语言,行文简洁易懂,而且各种第三方库使得其功能得到进一步扩展;实际上,实现一个功能时,使用合适的第三方库比自己编程要更省时省力,也不容易出现bug。爬虫是一类会访问网络的程序,爬虫的主要目的通常是获取一些信息,比如大量的数据,当然爬虫也可有其他功能,但最重要的一点是大多数爬虫都使用了python语言。深度学习是近年来兴起的AI概念,深度学习的过程中就需要大量的数据。
在python,爬虫与深度学习的关系上,python是爬虫和深度学习的语言基础,爬虫可以提供大量的数据,而深度学习就需要大量数据;当然它们也可以各自分开使用,python除了爬虫和深度学习之外还有很多其他用途,爬虫不止能提供数据,深度学习也有更广阔的应用。
最后,关于python的学习,可以下载一些pdf书籍(这里推荐 《A Byte of Python》 ,入门十分不错),或者也可以参考 菜鸟教程 ,菜鸟教程除了python还有其他很多CS的教程,是一个对初学者十分实用的网站。本文只是水一篇博文,没有什么价值。如果完全初学,建议就看菜鸟教程。但这些都是比较基础的教程,对于爬虫来说或许够用,如果后续需要进阶使用python,建议看一些英文原著。最后,如果不是专业的程序员,在学习python的过程中要以问题为导向,编写某个程序时遇到了某个问题就查询各种资料,直接bing搜索、参考一些书籍等,现在很多知识都能从互联网学到,一定要勤搜索,看别人的代码时哪行不懂就搜哪行。

1.环境搭建

要使用、测试python,首先要搭建一个运行python的环境,主要是下载安装python和编辑器。这里在win10电脑上使用VS Code进行演示;windows或其他操作系统也有很多不同的编辑器,可根据自身情况自行选用。
首先安装python,到 python for windows 下载页下载最新的(或者也可选择之前的版本) python-3.X.X 稳定版(stable)python,下载 Windows installer (64-bit) 的安装包,根据提示安装即可。安装完成后检查一下环境变量,在win10上设置里搜索 环境变量 ,看看python的安装目录是否在系统变量的Path里(安装过程中应该会自动把python加到环境变量)。
然后安装VS Code,即Visual Studio Code,是微软公司于2015年推出的一款可运行于Windows、Linux、Mac平台的编辑器,有各种丰富的扩展,支持C++、python、Go、Java、php、lisp等多种编程语言,也可以用来查看Markdown、html、json、yaml等多种数据格式。相比于占用空间很大的Visual Studio,VS Code的优点是轻量级,简约而不简单。
VS Code下载页 下载稳定版本,然后在左侧边栏里找到 扩展 ,搜索 python 并下载python扩展,VS Code也会检测到python、自动推荐一些扩展,可以看看功能、酌情选择。VS Code还有中文扩展,装了以后重启就能看到中文界面。
另外VS Code上方的 文件 - 首选项 - 设置 里可以进行各种设置;ctrl+shift+p则是VS Code的命令行;对于非专业的程序员,VS Code已经完全够用了,要做大型项目的话可以使用PyCharm的社区版(但是PyCharm只能写python,VS Code有时候可以为python写一些C++的模块)。由于这里是轻度使用python,所以也不介绍virtualenv等管理python版本的工具了。
下面对VS Code进行一些设置。在首选项的设置里搜索 python.formatting.provider ,选择yapf,右下角会弹出提示未下载yapf,点击install即可(如果下载失败就重复下载直到成功);然后在设置里搜 Auto Save ,把它选成 OnFocusChange ;搜索 Format On Save ,把它勾选上。这些设置分别是选择python的格式工具,设置焦点改变时就保存文件,设置保存文件时就把python的格式检查一遍。最后,按 ctrl+S 可以手动保存,要多保存。

最后测试一下环境是否搭建成功。使用cmd(win+R,输入cmd并运行),运行 python -V 查看python版本。

然后开始编写一个入门程序。在一个合适的地方新建一个test文件夹,在VS Code的 文件 - 打开文件夹 里打开这个文件夹,信任文件夹作者;然后在左侧的资源管理器里单击右键、新建文件,起名为test.py,写入如下内容:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

if __name__=="__main__":
    print('芜湖,起飞')

在py文件打开页面的右上角有一个朝右的三角形,点击它即可运行这个python程序,该程序会输出 芜湖,起飞 。
这里简单解释一下,第一行的 #!/usr/bin/python 也可以写成 #!/usr/bin/env python ,用于指定运行哪个python的解释器,主要是用于linux平台(为了以后程序的可移植性,这行最好加上,比如以后可能把程序放到树莓派上运行); # -*- coding: UTF-8 -*- 说明这个文件的编码格式为UTF-8,大多数情况下可以不加,但还是加上以防万一; if __name__=="__main__": 这行,一个py文件可能被其他py文件引用,也可能自己运行,而 if __name__=="__main__": 之内的命令只会在第二种情况下执行; print('芜湖,起飞') 则用于输出 芜湖,起飞 ,可以把鼠标悬停在 print 上查看帮助信息。

2.python基础

(1)常量与变量

常量代表的内容不会更改,比如 21.19.25e-3 这样的数字, "123"'芜湖,起飞' 这样的字符串。
变量有一个变量名和内容,好比壶与酒的关系,壶不变,里面装的液体可以变。比如 a=1 ,那么a就是一个整数型变量,内容是1这个数字。按传统编程语言看,python的变量有数字、字符串,其中数字包括整数( 123 )、浮点数(有小数点的,如 1.15e-3 )、复数(比如 (1.1+2.1j) )。下面则详细讲一下字符串。

字符串就是一串字符,在python中用单/双/三引号括起来的一般就是字符串,比如 '123'"234" (这里单双引号没有区别);三引号括起来的字符串可以跨行,如:

"""
第一行
第二行
"""
'''
第一行
第二行
'''

三引号括起来的字符串也可以用于写注释。
另外,有时字符串中需要包含单、双引号,比如需要包含单引号就可以写 "I'm happy." ,双引号亦然。也可以使用转义符号( \ ),比如写成 'I\'m happy.',另外还有一些转义序列,比如 \n 表示换行, \t 表示水平制表符,更多转义序列可参考ASCII编码表;注意,行末的 \ 表示本行继续到下一行,例如:

"第一行\
第二行"

"第一行第二行" 是一样的。如果想让字符串包含字面意义上的 \n 而非换行符,可以写成 '\\n' 或者 r'\n' (或者用 R'\n' ), r 表示自然字符串,自然字符串还经常用于正则表达式(后面爬虫经常用到正则表达式)。最后,字符串可以进行拼接,比如 'a''b' 或者 'a'+'b''ab' 是一样的。
在字符串的输出格式方面,可以用字符串的format方法指定(python2中可能用 % 或者字符串模板更多)。比如下面的程序:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

if __name__=="__main__":
    name = '张三'
    salary = 3000
    print('{}今年的月薪高达{}。我要输出{{}}'.format(name, salary))
    print('{0}今年的月薪高达{1}。我要输出{{}},还要输出一次名字:{0}'.format(name, salary))

前者的两个 {} (自动编号)分别被替换成了 '张三'3000 ,后者则加上了手动编号,从0开始;而 '{{}}' 则会输出 {}
另外,format在指定字符串的输出格式方面很有用,比如:

'{:.3}'.format(1/3) #指定浮点数精确到小数点后几位,这里是有四舍五入的,并且末尾的0不显示(0.500会显示为0.5)
'{:_^11}'.format('hello') #输出要居中占11格,不够的地方用_填充。把 ^ 换成 < 或 > 则为靠左、靠右输出
'{name}的月薪为{salary}'.format(salary=3000, name='张三') #用键值对而不是编号的方式指定填充内容

更多关于format的内容可查看 这个网页

变量需要命名,上面的 name = '张三'salary = 3000 中就有 namesalary 两个变量名。变量名有以下规则:
第一个字符是大写/小写字母或 _ ,中间则还可以有数字;变量名对大小写敏感, nameName 不是同一个变量。

(2)面向对象

编程方法可以分为面向问题和面向对象,面向问题就是以解决问题为导向,比如我需要进行某个简单操作很多次,这种问题一般面向问题即可,写一个函数进行这个操作;面向对象则不会首先想着解决问题,而是先将问题中一些可以模拟的东西写出来,比如我要做一个GTAV一样的游戏,其中有很多汽车,那么我就会模拟一个汽车,每个汽车有一些固定的数据(如汽车的品牌、颜色、长宽),还有能进行的操作(汽车可以行驶,可以鸣笛),把汽车的数据(数据)和操作(方法)都赋予它,它就会越来越像汽车。面向对象有三大特征,封装、继承、多态:在操作这辆汽车时,我不会关心它的方法是怎么写的,而只会调用方法,比如我要让它行驶,就会按 汽车.行驶() 这样的格式调用方法,这叫封装;汽车分小轿车、大货车、电动车、烧气车、油车,在汽车的基础类别上我再加一些独特的数据或方法就能形成这些子类别,这叫继承;在父类别和子类别中调用同一个方法可能有不同的结果,比如 大货车.鸣笛() 可能就比 汽车.鸣笛() 响一些,这叫多态。

在python中,一切都是对象。这使得python用起来很简单,但在某些情况下可能导致一些麻烦,比如运行速度较慢。

(3)缩进

python里的空格很重要。在VS Code里编辑py文件时,按一下Tab键会得到四个空格而非 \t ,这就是因为python完全靠缩进来决定层次,比如:

if __name__=="__main__":
    name = '张三'
salary = 3000

这里 name = '张三' 就属于 if __name__=="__main__": 内, salary = 3000 则与 if __name__=="__main__": 平级。像之前说的,这段语句被别的py文件调用时,会执行 salary = 3000 但不会执行 name = '张三'

(4)操作符与表达式

常用的操作符有:
算数运算符,包括: + 、 - 、 * 、/ 、 (乘方,xy表示x的y次方) 、 // (整除,比如5//3得1) 、 % (余,比如5%3得2)。
比较运算符,包括: == (用于判断相等), != (用于判断不相等), < 小于、 > 大于, <= 小于等于、 >= 大于等于。
赋值运算符,包括: = ( name = '张三' 就是把 '张三' 这个值赋给 name ), += (比如 a += c就是a = a + c的意思),以及 -= 、 = 、 /= 、 %= 、 *= 、 //= ,都类似于 += 。
位运算符(仅了解),包括:<< 左移,>> 右移,& 按位与,| 按位或,^ 按位异或,~ 按位取反。
逻辑运算符,包括: and 表示 与 , or 为 或 , not 为 非 。
成员运算符, innot in ,用于判断是否成员,比如 a in b
身份运算符,(了解) isis not ,用于判断两个变量名是否引用同一个对象。

运算符有优先级,一般 * 、 / 比 + 、 - 优先,其他情况下建议用圆括号,如: a + b * ca + (b * c) 。另外运算有顺序, a + b + c 顺序为 (a + b) + c ,而赋值运算 a = b = c 的顺序是 a = (b = c)

(5)if、for、while

if语句用于分情况讨论,根据条件的不同执行不同的程序块,其格式为:

if 条件1:
    程序块1
elif 条件2:
    程序块2
elif 条件3:
    程序块3
else:
    程序块4

把程序想象成水流,那么这里就根据情况不同而流到了4个不同的程序块中。注意各个条件之间应该是互斥的,比如条件1是 a<1 ,条件2就不能是 a<10 ,因为 a 可以同时满足这两个条件。另外,if是可以嵌套的:

if 条件1:
    if 条件2:
...

最后,elif和else不一定要写,但if是必须的。

while用于在条件为真时一直执行程序块:

while 条件:
    程序块1
else:
    程序块2

这里的else也是可选的。一般在程序块1中会设置一些语句,使得某些情况下跳出循环,这样才不会陷入死循环。

for也是一种循环,格式为:

for 变量名 in 序列:
    程序块1
else:
    程序块2

这里的else同样是可以不写的。 变量名 一般用 i ,可以用在程序块1里;序列,比如 一个列表 [1,2,3,4,5] ,那么 for i in [1,2,3,4,5] 就会让 i 分别取1~5,这个循环会执行5次程序块1。另外 range(1,6) 也可以生成 [1,2,3,4,5]

最后,在while或for循环里可以用 breakcontinue 控制程序流, break 会跳出循环、不执行else, continue 会直接开始下一次循环:

while True:
    if 条件1:
        break
    if 条件2:
        continue
    程序块1
else:
    程序块2

当条件1成立时,while内、break语句后的部分,以及else内的程序块2都不会执行;当条件2成立,程序块1不会执行,而是直接进行下一次循环。

(6)函数

函数可以分为内建函数和自建函数。如果要多次执行某段程序块,就可以把它们写成一个函数,调用的时候只需要一行即可。一个比较简单的函数格式如下:

def 函数名(变量名1, 变量名2, ...):
    程序块

变量名1、变量名2是在调用函数时可能需要输入的东西,当然也可以不需要变量: def 函数名(): ,这样调用时也不用输入任何东西。
形参指变量名1、2,实参指调用时传入的值。
一个完整的函数及其调用示例如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

def CompareMax(a, b):
    if a > b:
        print(a, '更大')
    elif a < b:
        print(b, '更大')
    else:
        print('两者相等')

if __name__=="__main__":
    CompareMax(3, 4)
    x = 1
    y = 2
    CompareMax(x, y)

这里提到了函数,再提一下变量名的作用域,变量名声明后可以用于同级或下级的局部程序块:

a = 1
if 条件1:
    b = 2
else:
    程序块1
程序块2

这时在程序块1、2里就不能使用if里的b(当然你也可以在这两块程序里定义自己的b),但a则可以在这里随处使用。另外需要注意 global ,它用于指定某个变量是全局变量:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

PI = 3.14
variable1 = 123


def calculate_circle_area(r):
    global PI, variable1
    return PI * r * r


if __name__ == "__main__":
    print(calculate_circle_area(1))

为了尊重传统,一般代表常量的名字用全大写。这里其实不写 global 程序运行结果也不会变,但为了保证python的易懂,建议还是写上。除了 global ,还有 nonlocal ,这个一般用于函数中的函数,当在一个函数里定义了一个变量名x、在这个函数里写一个子函数,那么如果在这个子函数里也定义变量名x、又不想用父函数中的x,那么就用 nonlocal x

刚才提到函数可以有参数,其实参数可以设置默认值: def calculate_circle_area(r = 1): ,那么调用函数时就可以不传递实参,函数会使用默认值。另外,写的时候要把没有默认值的参数写在前面,如 def func(a, b = 1, c = 2): ,而不能写成 def func(b = 1, a, c = 2): ;这种多个参数、有默认值的,可以只指定其中几个参数,比如对 def func(a, b = 1, c = 2): ,调用时用 func(3, 4)func(5, c = 6)func(c = 7, a = 8) 都是可以的。

最后,关于函数的参数还有两个知识点。其一,可以用 * 来使参数获取任意个数的参数:

#!/usr/bin/python
# -*- coding: UTF-8 -*-


def func(a = 5, *b, **c):
    程序块

func(10, 1, 2, 3, x = 4, y = 5)

func(10, 1, 2, 3, x = 4, y = 5) 在调用时, a 得到的实参为10,b得到了一个列表( [1, 2, 3] ),c得到了字典( {'x': 4, 'y': 5} )。关于列表和字典将在后面解释。
其二,将上面的例子稍微修改为 def func(a = 5, *b, c): ,那么由于c在带 *** 同理)的形参后面声明,所以要给c赋值必须用关键字(以 func(..., c = 1) 这样的形式),否则python是分不清什么时候b停止收集实参的。另外,如果只是想让别人在调用时必须写出 c = 1 ,则可以把函数这样定义: def func(a = 5, *, c): ,这时 * 没有收集参数、只是把c变成了关键字参数。

函数的程序块中,有时可用 return 来跳出函数,如:

def func():
   if ... :
       return 1
   elif ... :
       return

return 后面可以接想要返回的对象,不写的话表示 return None (实际上即使不写 return ,函数的末尾也暗含了 return None )。

如果要给程序写说明,用这样的格式:

def func():
   '''程序说明第一行
   第二行'''
   程序块

调用时,把鼠标放在函数名上,就能看到程序说明。或者 func.__doc__ 也指代这段说明文字。除了函数,模块、类也可以这样写说明。

3.python进阶

(1)模块

上面介绍过了函数,有时我们写的函数很有用,想用在不止一个程序里,那么就可以把函数装在模块里,方便调用。除了函数,自己定义的类也可以装在模块里;除了自己写,其他人的模块也可以借鉴;除了python,还可以用C、C++写为python写模块,提高运行效率。实际上,python自带的标准模块(标准库)就十分有用,大多数功能在标准库中都有。下面是一个导入模块的例子:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import sys

for i in sys.argv:
    print(i)

sys是标准库,这里就不解释sys.argv的意思了,总之记得用 import 导入库;如果是自己写的库,确保python知道你的库在哪里(即保证自己的库的路径在python路径中,见后)。另外,导入库时经常会看到pyc文件,这是python为了加快 import 的过程而生成的文件。

import 还有两种形式: from sys import argv ,和 from sys import * ,前者是直接导入argv这个名字,后者是导入所有名字。使用这两种形式时, sys.argv 只需要写成 argv 即可,但也经常造成自己的名字和库的名字冲突,所以尽量不要用这两种形式( dir(模块名) 可查看模块里定义过的名字,或称标识符)。

如果想自己写库并把它加入python的路径,可以这样操作:
在一个合适的地方新建文件夹(这里假设就放在D盘下),改名为 MyLibs 或者其他合适的名字,在该文件夹里新建一个同名的文件夹,在子文件夹里新建一个名为 __init__.py 的空白文件;然后到python的安装目录,进入 Lib\site-packages 目录,新建一个pth文件,比如名为 MyLibs.pth ,在这个文件里加上一行:

D:\MyLibs

记得把 D: 改成自己的 MyLibs 文件夹在的位置。或者也可以添加系统变量/用户变量 PYTHONPATH ,把自己的库路径加入里面,详情自行搜索。

(2)数据结构

数据结构,即数据的存储形式。python里自带了4种数据结构,包括列表、元组、字典、集合。

a.列表

列表是一种有序的数据结构,在python中用 [] 括起来、元素之间用 , 分隔,如: [1, 'test', 3.14, 4] 。可以对列表的元素进行搜索;而且列表是可变的,即可以对元素进行增、删、改操作。另外,列表的元素之间不需要保证类型一致。
len(列表) 可查询元素个数; 列表[元素的序号] 可指定某一个元素(0为第一个元素,-1为倒数第一个);在列表末尾增加元素用 列表.append(要增加的元素) ;删除元素用 del 列表[元素的序号]del 也可用于删除标识符),或者 列表.pop(元素的序号);列表排序用 列表.sort() 。这里只是简单介绍,更详细的内容参考runoob,或bing搜索。

b.元组

元组也是有序的,十分甚至九分像列表,不同在于元组不可变,并且用 () 括起来,如: (1, 'test', 3.14, 4) 。注意元组只含一个元素时要多写一个逗号,如 (1, ) ,便于python区分。

c.字典

字典无序,通过键值对储存信息,如: dict_test = {'a':1, 'b':2, 'c':3} ,这里 'a''b''c' 就是键,各自有对应的值。注意键必须是不可变的对象,比如字符串,元组,但尽量使用简单的对象做键,一般就是字符串;键也不能重复,但值可以重复。通过 dict_test[键] 可取出对应的值。如果要取出字典中的所有值,可以使用如下格式:

for key,value in dict_test.items():
    程序块

字典的items方法会返回键值对的元组列表,即每对是一个元组,所有元组构成一个列表。

d.集合

无序,适合用于判断一个元素有没有出现(比如后面爬网址的时候,判断一个网址爬过没有就可以用集合。当然如果大的网站还是要用数据库)。集合有一些数学上的操作,比如求交集、并集等。集合也是用 () ,为了与元组区分,一般写成: set_test = set(1, 2, 3, 4) 这样。

另外,提醒一下这些数据结构是可以嵌套的,比如列表套列表、元组套元组,或者各自互相套。最后,由于python比较智能,给对象赋予新的名字时其实没有创建新的对象、只是把标识符指向对象,比如 a = 某个序列; b= 同一序列 ,则a、b指向的都是同一个序列(浅拷贝),如果不想这样就需要创建新的序列给b(深拷贝),一般可以用copy模块的 copy.deepcopy(对象) 来实现深拷贝,具体可自行搜索。

(3)面向对象进阶

在大型项目中,一般都需要使用类,面向对象的方法(爬虫中会使用到Scrapy模块,是一个好用的开源爬虫,就是一个不错的例子)。
类的成员有数据成员和方法成员,一个典型的类格式如下:

class MyClass(父类):
    var_common = 1

    def __init__(self, var1, var2):
        父类.__init__(self, ...)
        self.x = var1
        self.y = var2
        MyClass.var_common += 2
    
    def __del__(self):
        ...

    def func1(self):
        程序块

__init__ 方法会在每次初始化实例时运行一次。如果不实例化对象就想用类的数据、方法,用 类名. 的格式。另外,python不像C++一样用private声明私有,而是前面加 __ 前缀表示私有。

(4)文件读取

格式:

with open('文件路径', 'w', encoding="UTF-8") as f:
    ...

(5)异常

格式:

try:
    程序块1
except ...:
    程序块2
finally:
    程序块3

finally 部分一定会执行。

4.总结

写着写着笔者就不太想写了,从C、C++到Python,一门语言的内容太多,光看几篇文章或者几本书完全不够;实际上用到的东西却不多,代码胜于雄辩,如果真的要学习python还是建议自己多看书、多写代码。
如果想了解关于爬虫与python的进一步内容,可查看后续文章。

标签: python

添加新评论