在上一章,我们了解到Python一切皆对象,那么在这一章,我们将了解更多的Python对象。同时,我们还将了解几个常用的Python库。
1 存储
在电脑中的操作系统中,一般都是以文件的形式存储数据。所以我们利用Python来存储对象,就需要先创建文件对象:
f = open(文件位置, 读取方法)
这里文件位置是指文件放置的相对路径或绝对路径。读取方法分为:'r'为只读;'w'为写入,如果当前位置没有改文件则新建文件,如果存在重名文件,则对其覆盖;'a'和'w'相似,只是如果存在重名文件,则在文件的末尾添加内容。比如我们在项目路径内创建一个只读对象:
f = open('sample.txt', 'r')
有了文件后,如果该文件确实在路径中存在,且有内容,我们可以利用文件对象中的三种方法读取文件内容:
words = f.read(10) # 读取该文件的10个字节
words = f.readline() # 读取该文件一行
words = f.readlines() # 读取文件所有内容,按列表存储,每个元素为一行
如果为新创建文件,我们想往文件中写入内容,我们可以利用文件对象的wirte方法,且字符创串中用特殊字符表示换行:
f.write('the content write in document') # 写入文件
f.write('the content write in document \n') # UNIX系统的换行符
f.write('the content write in document \r\n') # Windows下的换行符
因为创建文件对象需要花费计算机资源,所以在使用完毕后要利用文件的close方法关闭文件:
f.close() # 关闭文件
其实纯使用上述的文件对象控制方法,在做较大工程时容易忘记关闭文件,经常使程序崩溃。Python提供上下文管理器,将打开文件的操作限制在一个缩进的范围内,一旦出去范围,便自动关闭文件对象。上下文管理器采用with
和as
两个关键字组合。假设现在我们要把内容写入文件,如果用之前的方法应当这么写:
f = open('text.txt', 'w')
print(f.closed)
f.wirte('hello world')
f.close()
print(f.close())
利用上下文管理器可以简化代码这么写:
with open('text.txt', 'w') as f:
f.write('hello world')
print(f.closed)
这里,当执行完毕第二行代码时,文件便自动关闭。其实上下文管理器和函数等类似,其在类中有__exist__()
, __enter__()
这两个内置方法,下面举个例子:
class task(object):
def __init__(self, text): # 初始化对象调用方法
self.text = text
def __enter__(self): # 打开文件时调用方法
self.text = self.text
return self
def __exit__(self, exc_type, exc_value, traceback): # 关闭文件调用方法
self.text = self.text+'!'
with task('I'm fine') as my_task:
print(my_task.text)
print(my_task.text)
# 输出结果:
# I'm fine
# I'm fine!
只要类中有enter
和exit
这两个方法定义,就可以使用上下文。在使用with.as.
时,首先调用init
方法。之后调用enter
方法,将enter的返回对象将作为as后指代的变量,这里写的是self
,也就是自己类。最后关闭文件时调用exit
方法,除了self
,后三个参数exc_type
, exc_value
, traceback
都是程序异常的变量,如果运行正常,它们的值为None。
之前我们讲过函数的定义存储和调用,在直到Python有许多对象后,应当了解,类也可以储存和调用。Python提供pickle
函数包,利用其dumps方法可以把对象转换为文本流,再将其写入文件中。
import pickle
class Bird(object):
feather = True
reproduction = 'egg'
spring = Bird()
pickle_string = pickle.dump(spring) # 转换成文本流
with open('sample.pkl', 'wb') as f: # 写入文件
f.write(pickle_string)
上述两个步骤也可以一步完成,在定义好类后,可以这么写:
spring = Bird()
with open('sample.pkl, 'w') as f:
pickle.dump(spring, f) # 序列化后保存对象
这个类已经保存在磁盘,现在只需要反向调用就好,我们可以使用open打开.pkl文件后利用pikle的loads方法将字节流转换成对象。光反向恢复对象还不行,Python要求程序中必须有其对应的已经定义的类。所以我们需要再把类定义一遍,就可以引用了:
import pickle
class Bird(object): # 重新定义一遍类
feather = True
reproduction = 'egg'
with open('spring.pkl','rb') as f: # 引用保存的对象
spring = pickle.load(f)
print(spring.feather)
2 time和datetime库
这两个库是Python的内置库,可以调用系统的时间。在计算机的主板上有一个计时表,它会不停的计算时间,依据这个硬件,计算机系统便有了挂钟时间,它是一个固定时间到现在的时间间隔。此外,计算机还可以测量CPU运行时间,也即处理器时间。当CPU空闲时,事件处理器也会暂停。而Python的time库是可以调用这两个东西的,我们接下来详细讲讲。
首先是挂钟时间,我们可以使用time
方法得到,单位是秒,可以利用它测量程序运行时间。我们还可以利用clock
方法得知程序的运行速度。下面举个例子:
import time
print(time.time()) # 查看挂钟时间
start = time.clock()
for i in range(10000):
j = 1
end = time.clock()
print(end-start)
每使用一次clock,就会记录一次当前的时间,UNIX系统记录的是CPU时间,而Windows系统记录挂钟时间。也就是说在Windows系统,使用clock
方法和time
方法是一样的。但请注意,clock方法在3.8后的版本已经被废除不能使用,需要改用perf_counter
或process_time
才可以。
我们还可以利用`time`包设置时间格式。利用`struct_time`对象,我们可以得到`tm_year`, `tm_ month`等属性。下面的方法可以将挂钟时间转换成`struct_time`对象:
st = time.gmtime() # 返回struct_time的UTC时间
st = time.localtime() # 返回当地时间,有系统所在环境决定
也可以反过来,经struct_time转换成time对象:
t = time.mktime(st)
在了解time库后,我们来学习datetime库。其实它是比time更牛的库,能够更加方便的获取时间。顾名思义,它是由date和time两个对象组成,更有将两者融合的datetime
对象,我着重讲解这个,其他的用法类似。我们现在就来写写,比如我现在要输出2019年7月13日22点35分,可以这么写:
import datetime
t = datetime.datetime(2019, 7, 13, 22, 35)
print(t)
datetime
对象有hour
, minute
, second
, millisecond
, microsecond
对应time的小时、分、秒、毫秒、微妙,有year
, month
, day,``weekday
对应date的年、月、日、星期,这些属性。借助这些属性,我们可以进行时间的数学运算,得到时间间隔。举个例子:
import datetime
t = datetime.datetime(2019, 7, 13, 22, 35)
t_next = datetime.datetime(2019, 7, 13, 23, 35)
delta1 = datetime.timedelta(seconds = 600)
delta2 = datetime.timedelta(weeks = 3)
print(t+delta1)
print(t+delta2)
print(t_next-t)
timedelta
就是时间间隔,里面的属性就是刚才列出的那些,加上s即可。此外利用datetime
对象还可以做比较运算:
print(t>t_next)
datetime的强大之处在于可以设置时间格式,我们可以先设置一个format
,再利用strptime
方法改变格式。下面举个例子:
from datetime import datetime
str = 'output-2019-7-13-230300' # 要识别的日期
format = 'output-%Y-%m-%d-%H%M%S' # 日期格式
t = datetime.strptime(str,format) # 使用方法
print(t)
可以看到format内使用了%
转义,除了上面的年月日时分秒,还有其他转义:%A
表示英文星期几,%a
表示简写英文星期几,%I
表示十二小时制,%p
表示上午或下午(AM, PM),%f
表示毫秒(2, 0014, 000001),%%
表示百分号。
除此之外,我们还可以将datetime对象用strftime
方法装换成指定格式的字符串。举个例子:
from datetime import datetime
format = '%Y-%m-%d %H:%M'
t = datetime(2019, 7, 13, 23, 50)
print(t.strftime(format))
这里,strftime
方法会根据datetime的参数一次排入format%
特殊字符中,形成指定格式的字符串时间输出。
3 正则表达式
正则表达式是一个定义好的字符串特定的模式,用于搜索指定格式的字符串内容。Python中可以使用re包处理正则表达式,我们可通过其search
方法进行正则匹配。其中search
的第一个参数是正则表达式,第二个是要寻找的字符串。然后在利用正则对象的group
方法查看搜索结果。举个例子:
import re
m = re.search('[0-9]', 'abcde4f')
print(m.group(0))
如果找不到,search
会返回None,可以通过条件判断检查。除了search,match
方法也可以查找,唯一的区别是它会从头开始找是否符合,必须从第一个字符串开始就要匹配,否则就是不符合要求,返回None。
刚刚有一个正则表达式[0-9]是代表任意0-9数字字符串,下面将罗列常用的正则表达式:
. # 任意字符
a|b # 字符a或字符b
[afg] # afg当中的一个字符
[0-4] # 0-4范围的一个字符
[a-f] # a-f中的一个字符
[^m] # 不是m的字符
\s # 一个空格
\S # 一个非空格
\d # 一个数字
\D # 一个非数字,等价于[^0-9]
\w # 数字或字母
\W # 非数字或字母,等价于[^0-9a-zA-Z]
除此之外,正则表达式还有很多特殊符号,跟在一个字符后表示特殊意义:
* # 重复超过0次
+ # 重复1次或超过1次
? # 重复0次或1次
{m} # 重复m次
{m,n} # 重复m到n次
正则表达式用以下两个表示位置:
^ # 起始位置
$ # 结尾位置
下面我们来提取几个试试:
[0-9]{3, 5} # 表示字符串有连续3到5个的1到9数字,如:87978
a?b # 表示字符串有连续0或1个a,后连1个b,如:ab或b
a+b # 表示字符串有连续1个或以上的a,后跟1个b,如:aaaab
我们可以通过更详细的表达获取字符串一部分,例如:
import re
str = 'aaaaaadsfdfwefseftime_2019sdkfuhwifsoidjaf'
str.search('time_(?P<year>\d{4})', str)
print(str.group(1))
print(str.group('year'))
这里有几个新内容需要解释。首先正则表达式出现'time_'意思就是寻找以这个开头的字符串,就是其一部分。正则后面的意思是连续4个数字,外面用小括号括起,代表这是提取中进一步筛选的信息。记得之前使用group(0)得到结果,其实这个得到的是整体结果,可以用group(1)得到第1个筛选结果,以此类推。在括号开头有'?p<year>',这是给筛选信息取名,这里的名字是year,利用group得到信息时可以使用year来提取。所以这两种输出方法是一样的。
4 Python网络编程
计算机可以通过网络进行信息交互,而最常用的网络交互协议就是HTTP协议。所以利用Python做网络编程,首先要学会使用HTTP协议。一般计算机会这么发送消息:
GET /index.html HTTP/1.1
Host: www.example.com
其中get
方法是服务器获取信息的操作,后面带有/的是路径,1.1是HTTP协议的版本。之后服务器接到请求并回复:
HTTP/1.1 200 OK
Content-Type:text/plain
Content-length:12
Hello World!
回复中200
是状态码,OK
是状态码的具体描述,表示一切正常。还有其他的码,比如302
代表要重定向,404
代表找不到。type
意为资源类型,text/plain
是文本类型,此外还有text/html
是HTML文本,image/jpeg
是JPEG图片,image/gif
是GIF图片,length是得到文本的长度。
在Python中,我们利用HTTP的client
库实现HTTP通信,下面举个例子:
import http.client
conn = http.client.HTTPConnection('*www.example.com*')
conn.request('GET', '/')
response = conn.getresponse()
print(response.status, response.reason)
content = response.read()
print(connnect)
这里首先连接网址,并利用request
方法,采用GET
方法得到数据,/
是路径位置。接着利用getresponse
方法得到回复,而回复的对象又有很多属性,status
是状态码,reason
是状态描述。最后我们利用对象的read
方法得到具体内容并输出。
虽然这是个简单的应用,但包括HTTP通信的全部步骤,包括获取信息和得到信息。
5 Python爬虫
利用前面所学知识,我们现在可以写一个网络爬虫进行综合运用。网络爬虫顾名思义就是像一只虫子一样在网络上爬取我们想要的信息。我们可以先通过网络通讯获取网页内容,再利用正则表达式筛选内容,利用datetime库获取相应时间,最后把这些信息总结保存到磁盘文件中。现在我们来试试吧。
import http.client
import re
# 网络通信
conn = http.client.HTTPSConnection('www.cnblogs.com')
conn.request('GET', '/vamei')
response = conn.getresponse()
content = response.read().decode('utf-8')
content = content.split('\n') # 将字符串分隔成序列
# 正则提取
for line in content:
m = re.search('@', line)
if m != None:
print(line)
程序中,我们首先导入包。利用client
包获取信息并利用split
分隔,利用正则表达式,获取评论的时间,然后在列表中逐个字符串寻找并输出筛选信息。
实例代码请看我的码云:第五章样例代码