更新时间:2022-06-06 22:53:09
从零开始学Scrapy网络爬虫
(视频教学版)
点击查看第二章
点击查看第三章
张涛 编著
Scrapy网络爬虫框架是用Python编写的,因此掌握Python编程基础是更好地学习Scrapy的前提条件。即使你从未接触过Python,通过本章的学习,也能很熟练地进行Scrapy网络爬虫开发,因为Python的设计哲学是优雅、明确、简单,用最少的代码完成更多的工作。
在开发者社群流行一句话“人生苦短,我用Python”。看似一句戏言,其实十分恰当地说明了Python独特的魅力及其在开发者心目中的地位。
要说近几年最受关注的编程语言,非Python莫属。根据2019年3月Tiobe发布的编程语言排行榜显示,Python以惊人的速度上升到了第三位。这门“古老”的语言,之所以能够焕发新生,得益于人工智能的崛起。因为Python是人工智能的首选编程语言,这已是业界的共识,也是必然的选择。
Python是一门解释型的高级编程语言,创始人为荷兰人Guido van Rossum(吉多·范罗苏姆)。1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节无聊的时间,决心开发一个简单易用的新语言,它介于C和Shell之间,同时吸收了ABC语言的优点。之所以起Python这个名字,是因为他喜欢看英国电视秀节目《蒙提·派森的飞行马戏团(Monty Python’s Flying Circus)》。
Python主要有以下几个特点。
一提到环境的搭建,相信很多人都有过痛苦的经历,除了需要安装一堆软件,还要忍受一系列复杂的步骤及天书般的配置命令,稍有不慎,就会功亏一篑。本节将为大家介绍使用Anaconda“傻瓜式”地搭建Python编程环境的方法。
1.Anaconda介绍
Anaconda是最受欢迎的数据科学Python发行版,它集成了Python环境,包含了一千多个Python/R数据科学包,并能有效地管理包、依赖项和环境,更重要的是它包含了Scrapy框架的各种依赖包,因此以后安装Scrapy框架时,基本不会出现任何问题。
2.安装Anaconda
(1)下载Anaconda。
官方网站下载网址为https://www.anaconda.com/download,如图1-1所示。
图1-1 Anaconda下载页面
网速慢的读者可在清华大学开源软件镜像站下载,网址为https://mirrors.tuna.tsinghua. edu.cn/anaconda/archive/,如图1-2所示。
(2)Anaconda是跨平台的,有Windows、Linux和Mac OS版本,请根据自己的操作系统及系统类型(32/64位),下载最新版本的Anaconda。
图1-2 清华大学开源软件Anaconda下载页面
图1-3 设置环境变量
二是忽略下载VSCode,即单击Skip按钮,如图1-4所示。VSCode(Visual Studio Code),是微软推出的一款轻量级代码编辑器,这里用不到。
3.验证安装是否成功
如何验证Anaconda是否已经安装成功了呢?很简单,打开控制台,输入命令:python。如果显示如图1-5所示的Python版本的信息,说明Anaconda已经成功安装,这时即可进入Python的解释器界面。
图1-4 忽略安装VSCode
图1-5 验证安装是否成功
4.编写第一行Python代码
在解释器界面就可以进行Python编程了,如输入print("hello Python!"),回车,就会打印出字符串"hello Python!",如图1-6所示。自己动手试一试吧。
图1-6 第一行Python代码
如果仅仅是基本的Python程序开发,安装Anaconda就足够了。但是对于Scrapy网络爬虫开发,就显得力不从心了,我们需要功能更强大的集成开发环境,来帮助我们整合资源,减少错误,提高效率。
PyCharm是一种Python编程的集成开发环境(IDE),带有一整套可以帮助用户在使用Python语言开发时提高效率的工具,比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测试和版本控制等。当然PyCharm对于专业的Python Web开发,也提供了Django框架(用于开发Python Web的框架)的支持。
1.下载PyCharm
PyCharm官方网站下载网址为https://www.jetbrains.com/pycharm/download。
2.选择版本
PyCharm分Professional(专业版)和Community(社区版)。专业版拥有全部功能,但是收费;社区版是个较轻量级的IDE,免费开源。对于开发者来说,使用社区版完全够用了。
3.安装PyCharm
PyCharm的安装也是“傻瓜式”的,只要按照提示执行“下一步”即可。不过,在选择操作系统类型(32/64位)时,需要根据操作系统的实际情况选择对应的系统类型,如图1-7所示。
图1-7 选择自己的操作系统类型
图1-8 PyCharm编程
Python语法简单、优雅,如同阅读英语一般,即使是非科班出身的用户,也能很快理解Python语句的含义。
1.变量
有计算机基础的读者都知道,计算机在工作过程中所产生的数据都是在内存中存储和读取的。内存类似于工厂的仓库,数据作为零件存储在仓库(内存)中不同的地方。仓库那么大,怎样才能快速找到这些零件(数据)呢?我们可以给每个零件贴上“标签”,根据标签名称,就可以找到它们了。这些标签,就是传说中的“变量”。使用“变量”可以快速定位数据、操作数据。比如,存储同学cathy的个人信息:
name = "cathy" #变量名name,存储姓名
age = 10 #变量名age,存储年龄
height = 138.5 #变量名height,存储身高
is_student = True #变量名is_student,存储是否是学生的标记
score1 = None #变量名score1,存储成绩
【重点说明】
2.数据类型
Python的基本数据类型包括整数、浮点数、布尔型和字符串,可以使用type()函数查看一个变量的类型。比如查看同学cathy的各个变量的类型:
type(name) #字符串:
type(age) #整数:
type(height) #浮点数:
type(is_student) #布尔型:
type(score1) #NoneType:
【重点说明】
Python中数值的基本运算和其他语言差不多,运算符及其使用说明如表1-1所示。
表1-1 运算符及其使用说明
条件语句是指根据条件表达式的不同,使程序跳转至不同的代码块。Python的条件语句有:if、if-else和if-elif-else。下面来看几个判断成绩的例子。
(1)判断成绩是否合格:
score = 95 #成绩
#二选一
if score >= 60: #如果成绩60分及以上,则输出“合格”
print("合格")
else: #否则,输出“不合格”
print("不合格")
运行以上代码后,输出“合格”。
(2)判断成绩是优秀、良好、及格还是不及格:
#多选一
if score >= 90: #如果成绩大于等于90
print("优秀")
print("再接再厉")
elif score <90 and score >=70: #如果成绩在70~90之间
print("良好")
elif score < 70 and score >= 60: #如果成绩在60~70之间
print("及格")
else: #其他情况
print("不及格")
运行以上代码后,输出“优秀”和“再接再厉”。
【重点说明】
表1-2 关系运算符
下面来看一个判断闰年的例子。要判断是否是闰年,只要看年份是否满足条件:能被4整除,并且不能被100整除,或者能被4整除,并且又能被400整除。
实现代码如下:
year = input("请输入年份:") #通过命令行输入年份
year = int(year) #转换为整型
if (year%4==0 and year%100!=0) or (year%4==0 and year%400==0):
print("%d年是闰年"%year) #闰年
else:
print("%d年不是闰年"%year) #非闰年
【重点说明】
if year%4==0 and (year%100!=0 or year%400==0):
生活中有许多重复的劳动,如cathy做错事被罚抄课文5遍等。代码的世界也是如此,对于重复的功能,如果通过简单的复制、粘贴,代码就会变得沉重冗余,难以理解。Python中使用while和for循环来实现代码的重复利用,通常用于遍历集合或累加计算。
1.while循环
while循环的语法结构为:
while <条件>:
循环体
在给定的判定条件为True时执行循环体,否则,退出循环。循环的流程图如图1-9所示。
图1-9 循环流程图
for循环的语法结构为:
for <目标对象> in <对象集合>:
循环体
当执行for循环时,会逐个将对象集合中的元素赋给目标对象,然后为每个元素执行循环体。以下代码使用for循环实现了计数和遍历集合的功能:
#1.使用for执行5次循环
for n in range(1,6): #range()函数生成整数集合(1,2,3,4,5)
print("cathy正在努力抄第%d遍课文"%n) #每次循环输出文字
#2.遍历字符串所有字符
name = "cathy"
for n in name:
print(n) #每次循环分别输出c、a、t、h、y
#3.遍历列表中的所有项目
student = ["cathy",10,25] #记录姓名、年龄、体重
for item in student:
print(item) #每次循环分别输出cathy 10 25
3.break和continue
在循环过程中,有时需要终止循环或者跳过当前循环。Python使用break和continue来分别表示终止循环和跳过当前循环。
来看一个break的例子:实现在1~100之间,找到第一个能被3整除且能被8整除的整数。实现代码如下:
a = 1 #初始为1
while a<=100: #循环100次
if a%3==0 and a%8==0:
print("第一个能被3整除且能被8整除的整数是:%d"%a)
break #终止循环
a+=1 #每次循环自增1
再来看一个continue的例子:实现在1~100之间,找到所有不能被3和8整除的数。实现代码如下:
for i in range(1,101):
if i%3==0 and i%8==0:
continue
print("%d "%i)
【重点说明】
一般情况下,while和for循环可以互相代替,但也有一些使用原则:
1.引号
字符串是Python中最常见的数据类型,它包含在一对双引号(" ")或单引号(' ')中。单引号和双引号没有任何区别。
name = "cathy" #双引号字符串
like = 'english' #单引号字符串
当单引号中含有单引号(或者叫撇号)时,程序运行就会出错,解释器会“犯迷糊”,如下面的代码所示。它会将'i'看成一个字符串,后面的m就不知道如何处理了。
age = 'i'm ten ' #单引号中包含单引号
错误信息:SyntaxError: invalid syntax
针对上述问题,有以下两种修改方式。
方式一:将字符串改为双引号括起来。
age = "i'm ten " #使用双引号
方式二:使用反斜杠()将字符串中的单引号进行转义。
age = 'i'm ten ' #加转移字符:\
2.访问字符串
Python访问字符串,可以使用方括号([ ])下标法来截取字符串,代码如下:
hello = "hello,Python!"
hello[0] #获取第1个值:h
hello[1:4] #获得第2~5个(不包括)范围的值:ell
hello[-1] #获取最后一个值:!
【重点说明】
字符串自带很多处理方法,通过简单的调用,就可以实现对自身的处理。以下为字符串最常用的几种处理方法,读者可以打印出来看一下效果。
cathyStr =" Hello,cathy! " #两边有空格的字符串
cathyStr.strip(" ") #去除字符串两边的空格
cathyLst = cathyStr.split(",") #以逗号作为分隔符,切分字符串,保存为列表
cathyStr.replace("!",".") #将字符串中所有感叹号替换为句号
cathyStr.lower() #将字符串中所有字母都转换为小写字母
cathyStr.upper() #将字符串中所有字母都转换为大写字母
4.格式化输出
字符串的格式化输出有3种方法:
第1种是我们一直在print( )函数中使用的%格式法。例如,要输出字符串“我的名字叫XX,今年X岁了。”,其中名字和年龄都是动态输入的。实现代码如下:
name = input("请输入姓名:")
age = int(input("请输入年龄:"))
message = "我的名字叫%s,今年%d岁了。"%(name,age)
print(message)
【重点说明】
>请输入姓名:cathy
>请输入年龄:10
我的名字叫cathy,今年10岁了。
这种方法有个特点,就是格式化符号和后面的变量要一一对应,位置一旦搞错,就会出现错乱。这时候可以考虑使用第2种格式化输出方法。先看一下代码:
name = input("请输入姓名:")
age = int(input("请输入年龄:"))
message = "我的名字叫%(i_name)s,今年%(i_age)d岁了。"%{"i_name":name,"i_age":age}
print(message)
【重点说明】
>请输入姓名:tom
>请输入年龄:15
我的名字叫tom,今年15岁了。
第3种格式化输出的方法是使用字符串的format()函数,用法与第2种方法类似。还是先来看代码:
name = input("请输入姓名:")
age = int(input("请输入年龄:"))
message = "我的名字叫{i_name},今年{i_age}岁了。".format(i_name=name,i_age=age)
print(message)
【重点说明】
>请输入姓名:lili
>请输入年龄:8
我的名字叫lili,今年8岁了。
1.2.5节使用了变量存储同学cathy的个人信息,但是如果她的个人信息很多,就需要定义更多的变量来存储,这就会产生以下问题:
列表是一组元素的集合,可以实现元素的添加、删除、修改和查找等操作。现将同学cathy的个人信息统一放到列表中,代码如下:
cathy = ["cathy",10,138.5,True,None] #cathy的个人信息
score = [90,100,98,95] #各科成绩
name = list("cathy") #利用list()函数初始化一个列表
print(name) #输出结果:['c', 'a', 't', 'h', 'y']
【重点说明】
cathy = ["cathy",10,138.5,True,None] #cathy的个人信息
a = cathy[0] #下标法获取第1个元素(姓名):cathy
b = cathy[1:3] #使用切片获取下标1到下标3之前的子序列:[10, 138.5]
c = cathy[1:-2] #切片下标也可以倒着数,-1对应最后一个元素:[10, 138.5]
d = cathy[:3] #获取从开始到下标3之前的子序列:['cathy', 10, 138.5]
e = cathy[2:] #获取下标2开始到结尾的子序列:[138.5, True, None]
cathy[2] = 140.2 #将第3个元素修改为140.2
10 in cathy #判断10是否在列表中,True
cathy.append(28) #将体重添加到列表末尾
print(cathy) #['cathy', 10, 140.2, True, None, 28]
cathy.insert(2,"中国") #将国籍插入到第2个元素之后
print(cathy) #['cathy', 10, '中国', 140.2, True, None, 28]
cathy.pop() #默认删除最后一个元素
print(cathy) #['cathy', 10, '中国', 140.2, True, None]
cathy.remove(10) #删除第1个符合条件的元素
print(cathy) #['cathy', '中国', 140.2, True, None]
cathy.reverse() #倒序
print(cathy) #[None, True, 140.2, '中国', 'cathy']
现在要使用列表存储另一个同学terry的信息,已知除了姓名以外,其他的信息跟cathy一样。通过以下操作就可以得到同学terry的列表。
#cathy的个人信息
cathy_list = ["cathy",10,138.5,True,None]
terry_list = cathy_list #将cathy_list赋给变量terry_list
terry_list[0] = "terry" #修改terry的姓名
print(terry_list) #打印terry信息:['terry', 10, 138.5, True, None]
print(cathy_list) #打印cathy信息:['terry', 10, 138.5, True, None]
和大家的预期不同的是,cathy_list中的姓名也变成terry了,但是我们并未修改cathy_list的姓名,这是什么原因呢?原来在执行terry_list=cathy_list时,程序并不会将cathy_ list的值复制一遍,然后赋给terry_list,而是简单地为cathy_list的值即["cathy",10,138.5, True,None]建立了一个引用,相当于cathy_list和terry_list都是指向同一个值的指针,所以当terry_list中的值改变后,cathy_list的值也会跟着变。可以通过id()函数来获取变量的地址。实现代码如下:
print(id(cathy_list)) #获取cathy_list的地址:2011809417032
print(id(terry_list)) #获取terry_list的地址:2011809417032
结果显示,cathy_list和terry_list这两个变量均指向同一个地址。如何解决这个问题呢?可以使用copy()函数将值复制一份,再赋给terry_list,实现代码如下:
#terry_list = cathy_list #删除该条语句
terry_list = cathy_list.copy() #将值复制一份赋给变量terry_list
将同学cathy各科的成绩保存于列表score中,实现代码如下:
score = [90,100,98,95] #成绩
如果想要获取cathy的语文成绩,如何做到呢?除非事先将每门课的位置都做了记录,否则无论如何是获取不到语文成绩的。当需要对数据做明确的标注,以供别人理解和处理时,使用列表就不太方便了,这时字典就派上用场了。
字典是一种非常常见的“键-值”(key-value)映射结构,它为每一个元素分配了一个唯一的key,你无须关心位置,通过key就可以获取对应的值。下面来看一下使用字典保存的成绩:
score1 = {"math":90,"chinese":100,"english":98,"PE":95} #成绩字典
print(score1["chinese"])
【重点说明】
score2 = dict(math=90,chinese=100,english=98,PE=95)
print(score1["chinese"]) #根据key获取语文成绩:100
if "PE" in score1: #判断字典中是否包含"PE"的key
print(score1["PE"]) #得到体育成绩:95
#获取所有的key并保存于列表中,输出结果:['math', 'chinese', 'english', 'PE']
print(score1.keys())
#获取所有的value并保存于列表中,输出结果:[90, 100, 98, 95]
print(score1.values())
#获取key和value对转化为列表
#输出结果:[('math', 90), ('chinese', 100), ('english', 98), ('PE', 95)]
print(score1.items())
元组和列表最大的区别就是不可变的特性,即元组的值一旦确定了,就无法进行任何改动,包括修改、新增和删除。
sex1 = ("male","female") #使用括号生成并初始化元组
sex2 = tuple(["male","female"]) #从列表初始化
sex3 = ("male",) #只有一个元素时,后面也要加逗号
sex4 = "male","female" #默认是元组类型("male","female")
【重点说明】
for循环用于遍历一个对象集合,依次访问集合中的每个项目。前面提到的列表、字典和元组,均可通过for循环遍历。下面来看几个例子。
1.遍历列表
cathy = ["cathy",10,138.5,True,None]
#依次输出:"cathy",10,138.5,True,None
for a in cathy:
print(a)
可以通过下标遍历列表。用len()函数获得列表长度,再用range()函数获得所有下标的集合,实现代码如下:
#依次输出:"cathy",10,138.5,True,None
for i in range(len(cathy)):
print(cathy[i])
2.遍历字典
score = {"math":90,"chinese":100,"english":98,"PE":95} #成绩字典
#键的遍历,不按顺序输出:"math","chinese","english","PE"
for key in score:
print(key)
#键和值的遍历,不按顺序输出:math : 90,chinese : 100,english : 98,PE : 95
for key,value in score.items():
print(key,":",value)
程序如果执行多次,会发现输出的顺序不一定一致,这是因为字典是无序的。
3.遍历元组
sex = ("male","female")
#依次输出:"male","female"
for b in sex:
print(b)
在编写程序的时候,读者会不会被一个问题所困扰?有些功能多处要用到,实现时不得不复制和粘贴相同的代码。这不但会使程序代码冗余、容易出错,而且维护起来十分困难。因此,可以将这段重复使用的代码打包成一个可重用的模块,根据需要调用这个模块,而不是复制和粘贴现有的代码,这个模块就是Python的函数。
另外,我们有时希望将模块和模块所要处理的数据相关联,就跟Python内置的数据结构一样,能有效地组织和操作数据。Python允许创建并定义面向对象的类,类可以用来将数据与处理的数据功能相关联。
Scrapy爬虫框架正是基于Python模块化(函数和类)的设计模式进行组织和架构的。而且几乎所有爬虫功能的实现,都是基于函数和类的。可以说,Python模块化设计是理解Scrapy爬虫框架及掌握爬虫编程技术的重要前提。
函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段。它有一个入口,用于输入数据,还有一个出口,用于输出结果。当然,根据实际需求,入口和出口是可以省略的。下面先看几个例子。
(1)判断闰年的函数。
def is_leap(year): #函数定义
if (year % 4 == 0 and year % 100 != 0) or (year % 4 == 0 and year % 400 == 0):
return 1 #闰年
else:
return 0 #非闰年
【重点说明】
year = int(input("请输入年份:")) #控制台输入年份
result = is_leap(year) #函数调用,传递参数year
if result == 1:
print("%d年是闰年"%year)
else:
print("%d年不是闰年"%year)
【重点说明】
def print_student(name,age,sex="女"): #性别使用了默认值,必须放最后面
print("name:",name)
print("age:",age)
print("sex:",sex)
print_student("cathy",10) #函数调用,性别使用了默认设置
print_student("terry",20,"男") #函数调用
【重点说明】
def get_avg(scores): #scores前面加,表示可变长参数
sum = 0 #总成绩,初始值为0
for one in scores:
sum+=one
return (sum/len(scores)) #计算出平均值,返回给调用方
avg = get_avg(80,90,95) #调用函数,求3门课的平均分
avg1 = get_avg(77,88) #调用函数,求2门课的平均分
print(avg) #结果:88.33333333333333
print(avg1) #结果:82.5
【重点说明】
大家都知道,通过网络爬虫提取的数据,数据量往往都很大。如果将所有数据都保存到列表或字典中,将会占用大量的内存,严重影响主机的运行效率,这显然不是一个好方法。遇到这种情况,就需要考虑使用迭代器(iterator)了。
迭代器相当于一个函数,每次调用都可以通过next()函数返回下一个值,如果迭代结束,则抛出StopIteration异常。从遍历的角度看这和列表没什么区别,但它占用内存更少,因为不需要一下就生成整个列表。
能够使用for循环逐项遍历数据的对象,我们把它叫做可迭代对象。例如列表、字典和rang()函数都是可迭代对象。可以通过内置的iter()函数来获取对应的迭代器对象。例如,使用迭代器获取列表中的每个元素,代码如下:
cathy = ["cathy",10,138.5,True,None]
iter1 = iter(cathy) #生成迭代器对象
print(next(iter1)) #得到下一个值:"cathy"
print(next(iter1)) #得到下一个值:10
在Python中,把使用了yield的函数称为生成器(generator)。生成器是一种特殊的迭代器,它在形式上和函数很像,只是把return换成了yield。函数在遇到return关键字时,会返回值并结束函数。而生成器在遇到yield关键字时,会返回迭代器对象,但不会立即结束,而是保存当前的位置,下次执行时会从当前位置继续执行。
下面来看一个著名的斐波那契数列,它以0、1开头,后面的数是前两个数的和,下面展示的是前20个斐波那契数列的数据。
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181
下面分别使用普通函数和生成器实现斐波那契数列的功能,以此来说明它们的不同之处。
(1)定义普通函数。
#普通斐波那契函数定义
def get_fibonacci(max): #max:数量
fib_list =[0,1] #保存斐波那契数列的列表,初始值为0和1
while len(fib_list) < max:
fib_list.append(fib_list[-1]+fib_list[-2]) #最后两个值相加
return fib_list
#主函数
if name == "__main__":
#函数调用,输出前10个斐波那契数列的值:0 1 1 2 3 5 8 13 21 34
for m in get_fibonacci(10):
print(m,end=" ")
因为函数只能返回一次,所以每次计算得到的斐波那契数必须全部存储到列表中,最后再使用return将其返回。
(2)使用带yield的函数——生成器。
#使用yield的斐波那契函数定义
def get_fibonacci2(max):
n1 = 0 #第一个值
n2 = 1 #第二个值
num = 0 #记录数量
while num < max:
yield n1
n1,n2 = n2,n1+n2
num+=1
#主函数
if name == "__main__":
#输出前10个斐波那契数列的值:0 1 1 2 3 5 8 13 21 34
for n in get_fibonacci2(10):
print(n,end=" ")
yield一次返回一个数,不断返回多次。先来看一下程序执行的流程图,如图1-10所示。
图1-10 斐波那契数列流程图
1.类和对象
我们希望尽量将函数和函数所要处理的数据相关联,就跟Python内置的数据结构一样,能有效地组织和操作数据。Python中的类就是这样的结构,它是对客观事物的抽象,由数据(即属性)和函数(即方法)组成。
就像函数必须调用才会执行一样,类只有实例化为对象后,才可以使用。也就是说,类只是对事物的设计,对象才是成品。
以下为描述人这个类的代码示例:
class People: #定义人的类
#构造函数,生成类的对象时自动调用
def __init__(self,my_name,my_age,my_sex):
self.name = my_name #姓名
self.age = my_age #年龄
self.sex = my_sex #性别
#方法:获取姓名
def get_name(self):
return self.name
#方法:打印信息
def get_information(self):
print("name:%s,age:%d,sex:%s"%(self.name,self.age,self.sex))
【重点说明】
#主函数
if name == "__main__":
#生成类的对象,赋初值
cathy = People("cathy",10,"女")
print(cathy.get_name()) #调用方法并打印,得到:"cathy"
cathy.get_information() #调用方法,得到:name:cathy,age:10,sex:女
【重点说明】
刚才定义了人这个类,如果还想再实现一个学生的类,是否需要重新设计呢?显然这会浪费很多时间,因为学生首先是人,具有人的所有属性和功能,再加上学生独有的一些特性,如年级、学校等即可。因此,我们没有必要重复“造***”,只要将人的类继承过来再加上自己的特性就生成了学生的类,这种机制叫做继承,其中学生类叫做子类,人的类叫做父类。类似于“子承父业”,即子类继承了父类所有的属性和方法。
学生类实现代码如下:
class Student(People):
def __init__(self,stu_name,stu_age,stu_sex,stu_class):
People.__init__(self,stu_name,stu_age,stu_sex) #初始化父类属性
self.my_class = stu_class #班级
#打印学生信息
def get_information(self):
print("name:%s,age:%d,sex:%s,class:%s"%(self.name,self.age,self.
sex,self.my_class))
#主函数
if name == "__main__":
#生成Student类的对象
cathy = Student("cathy",10,"女","三年二班")
#打印结果name:cathy,age:10,sex:女,class:三年二班
cathy.get_information()
1.文件操作
Python提供了文件操作的函数,用于将数据保存于文件中,或者从文件中读取数据。
以下代码实现了将学生列表数据保存到文件中的功能:
#学生列表
students=[["cathy",10,"女"],
["terry",9,"男"]]
#使用with…as…打开文件,文件会自动被关闭
with open("students.txt","a",encoding="utf-8") as f:
for one in students:
#以逗号隔开,连成一个长字符串
to_str = one[0]+","+str(one[1])+","+one[2]+"n"
f.write(to_str) #将字符串写入文件
【重点说明】
文件名:students.txt。
打开方式:a表示追加,r表示只读,w表示只写。
编码方式:utf-8(支持中文)。
图1-11 文件内容
以下代码实现了从文件中读取数据到列表的功能:
students1 = []
with open("students.txt","r",encoding="utf-8") as f:
for one in f: #f为可迭代文件对象,使用for循环,依次遍历
# 将读取到的字符串去除换行符,再转换为列表
one_list = one.strip("n").split(",")
one_list[1] = int(one_list[1]) #将年龄转为整型
students1.append(one_list) #增加到学生列表中
#输出结果:[['cathy', '10', '女'], ['terry', '9', '男']]
print(students1)
2.异常处理
上面的代码实现了从文件中读取数据到列表中的功能,但是,如果students.txt文件不存在,程序就会报错。这时可以使用try…except结构捕获异常,并对异常做出处理。
加入异常处理的代码如下:
students1 = []
try:
with open("students.txt","r",encoding="utf-8") as f:
for one in f: #f为可迭代文件对象,使用for循环,依次遍历
# 将读取到的字符串去除换行符,再转换为列表
one_list = one.strip("n").split(",")
one_list[1] = int(one_list[1]) #将年龄转换为整型
students1.append(one_list) #增加到学生列表中
#输出结果:[['cathy', '10', '女'], ['terry', '9', '男']]
print(students1)
except FileNotFoundError:
print("文件不存在!")
except:
print("其他错误!")
【重点说明】
本章首先简单介绍了Python的历史及其在人工智能领域无可替代的优势;接着使用Anaconda“傻瓜式”地搭建了Python编程环境,并介绍了PyCharm集成开发环境;最后紧密围绕Scrapy网络爬虫开发需要,介绍了Python的基本语法、内置数据结构和模块化设计,为Scrapy网络爬虫开发打下坚实的编程基础。