常用内建模块
datetime
datetime是python处理时间和日期的标准库。
获取当前日期和时间
1 2 3 4 5 from datetime import datetimenow=datetime.now() print (now)2022 -10 -13 16 :01:58.422125
获取指定日期和时间
1 2 3 4 5 from datetime import datetimedt=datetime(2022 ,4 ,19 ,12 ,22 ) print (dt)2022 -04-19 12 :22 :00
datetime转换为timestamp
在计算机中时间是用数字表示的,1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为0,当前时间就是相对于epoch time的秒数 称为timestamp。
1 2 3 4 5 from datetime import datetimedt=datetime(2022 ,4 ,19 ,12 ,22 ) print (dt.timestamp())1650342120.0
timestamp转换为datetime
1 2 3 4 5 from datetime import datetimedt=1650342120.0 print (datetime.fromtimestamp(dt))2022 -04-19 12 :22 :00
str转换为datetime
1 2 3 4 5 from datetime import datetimedt=datetime.strptime('2022-04-19 12:22:00' ,'%Y-%m-%d %H:%M:%S' ) print (dt)2022 -04-19 12 :22 :00
datetime转换为str
1 2 3 4 5 from datetime import datetimenow=datetime.now() print (now.strftime('%a, %b %d %H:%M' ))Thu, Oct 13 16 :13
datetime加减
1 2 3 4 5 6 7 8 9 10 11 from datetime import datetime,timedeltanow=datetime.now() print (now)print (now+timedelta(hours=10 ))print (now+timedelta(days=1 ))print (now+timedelta(days=2 ,hours=12 ))2022 -10 -13 16 :17 :44.344550 2022 -10 -14 02:17 :44.344550 2022 -10 -14 16 :17 :44.344550 2022 -10 -16 04:17 :44.344550
本地时间转换为UTC时间
本地时间是指系统设定时区的时间,如北京时间是UTC+8:00,UTC时间是指UTC+0:00时区的时间。
1 2 3 4 5 6 7 8 9 from datetime import datetime,timedelta,timezonetz_utc_8=timezone(timedelta(hours=8 )) now=datetime.now() print (now)dt=now.replace(tzinfo=tz_utc_8) print (dt)2022 -10 -13 16 :25 :07.688476 2022 -10 -13 16 :25 :07.688476 +08:00
时区转换
1 2 3 4 5 6 7 8 from datetime import datetime,timedelta,timezoneutc_dt=datetime.utcnow().replace(tzinfo=timezone.utc) print (utc_dt)bj_dt=utc_dt.astimezone(timezone(timedelta(hours=8 ))) print (bj_dt)2022 -10 -13 08:28 :08.363061 +00 :00 2022 -10 -13 16 :28 :08.363061 +08:00
collections
collections是Python内建的一个集合模块,提供了许多有用的集合类。
namedtuple
namedtuple是一个函数,用来创建一个自定义的tuple对象,并规定了tuple元素的个数,可以用属性而不是索引来引用tuple的某个元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from collections import namedtuplePoint=namedtuple('Point' ,['x' ,'y' ]) Circle=namedtuple('Circle' ,['x' ,'y' ,'r' ]) p=Point(1 ,2 ) c=Circle(1 ,2 ,3 ) print (isinstance (p,Point))print (isinstance (p,tuple ))print (p.x,p.y)print (c.x,c.y,c.r)True True 1 2 1 2 3
deque
deque可以高效实现插入和删除操作的双向列表,适合用于队列和栈:
1 2 3 4 5 6 7 from collections import deque q=deque(['a' ,'b' ,'c' ]) q.append('x' ) q.appendleft('y' ) print (q)deque(['y' , 'a' , 'b' , 'c' , 'x' ])
除此之外,还支持pop()、popleft()等方法,可以非常高效地往头部添加或删除元素。
defaultdict
使用dict时,若引用的Key不存在,就会抛出KeyError
,若希望Key不存在时返回一个默认值,可以用defaultdict:
1 2 3 4 5 6 7 8 from collections import defaultdictd=defaultdict(lambda :'N/A' ) d['key1' ]='abc' print (d['key1' ])print (d['key2' ])abc N/A
默认值是调用函数返回的,而函数在创建defaultdict对象时传入。
OrderedDict
使用dict时,Key是无序的。若要保持Key的顺序,可以用OrderedDict:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from collections import OrderedDictd=dict ([('c' ,1 ),('a' ,2 ),('b' ,3 )]) print (d)od=OrderedDict([('b' ,1 ),('a' ,2 ),('c' ,3 )]) print (od)odd=OrderedDict() odd['x' ]=1 odd['z' ]=2 odd['y' ]=3 print (list (odd.keys())){'c' : 1 , 'a' : 2 , 'b' : 3 } OrderedDict([('b' , 1 ), ('a' , 2 ), ('c' , 3 )]) ['x' , 'z' , 'y' ]
OrderedDict可以实现一个先进先出的dict,当容量超出限制时,先删除最早添加的Key。
ChainMap
ChainMap可以把一组dict串起来组成一个大的dict。但在查找数据时会按照顺序在内部的dict依次查找。
例如,使用ChainMap来实现参数的优先级查找,其顺序为:命令行参数>环境变量>默认参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from collections import ChainMapfrom email.policy import defaultimport os,argparsedefaults={ 'color' :'red' , 'user' :'guest' } parser=argparse.ArgumentParser() parser.add_argument('-u' ,'--user' ) parser.add_argument('-c' ,'--color' ) namespace=parser.parse_args() command_line_args={k :v for k,v in vars (namespace).items() if v} combined=ChainMap(command_line_args,os.environ,defaults) print ('color=%s' % combined['color' ])print ('user=%s' % combined['user' ])
打开cmd,将路径切换到当前.py
文件所在的目录,运行代码:
在没有任何参数传入时,打印出默认参数:
1 2 3 4 5 C:\Users\Administrator\Desktop>python learn.py color=red user=guest
当传入命令行参数时,优先使用命令行参数:
1 2 3 4 C:\Users\Administrator\Desktop>python learn.py -u bob color=red user=bob
当同时传入命令行参数和环境变量时,也是优先使用命令行参数。
Counter
Counter是一个简单的计数器,也是dict的一个子类,可以统计字符出现的个数:
1 2 3 4 5 6 7 8 9 10 from collections import Counterc=Counter() for ch in 'programming' : c[ch]=c[ch]+1 print (c) c.update('hello' ) print (c)Counter({'r' : 2 , 'g' : 2 , 'm' : 2 , 'p' : 1 , 'o' : 1 , 'a' : 1 , 'i' : 1 , 'n' : 1 }) Counter({'r' : 2 , 'o' : 2 , 'g' : 2 , 'm' : 2 , 'l' : 2 , 'p' : 1 , 'a' : 1 , 'i' : 1 , 'n' : 1 , 'h' : 1 , 'e' : 1 })
urllib
urllib提供了一系列用于操作URL的功能。
Get
urllib的request
模块可以非常方便地抓取URL内容,也就是发送一个GET请求到指定的页面,然后返回HTTP的响应。例如,对百度网址进行抓取,并返回响应::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from urllib import requestwith request.urlopen('https://baidu.com/' ) as f: data = f.read() print ('Status:' , f.status, f.reason) for k, v in f.getheaders(): print ('%s: %s' % (k, v)) print ('Data:' , data.decode('utf-8' )) Status: 200 OK Bdpagetype: 1 Bdqid: 0x8dfa7ad90002130f Content-Type : text/html; charset=utf-8 Date: Mon, 24 Oct 2022 09:01:21 GMT ... Data: <!DOCTYPE html><!--STATUS OK--><html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" ><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" ><meta content="always" name="referrer" ><meta name="theme-color" content="#ffffff" ><meta name="description" content="全球领先的中文...
若要模拟浏览器发送GET请求,需要使用Request
对象,通过往Request
对象添加HTTP头,可以把请求伪装成浏览器。例如,模拟iPhone 6去请求豆瓣首页:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from urllib import requestreq = request.Request('http://www.douban.com/' ) req.add_header('User-Agent' , 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25' ) with request.urlopen(req) as f: print ('Status:' , f.status, f.reason) for k, v in f.getheaders(): print ('%s: %s' % (k, v)) print ('Data:' , f.read().decode('utf-8' )) <!DOCTYPE html> <html itemscope itemtype="http://schema.org/WebPage" class ="ua-safari ua-mobile " > <head> <meta charset="UTF-8" > <title>豆瓣(手机版)</title> <meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" />...
Post
若要以POST发送一个请求,只需要把参数data
以bytes形式传入。
例如模拟一个微博登录,先读取登录的邮箱和口令,然后按照weibo.cn的登录页的格式以username=xxx&password=xxx
的编码传入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from urllib import request, parseprint ('Login to weibo.cn...' )email = input ('Email: ' ) passwd = input ('Password: ' ) login_data = parse.urlencode([ ('username' , email), ('password' , passwd), ('entry' , 'mweibo' ), ('client_id' , '' ), ('savestate' , '1' ), ('ec' , '' ), ('pagerefer' , 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F' ) ]) req = request.Request('https://passport.weibo.cn/sso/login' ) req.add_header('Origin' , 'https://passport.weibo.cn' ) req.add_header('User-Agent' , 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25' ) req.add_header('Referer' , 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F' ) with request.urlopen(req, data=login_data.encode('utf-8' )) as f: print ('Status:' , f.status, f.reason) for k, v in f.getheaders(): print ('%s: %s' % (k, v)) print ('Data:' , f.read().decode('utf-8' ))
若登录成功,获得的响应如下:
1 2 3 4 5 6 Status: 200 OK Server: nginx/1.2 .0 ... Set -Cookie: SSOLoginState=1432620126 ; path=/; domain=weibo.cn... Data: {"retcode" :20000000 ,"msg" :"" ,"data" :{...,"uid" :"1658384301" }}
若登录失败,获得的响应如下:
1 2 ... Data: {"retcode" :50011002 ,"msg" :"\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef" ,"data" :{"username" :"123456@qq.com" ,"errline" :15 }}
Handler
若需要更复杂的控制,比如通过一个Proxy去访问网站,需要利用ProxyHandler
来处理,例如:
1 2 3 4 5 6 proxy_handler = urllib.request.ProxyHandler({'http' : 'http://www.example.com:3128/' }) proxy_auth_handler = urllib.request.ProxyBasicAuthHandler() proxy_auth_handler.add_password('realm' , 'host' , 'username' , 'password' ) opener = urllib.request.build_opener(proxy_handler, proxy_auth_handler) with opener.open ('http://www.example.com/login.html' ) as f: pass
常用第三方模块
requests
Python内置的urllib模块,用于访问网络资源。但是,它用起来比较麻烦,而且,缺少很多实用的高级功能。
更好的方案是使用requests。它是一个Python第三方库,处理URL资源特别方便。
安装request:pip install requests
requests的使用
通过GET访问一个页面:
1 2 3 4 5 6 7 8 import requests r=requests.get('https://www.baidu.com/' ) print (r.status_code)print (r.text)200 '''<!DOCTYPE html> <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css ...'''
对于带参数的URL,传入一个dict作为params参数:
1 2 3 4 5 6 7 8 9 import requests r=requests.get('https://www.baidu.com/' ,params={'q' :'python' ,'cat' :'1001' }) print (r.url)print (r.encoding)print (r.content)https://www.baidu.com/?q=python&cat=1001 ISO-8859 -1 '''b'<!DOCTYPE html>\r\n<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css ...'''
对于特定类型的响应,例如JSON,可以直接获取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import requests r1=requests.get('https://p.3.cn/prices/mgets?skuIds=J_10026711061553' ) r2=requests.get('https://douban.com/' ,headers={'User-Agent' : 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit' }) print (r1.json())print (r2.text)[{'exception' : '该接口即将下线,请联系(erp)wangjianyu1,liuhuimin9,liteng36;p.3.cn,null' }] '''<!DOCTYPE html> <html itemscope itemtype="http://schema.org/WebPage" class="ua-mobile "> <head> <meta charset="UTF-8"> <title>豆瓣(手机版)</title> <meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" /> ...'''
需要发送POST请求,只需要把get()方法变成post(),然后传入data参数作为POST请求的数据:
1 r=requests.post('https://accounts.douban.com/login' , data={'form_email' : 'abc@example.com' , 'form_password' : '123456' })
requests默认使用application/x-www-form-urlencoded
对POST数据编码。如果要传递JSON数据,可以直接传入json参数:
1 2 params={'key' : 'value' } r=requests.post(url,json=params)
类似的,上传文件需要更复杂的编码格式,但是requests把它简化成files
参数:
1 2 upload_files={'file' :open ('report.xls' ,'rb' )} r=requests.post(url,files=upload_files)
在读取文件时,注意务必使用'rb'
即二进制模式读取,这样获取的bytes
长度才是文件的长度。
requests对获取HTTP响应的其他信息也非常简单。例如,获取响应头:
1 2 3 4 5 print (r.headers)print (r.headers['Content-Type' ]){Content-Type ': ' text/html; charset=utf-8 ', ' Transfer-Encoding': ' chunked', ' Content-Encoding': ' gzip', ...} ' text/html; charset=utf-8 '
requests对Cookie做了特殊处理,使得不必解析Cookie就可以轻松获取指定的Cookie:
1 2 3 print (r.cookies['ts' ])'example_cookie_12345'
要在请求中传入Cookie,只需准备一个dict传入cookies
参数:
1 2 cs={'token' :'12345' ,'status' :'working' } r=requests.get(url,cookies=cs)
最后,要指定超时,传入以秒为单位的timeout参数:
1 r=requests.get(url,timeout=2.5 )
练习题
参考《OA统一待办接口协议》,使用requests访问统一待办接口,给自己生成一个待办,确认生成后,调用删除接口删除待办。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import requests import jsonheaders={'Content-Type' :'application/json' } url='GetToken的URL地址' url_create='CreateTask的URL地址' url_del='DeleteTask的URL地址' data={"app_id" :"app_id的数值" ,"app_secret" :"app_secret的数值" } r=requests.post(url,json=data) token=json.loads(r.text)['access_token' ] data_create={"access_token" :token,"title" :"20221025测试" ,"task_segment" :"报名" ,\ "task_url" :"http://tangmenjue.top/" ,"staff_id" :"staff_id的数值" } r_create=requests.post(url_create,json=data_create) print (r_create.text)r_del=requests.post(url,json=data) task_id=json.loads(r_create.text)['task_id' ] data_del={"access_token" :token,'task_id' :task_id} r_del=requests.post(url_del,json=data_del) print (r_del.text)
chardet
字符串编码一直是令人非常头疼的问题,尤其是在处理一些不规范的第三方网页的时候。虽然Python提供了Unicode表示的str
和bytes
两种数据类型,并且可以通过encode()
和decode()
方法转换,但是,在不知道编码的情况下,对bytes
做decode()
不好做。
对于未知编码的bytes
,要把它转换成str
,需要先“猜测”编码。猜测的方式是先收集各种编码的特征字符,根据特征字符判断,就能有很大概率“猜对”,此时可以用chardet来检测编码。
venv
在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.10。所有第三方的包都会被pip
安装到Python3的site-packages
目录下。
如果要同时开发多个应用程序,那这些应用程序都会共用一个Python,就是安装在系统的Python 3。如果应用A需要jinja 2.7,而应用B需要jinja 2.6怎么办?
这种情况下,每个应用可能需要各自拥有一套“独立”的Python运行环境。venv就是用来为一个应用创建一套“隔离”的Python运行环境。