python013 -- 常用標準庫

本章涉及標準庫:

1、sys

2、os

3、glob

4、math

5、random

6、platform

7、pikle與cPikle

8、subprocess

9、Queue

10、StringIO

11、logging

12、ConfigParser

13、urllib與urllib2

14、json

15、time

16、datetime


10.1 sys

1)sys.argv

命令行參數。

argv[0] #代表本身名字

argv[1] #第一個參數

argv[2] #第二個參數

argv[3] #第三個參數

argv[N] #第N個參數

argv #參數以空格分隔存儲到列表。

看看使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print sys.argv[0]
print sys.argv[1]
print sys.argv[2]
print sys.argv[3]
print sys.argv
print len(sys.argv)
 
# python test.py
test.py
a
b
c
c
['test.py''a''b''c']
4

值得注意的是,argv既然是一個列表,那麼可以通過len()函數獲取這個列表的長度從而知道輸入的參數數量。可以看到列表把自身文件名也寫了進去,所以當我們統計的使用應該-1纔是實際的參數數量,因此可以len(sys.argv[1:])獲取參數長度。

2)sys.path

模塊搜索路徑。

1
2
>>> sys.path
['', '/usr/local/lib/python2.7/dist-packages/tornado-3.1-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages']

輸出的是一個列表,裏面包含了當前Python解釋器所能找到的模塊目錄。

如果想指定自己的模塊目錄,可以直接追加:

1
2
3
>>> sys.path.append('/opt/scripts')
>>> sys.path
['', '/usr/local/lib/python2.7/dist-packages/tornado-3.1-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/opt/scripts']

3)sys.platform

系統平臺標識符。

系統 平臺標識符
Linux linux
Windows win32
Windows/Cygwin cygwin
Mac OS X darwin

>>> sys.platform

'linux2'

Python本身就是跨平臺語言,但也不就意味着所有的模塊都是在各種平臺通用,所以可以使用這個方法判斷當前平臺,做相應的操作。

4)sys.subversion

在第一章講過Python解釋器有幾種版本實現,而默認解釋器是CPython,來看看是不是:

1
2
>>> sys.subversion
('CPython', '', '')

5)sys.version

查看Python版本:

1
2
>>> sys.version
'2.7.6 (default, Jun 22 2015, 17:58:13) \n[GCC 4.8.2]'

6)sys.exit()

退出解釋器:

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print "Hello world!"
sys.exit()
print "Hello world!"
 
# python test.py
Hello world!

代碼執行到sys.exit()就會終止程序。

7)sys.stdin、sys.stdout和sys.stderr

標準輸入、標準輸出和錯誤輸出。

標準輸入:一般是鍵盤。stdin對象爲解釋器提供輸入字符流,一般使用raw_input()和input()函數。

例如:讓用戶輸入信息

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
name = raw_input("Please input your name: ")
print name
 
# python test.py
Please input your name: xiaoming
xiaoming

1
2
3
4
5
6
7
8
9
import sys
print "Please enter your name: "
name = sys.stdin.readline()
print name
 
# python b.py
Please enter your name:
xiaoming
xiaoming

再例如,a.py文件標準輸出作爲b.py文件標準輸入:

1
2
3
4
5
6
7
8
9
10
# cat a.py
import sys
sys.stdout.write("123456\n")
sys.stdout.flush()
# cat b.py
import sys
print sys.stdin.readlines()
 
# python a.py | python b.py
['123456\n']

sys.stdout.write()方法其實就是下面所講的標準輸出,print語句就是調用了這個方法。

標準輸出:一般是屏幕。stdout對象接收到print語句產生的輸出。

例如:打印一個字符串

1
2
3
4
5
6
7
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print "Hello world!"
 
# python test.py
Hello world!

sys.stdout是有緩衝區的,比如:

1
2
3
4
5
6
7
8
import sys
import time
for in range(5):
    print i,
    # sys.stdout.flush()
    time.sleep(1)
# python test.py
0 1 2 3 4

本是每隔一秒輸出一個數字,但現在是循環完纔會打印所有結果。如果把sys.stdout.flush()去掉,就會沒執行到print就會刷新stdout輸出,這對實時輸出信息的程序有幫助。

錯誤輸出:一般是錯誤信息。stderr對象接收出錯的信息。

例如:引發一個異常

1
2
3
>>> raise Exception, "raise..."
Traceback (most recent call last):File "<stdin>", line 1in <module>
Exception: raise...


博客地址:http://lizhenliang.blog.51cto.com

QQ羣:323779636(Shell/Python運維開發羣


10.2 os

os模塊主要對目錄或文件操作。

方法 描述 示例
os.name 返回操作系統類型 返回值是"posix"代表linux,"nt"代表windows
os.extsep 返回一個"."標識符
os.environ 以字典形式返回系統變量
os.devnull 返回/dev/null標識符
os.linesep 返回一個換行符"\n" >>> print "a" + os.linesep + "b"
a
b
os.sep 返回一個路徑分隔符正斜槓"/" >>> "a" + os.sep + "b"
'a/b'
os.listdir(path) 列表形式列出目錄
os.getcwd() 獲取當前路徑 >>> os.getcwd()
'/home/user'
os.chdir(path) 改變當前工作目錄到指定目錄 >>> os.chdir('/opt')
>>> os.getcwd()
'/opt'
os.mkdir(path [, mode=0777]) 創建目錄 >>> os.mkdir('/home/user/test')
os.makedirs(path [, mode=0777]) 遞歸創建目錄 >>> os.makedirs('/home/user/abc/abc')
os.rmdir(path) 移除空目錄 >>> os.makedirs('/home/user/abc/abc')
os.remove(path) 移除文件
os.rename(old, new) 重命名文件或目錄
os.stat(path) 獲取文件或目錄屬性
os.chown(path, uid, gid) 改變文件或目錄所有者
os.chmod(path, mode) 改變文件訪問權限 >>> os.chmod('/home/user/c/a.tar.gz', 0777) 
os.symlink(src, dst) 創建軟鏈接
os.unlink(path) 移除軟鏈接 >>> os.unlink('/home/user/ddd')
urandom(n) 返回隨機字節,適合加密使用 >>> os.urandom(2)
'%\xec'
os.getuid() 返回當前進程UID
os.getlogin() 返回登錄用戶名
os.getpid() 返回當前進程ID
os.kill(pid, sig) 發送一個信號給進程
os.walk(path) 目錄樹生成器,返回格式:(dirpath, [dirnames], [filenames]) >>> for root, dir, file in os.walk('/home/user/abc'):
...   print root
...   print dir
...   print file
os.statvfs(path)

os.system(command) 執行shell命令,不能存儲結果
popen(command [, mode='r' [, bufsize]]) 打開管道來自shell命令,並返回一個文件對象 >>> result = os.popen('ls')

>>> result.read()

os.path類用於獲取文件屬性。

os.path.basename(path) 返回最後一個文件或目錄名 >>> os.path.basename('/home/user/a.sh')
'a.sh'
os.path.dirname(path) 返回最後一個文件前面目錄 >>> os.path.dirname('/home/user/a.sh')
'/home/user'
os.path.abspath(path) 返回一個絕對路徑 >>> os.path.abspath('a.sh')
'/home/user/a.sh'
os.path.exists(path) 判斷路徑是否存在,返回布爾值 >>> os.path.exists('/home/user/abc')
True
os.path.isdir(path) 判斷是否是目錄
os.path.isfile(path) 判斷是否是文件
os.path.islink(path) 判斷是否是鏈接
os.path.ismount(path) 判斷是否掛載
os.path.getatime(filename) 返回文件訪問時間戳 >>> os.path.getctime('a.sh')
1475240301.9892483
os.path.getctime(filename) 返回文件變化時間戳
os.path.getmtime(filename) 返回文件修改時間戳
os.path.getsize(filename) 返回文件大小,單位字節
os.path.join(a, *p) 加入兩個或兩個以上路徑,以正斜槓"/"分隔。常用於拼接路徑 >>> os.path.join('/home/user','test.py','a.py')
'/home/user/test.py/a.py'
os.path.split( 分隔路徑名 >>> os.path.split('/home/user/test.py')
('/home/user', 'test.py')
os.path.splitext( 分隔擴展名 >>> os.path.splitext('/home/user/test.py')

('/home/user/test', '.py')

10.3 glob

文件查找,支持通配符(*、?、[])

1
2
3
4
5
6
7
8
9
10
11
# 查找目錄中所有以.sh爲後綴的文件
>>> glob.glob('/home/user/*.sh')
['/home/user/1.sh''/home/user/b.sh''/home/user/a.sh''/home/user/sum.sh']
 
# 查找目錄中出現單個字符並以.sh爲後綴的文件
>>> glob.glob('/home/user/?.sh')
['/home/user/1.sh''/home/user/b.sh''/home/user/a.sh']
 
# 查找目錄中出現a.sh或b.sh的文件
>>> glob.glob('/home/user/[a|b].sh')
['/home/user/b.sh''/home/user/a.sh']

10.4 math

數字處理。

下面列出一些自己決定會用到的:

方法 描述 示例
math.pi 返回圓周率 >>> math.pi
3.141592653589793
math.ceil(x) 返回x浮動的上限 >>> math.ceil(5.2)
6.0
math.floor(x) 返回x浮動的下限 >>> math.floor(5.2)
5.0
math.trunc(x) 將數字截尾取整 >>> math.trunc(5.2)
5
math.fabs(x) 返回x的絕對值 >>> math.fabs(-5.2)
5.2
math.fmod(x,y) 返回x%y(取餘) >>> math.fmod(5,2) 
1.0
math.modf(x) 返回x小數和整數 >>> math.modf(5.2)
(0.20000000000000018, 5.0)
math.factorial(x) 返回x的階乘 >>> math.factorial(5)
120
math.pow(x,y) 返回x的y次方 >>> math.pow(2,3)
8.0
math.sprt(x) 返回x的平方根 >>> math.sqrt(5)

2.2360679774997898

10.5 random

生成隨機數。

常用的方法:

方法 描述 示例
random.randint(a,b) 返回整數a和b範圍內數字 >>> random.randint(1,10)
6
random.random() 返回隨機數,它在0和1範圍內 >>> random.random()
0.7373251914304791
random.randrange(start, stop[, step]) 返回整數範圍的隨機數,並可以設置只返回跳數 >>> random.randrange(1,10,2)
5
random.sample(array, x) 從數組中返回隨機x個元素 >>> random.sample([1,2,3,4,5],2)
[2, 4]

10.6 platform

獲取操作系統詳細信息。

方法 描述 示例
platform.platform() 返回操作系統平臺 >>> platform.platform()
'Linux-3.13.0-32-generic-x86_64-with-Ubuntu-14.04-trusty'
platform.uname() 返回操作系統信息 >>> platform.uname()
('Linux', 'ubuntu', '3.13.0-32-generic', '#57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014', 'x86_64', 'x86_64')
platform.system() 返回操作系統平臺 >>> platform.system()
'Linux'
platform.version() 返回操作系統版本 >>> platform.version()
'#57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014'
platform.machine() 返回計算機類型 >>> platform.machine()
'x86_64'
platform.processor() 返回計算機處理器類型 >>> platform.processor()
'x86_64'
platform.node() 返回計算機網絡名 >>> platform.node()   
'ubuntu'
platform.python_version() 返回Python版本號 >>> platform.python_version()

'2.7.6'

10.7 pickle與cPickle

創建可移植的Python序列化對象,持久化存儲到文件。

1)pickle

pickle庫有兩個常用的方法,dump()、load() 和dumps()、 loads(),下面看看它們的使用方法:

dump()方法是把對象保存到文件中。

格式:dump(obj, file, protocol=None)

load()方法是從文件中讀數據,重構爲原來的Python對象。

格式:load(file)

示例,將字典序列化到文件:

1
2
3
4
5
>>> import pickle
>>> dict = {'a':1'b':2'c':3}
>>> output = open('data.pkl''wb')  # 二進制模式打開文件
>>> pickle.dump(dict, output)   # 執行完導入操作,當前目錄會生成data.pkl文件
>>> output.close()  # 寫入數據並關閉

看看pickle格式後的文件:

1
2
3
4
5
6
7
8
9
10
11
12
# cat data.pkl
(dp0
S'a'
p1
I1
sS'c'
p2
I3
sS'b'
p3
I2
s.

讀取序列化文件:

1
2
3
4
>>> f = open('data.pkl')
>>> data = pickle.load(f)
>>> print data
{'a'1'c'3'b'2}

用法挺簡單的,就是先導入文件,再讀取文件。

接下來看看序列化字符串操作:

dumps()返回一個pickle格式化的字符串

格式:dumps(obj, protocol=None)

load()解析pickle字符串爲對象

示例:

1
2
3
4
5
6
7
8
>>> s = 'abc'
>>> pickle.dumps(s) 
"S'abc'\np0\n."
>>> pkl = pickle.dumps(s)
>>> pkl
"S'abc'\np0\n."
>>> pickle.loads(pkl)
'abc'

需要注意的是,py2.x使用的是pickle2.0格式版本,如果用3.0、4.0版本的pickle導入會出錯。可以通過pickle.format_version 查看版本。


博客地址:http://lizhenliang.blog.51cto.com

QQ羣:323779636(Shell/Python運維開發羣


2)cPickle

cPickle庫是C語言實現,對pickle進行了優化,提升了性能,建議在寫代碼中使用。

cPicke提供了與pickle相同的dump()、load() 和dumps()、 loads()方法,用法一樣,不再講解。

10.8 subprocess

subprocess庫會fork一個子進程去執行任務,連接到子進程的標準輸入、輸出、錯誤,並獲得它們的返回代碼。這個模塊將取代os.system、os.spawn*、os.popen*、popen2.*和commands.*。

提供了以下常用方法幫助我們執行bash命令的相關操作:

subprocess.call():運行命令與參數。等待命令完成,返回執行狀態碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> import subprocess 
>>> retcode = subprocess.call(["ls""-l"])
total 504
-rw-r--r-- 1 root root      54 Nov  2 06:15 data.pkl
>>> retcode
0
>>> retcode = subprocess.call(["ls""a"]) 
ls: cannot access a: No such file or directory
>>> retcode
2
 
# 也可以這樣寫
>>> subprocess.call('ls -l', shell=True)
subprocess.check_call():運行命令與參數。如果退出狀態碼非0,引發CalledProcessError異常,包含狀態碼。
>>> subprocess.check_call("ls a", shell=True)
ls: cannot access a: No such file or directory
Traceback (most recent call last):
  File "<stdin>", line 1in <module>
  File "/usr/lib/python2.7/subprocess.py", line 540in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'ls a' returned non-zero exit status 2

subprocess.Popen():這個類我們主要來使用的,參數較多。

參數

描述

args 命令,字符串或列表
bufsize 0代表無緩衝,1代表行緩衝,其他正值代表緩衝區大小,負值採用默認系統緩衝(一般是全緩衝)
executable

stdin

stdout

stderr

默認沒有任何重定向,可以指定重定向到管道(PIPE)、文件對象、文件描述符(整數),stderr還可以設置爲STDOUT
preexec_fn 鉤子函數,在fork和exec之間執行
close_fds
shell 爲True,表示用當前默認解釋器執行。相當於args前面添加“/bin/sh”“-c或win下"cmd.exe /c "
cwd 指定工作目錄
env 設置環境變量
universal_newlines 換行符統一處理成"\n"
startupinfo 在windows下的Win32 API 發送CreateProcess()創建進程
creationflags 在windows下的Win32 API 發送CREATE_NEW_CONSOLE()創建控制檯窗口

subprocess.Popen()類又提供了以下些方法:

方法

描述

Popen.communicate(input=None) 與子進程交互。讀取從stdout和stderr緩衝區內容,阻塞父進程,等待子進程結束
Popen. kill() 殺死子進程,在Posix系統上發送SIGKILL信號
Popen.pid 獲取子進程PID
Popen.poll() 如果子進程終止返回狀態碼
Popen.returncode 返回子進程狀態碼
Popen.send_signal(signal) 發送信號到子進程
Popen.stderr 如果參數值是PIPE,那麼這個屬性是一個文件對象,提供子進程錯誤輸出。否則爲None
Popen.stdin 如果參數值是PIPE,那麼這個屬性是一個文件對象,提供子進程輸入。否則爲None
Popen.stdout 如果參數值是PIPE,那麼這個屬性是一個文件對象,提供子進程輸出。否則爲None
Popen.terminate() 終止子進程,在Posix系統上發送SIGTERM信號,在windows下的Win32 API發送TerminateProcess()到子進程
Popen.wait() 等待子進程終止,返回狀態碼

示例:

1
2
3
4
5
6
7
8
9
>>> p = subprocess.Popen('dmesg |grep eth0', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
>>> p.communicate() 
......  # 元組形式返回結果
>>> p.pid
57039
>>> p.wait()
0
>>> p.returncode
0

subprocess.PIPE提供了一個緩衝區,將stdout、stderr放到這個緩衝區中,p.communicate()方法讀取緩衝區數據。

緩衝區的stdout、stderr是分開的,可以以p.stdout.read()方式獲得標準輸出、錯誤輸出的內容。

再舉個例子,我們以標準輸出作爲下個Popen任務的標準輸入:

>>> p1 = subprocess.Popen('ls', stdout=subprocess.PIPE, shell=True)

>>> p2 = subprocess.Popen('grep data', stdin=p1.stdout, stdout=subprocess.PIPE, shell=True)

>>> p1.stdout.close()   # 調用後啓動p2,爲了獲得SIGPIPE

>>> output = p2.communicate()[0]

>>> output

'data.pkl\n'

p1的標準輸出作爲p2的標準輸入。這個p2的stdin、stdout也可以是個可讀、可寫的文件。

10.9 Queue

隊列,數據存放在內存中,一般用於交換數據。

描述

Queue.Empty 當非阻塞get()或get_nowait()對象隊列上爲空引發異常
Queue.Full 當非阻塞put()或put_nowait()對象隊列是一個滿的隊列引發異常
Queue.LifoQueue(maxsize=0)   構造函數爲後進先出隊列。maxsize設置隊列最大上限項目數量。小於或等於0代表無限。
Queue.PriorityQueue(maxsize=0) 構造函數爲一個優先隊列。級別越高越先出。
Queue.Queue(maxsize=0) 構造函數爲一個FIFO(先進先出)隊列。maxsize設置隊列最大上限項目數量。小於或等於0代表無限。
Queue.deque 雙端隊列。實現快速append()和popleft(),無需鎖。
Queue.heapq 堆排序隊列。

用到比較多的是Queue.Queue類,在這裏主要了解下這個。

它提供了一些操作隊列的方法:

方法

描述

Queue.empty() 如果隊列爲空返回True,否則返回False
Queue.full() 如果隊列是滿的返回True,否則返回False
Queue.get(block=True, timeout=None) 從隊列中刪除並返回一個項目。沒有指定項目,因爲是FIFO隊列,如果隊列爲空會一直阻塞。timeout超時時間
Queue.get_nowait()   從隊列中刪除並返回一個項目,不阻塞。會拋出異常。
Queue.join() 等待隊列爲空,再執行別的操作
Queue.put(item, block=True, timeout=None) 寫入項目到隊列
Queue.put_nowait() 寫入項目到隊列,不阻塞。與get同理
Queue.qsize()   返回隊列大小
Queue.task_done() 表示原隊列的任務完成

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> from Queue import Queue
>>> q = Queue()
>>> q.put('test')
>>> q.qsize()
1
>>> q.get()
'test'
>>> q.qsize()
0
>>> q.full()
False
>>> q.empty()
True

10.10 StringIO

StringIO庫將字符串存儲在內存中,像操作文件一樣操作。主要提供了一個StringIO類。

方法

描述

StringIO.close() 關閉
StringIO.flush() 刷新緩衝區
StringIO.getvalue() 獲取寫入的數據
StringIO.isatty()
StringIO.next() 讀取下一行,沒有數據拋出異常
StringIO.read(n=-1)  默認讀取所有內容。n指定讀取多少字節
StringIO.readline(length=None) 默認讀取下一行。length指定讀取多少個字符
StringIO.readlines(sizehint=0) 默認讀取所有內容,以列表返回。sizehint指定讀取多少字節
StringIO.seek(pos, mode=0) 在文件中移動文件指針,從mode(0代表文件起始位置,默認。1代表當前位置。2代表文件末尾)偏移pos個字節
StringIO.tell() 返回當前在文件中的位置
StringIO.truncate() 截斷文件大小
StringIO.write(str) 寫字符串到文件
StringIO.writelines(iterable) 寫入序列,必須是一個可迭代對象,一般是一個字符串列表

可以看到,StringIO方法與文件對象方法大部分都一樣,從而也就能方面的操作內存對象。

示例:

1
2
3
4
>>> f = StringIO()
>>> f.write('hello')
>>> f.getvalue()
'hello'

像操作文件對象一樣寫入。

用一個字符串初始化StringIO,可以像讀文件一樣讀取:

1
2
3
4
5
6
7
8
>>> f = StringIO('hello\nworld!')
>>> f.read()
'hello\nworld!'
>>> s = StringIO('hello world!')
>>> s.seek(5)            # 指針移動到第五個字符,開始寫入      
>>> s.write('-')               
>>> s.getvalue()
'hello-world!'

10.11 logging

記錄日誌庫。

有幾個主要的類:

logging.Logger 應用程序記錄日誌的接口
logging.Filter 過濾哪條日誌不記錄
logging.FileHandler 日誌寫到磁盤文件
logging.Formatter 定義最終日誌格式

日誌級別:

級別 數字值 描述
critical 50 危險
error 40 錯誤
warning 30 警告
info 20 普通信息
debug 10 調試
noset 0 不設置

Formatter類可以自定義日誌格式,默認時間格式是%Y-%m-%d %H:%M:%S,有以下這些屬性:

%(name)s 日誌的名稱
%(levelno)s 數字日誌級別
%(levelname)s 文本日誌級別
%(pathname)s 調用logging的完整路徑(如果可用)
%(filename)s 文件名的路徑名
%(module)s 模塊名
%(lineno)d 調用logging的源行號
%(funcName)s 函數名
%(created)f 創建時間,返回time.time()值
%(asctime)s 字符串表示創建時間
%(msecs)d 毫秒錶示創建時間
%(relativeCreated)d 毫秒爲單位表示創建時間,相對於logging模塊被加載,通常應用程序啓動。
%(thread)d 線程ID(如果可用)
%(threadName)s 線程名字(如果可用)
%(process)d 進程ID(如果可用)
%(message)s 輸出的消息

示例:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/python
# -*- coding: utf-8 -*-
#--------------------------------------------------
# 日誌格式
#--------------------------------------------------
# %(asctime)s       年-月-日 時-分-秒,毫秒 2013-04-26 20:10:43,745
# %(filename)s      文件名,不含目錄
# %(pathname)s      目錄名,完整路徑
# %(funcName)s      函數名
# %(levelname)s     級別名
# %(lineno)d        行號
# %(module)s        模塊名
# %(message)s       消息體
# %(name)s          日誌模塊名
# %(process)d       進程id
# %(processName)s   進程名
# %(thread)d        線程id
# %(threadName)s    線程名
import logging
format = logging.Formatter('%(asctime)s - %(levelname)s %(filename)s [line:%(lineno)d] %(message)s')
# 創建日誌記錄器
info_logger = logging.getLogger('info')
# 設置日誌級別,小於INFO的日誌忽略
info_logger.setLevel(logging.INFO)
# 日誌記錄到磁盤文件
info_file = logging.FileHandler("info.log")
# info_file.setLevel(logging.INFO)
# 設置日誌格式
info_file.setFormatter(format)
info_logger.addHandler(info_file)
error_logger = logging.getLogger('error')
error_logger.setLevel(logging.ERROR)
error_file = logging.FileHandler("error.log")
error_file.setFormatter(format)
error_logger.addHandler(error_file)
# 輸出控制檯(stdout)
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
console.setFormatter(format)
info_logger.addHandler(console)
error_logger.addHandler(console)
if __name__ == "__main__":
    # 寫日誌
    info_logger.warning("info message.")
    error_logger.error("error message!")
1
2
3
4
5
6
7
# python test.py
2016-07-02 06:52:25,624 - WARNING test.py [line:49] info message.
2016-07-02 06:52:25,631 - ERROR test.py [line:50] error message!
# cat info.log
2016-07-02 06:52:25,624 - WARNING test.py [line:49] info message.
# cat error.log
2016-07-02 06:52:25,631 - ERROR test.py [line:50] error message!

上面代碼實現了簡單記錄日誌功能。分別定義了info和error日誌,將等於或高於日誌級別的日誌寫到日誌文件中。在小項目開發中把它單獨寫一個模塊,很方面在其他代碼中調用。

需要注意的是,在定義多個日誌文件時,getLogger(name=None)類的name參數需要指定一個唯一的名字,如果沒有指定,日誌會返回到根記錄器,也就是意味着他們日誌都會記錄到一起。

10.12 ConfigParser

配置文件解析。

這個庫我們主要用到ConfigParser.ConfigParser()類,對ini格式文件增刪改查。

ini文件固定結構:有多個部分塊組成,每個部分有一個[標識],並有多個key,每個key對應每個值,以等號"="分隔。值的類型有三種:字符串、整數和布爾值。其中字符串可以不用雙引號,布爾值爲真用1表示,布爾值爲假用0表示。註釋以分號";"開頭。

方法

描述

ConfigParser.add_section(section) 創建一個新的部分配置
ConfigParser.get(section, option, raw=False, vars=None) 獲取部分中的選項值,返回字符串
ConfigParser.getboolean(section, option) 獲取部分中的選項值,返回布爾值
ConfigParser.getfloat(section, option) 獲取部分中的選項值,返回浮點數
ConfigParser.getint(section, option) 獲取部分中的選項值,返回整數
ConfigParser.has_option(section, option) 檢查部分中是否存在這個選項
ConfigParser.has_section(section) 檢查部分是否在配置文件中
ConfigParser.items(section, raw=False, vars=None) 列表元組形式返回部分中的每一個選項
ConfigParser.options(section) 列表形式返回指定部分選項名稱
ConfigParser.read(filenames) 讀取ini格式的文件
ConfigParser.remove_option( section, option) 移除部分中的選項
ConfigParser.remove_section(section, option) 移除部分
ConfigParser.sections() 列表形式返回所有部分名稱
ConfigParser.set(section, option, value) 設置選項值,存在則更新,否則添加
ConfigParser.write(fp) 寫一個ini格式的配置文件

舉例說明,寫一個ini格式文件,對其操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# cat config.ini
[host1]   
host = 192.168.1.1
port = 22
user = zhangsan
pass = 123
[host2]
host = 192.168.1.2
port = 22
user = lisi
pass = 456
[host3]
host = 192.168.1.3
port = 22
user = wangwu
pass = 789

1)獲取部分中的鍵值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
section = conf.sections()[0]  # 獲取隨機的第一個部分標識
options = conf.options(section) # 獲取部分中的所有鍵
key = options[2]
value = conf.get(section, options[2]) # 獲取部分中鍵的值
print key, value
print type(value)
 
# python test.py
port 22
<type 'str'>

這裏有意打出來了值的類型,來說明下get()方法獲取的值都是字符串,如果有需要,可以getint()獲取整數。測試發現,ConfigParser是從下向上讀取的文件內容!

2)遍歷文件中的每個部分的每個字段

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
28
29
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
sections = conf.sections()  # 獲取部分名稱 ['host3', 'host2', 'host1']
for section in sections:
    options = conf.options(section) # 獲取部分名稱中的鍵 ['user', 'host', 'port', 'pass']
    for option in options:
         value = conf.get(section, option) # 獲取部分中的鍵值
         print option + ": " + value  
    print "-------------"
     
# python test.py
user: wangwu
host: 192.168.1.3
port: 22
pass789
-------------
user: lisi
host: 192.168.1.2
port: 22
pass456
-------------
user: zhangsan
host: 192.168.1.1
port: 22
pass123
-------------

通過上面的例子,熟悉了sections()、options()和get(),能任意獲取文件的內容了。

也可以使用items()獲取部分中的每個選項:

1
2
3
4
5
6
7
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
print conf.items('host1')
 
# python test.py
[('user''zhangsan'), ('host''192.168.1.1'), ('port''22'), ('pass''123')]

3)更新或添加選項

1
2
3
4
5
6
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
fp = open("config.ini""w")   # 寫模式打開文件,供後面提交寫的內容
conf.set("host1""port""2222")  # 有這個選項就更新,否則添加
conf.write(fp)  # 寫入的操作必須執行這個方法

4)添加一部分,並添加選項

1
2
3
4
5
6
7
8
9
10
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
fp = open("config.ini""w")
conf.add_section("host4")   # 添加[host4]
conf.set("host4""host""192.168.1.4")
conf.set("host4""port""22")
conf.set("host4""user""zhaoliu")
conf.set("host4""pass""123")
conf.write(fp)

5)刪除一部分

1
2
3
4
5
6
7
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
fp = open("config.ini""w")
conf.remove_section('host4')  # 刪除[host4]
conf.remove_option('host3''pass')  # 刪除[host3]的pass選項
conf.write(fp)

10.13 urllib與urllib2

打開URL。urllib2是urllib的增強版,新增了一些功能,比如Request()用來修改Header信息。但是urllib2還去掉了一些好用的方法,比如urlencode()編碼序列中的兩個元素(元組或字典)爲URL查詢字符串。

一般情況下這兩個庫結合着用,那我們也結合着瞭解下。

描述

urllib.urlopen(url, data=None, proxies=None) 讀取指定URL,創建類文件對象。data是隨着URL提交的數據(POST)
urllib/urllib2.quote(s, safe='/')

將字符串中的特殊符號轉十六進制表示。如:

quote('abc def') -> 'abc%20def'

urllib/urllib2.unquote(s) 與quote相反
urllib.urlencode(query, doseq=0) 將序列中的兩個元素(元組或字典)轉換爲URL查詢字符串
urllib.urlretrieve(url, filename=None, reporthook=None, data=None) 將返回結果保存到文件,filename是文件名
urllib2.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False)

一般訪問URL用urllib.urlopen(),如果要修改header信息就會用到這個。

data是隨着URL提交的數據,將會把HTTP請求GET改爲POST。headers是一個字典,包含提交頭的鍵值對應內容。

urllib2.urlopen(url, data=None, timeout=<object object>) timeout 超時時間,單位秒
urllib2.build_opener(*handlers) 構造opener
urllib2.install_opener(opener) 把新構造的opener安裝到默認的opener中,以後urlopen()會自動調用
urllib2.HTTPCookieProcessor(cookiejar=None) Cookie處理器
urllib2.HTTPBasicAuthHandler 認證處理器
urllib2.ProxyHandler 代理處理器

urllib.urlopen()有幾個常用的方法:

方法 描述
getcode() 獲取HTTP狀態碼
geturl() 返回真實URL。有可能URL3xx跳轉,那麼這個將獲得跳轉後的URL
info() 返回服務器返回的header信息。可以通過它的方法獲取相關值
next() 獲取下一行,沒有數據拋出異常
read(size=-1) 默認讀取所有內容。size正整數指定讀取多少字節
readline(size=-1) 默認讀取下一行。size正整數指定讀取多少字節
readlines(sizehint=0) 默認讀取所有內容,以列表形式返回。sizehint正整數指定讀取多少字節

示例:

1)請求URL

1
2
3
4
5
6
7
8
>>> import urllib, urllib2
>>> response = urllib.urlopen("http://www.baidu.com")   # 獲取的網站頁面源碼
>>> response.readline()
'<!DOCTYPE html>\n'
>>> response.getcode()
200
>>> response.geturl()
'http://www.baidu.com'

2)僞裝chrome瀏覽器訪問

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
28
29
30
>>> user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36"
>>> header = {"User-Agent": user_agent}
>>> request = urllib2.Request("http://www.baidu.com", headers=header) # 也可以通過request.add_header('User-Agent', 'Mozilla...')方式添加    
>>> response = urllib2.urlopen(request)
>>> response.geturl()
'https://www.baidu.com/'
>>> print respose.info()  # 查看服務器返回的header信息
Server: bfe/1.0.8.18
Date: Sat, 12 Nov 2016 06:34:54 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Vary: Accept-Encoding
Set-Cookie: BAIDUID=5979A74F742651531360C08F3BE06754:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=5979A74F742651531360C08F3BE06754; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1478932494; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1426_18240_17945_21118_17001_21454_21408_21394_21377_21525_21192; path=/; domain=.baidu.com
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Cache-Control: private
Cxy_all: baidu+a24af77d41154f5fc0d314a73fd4c48f
Expires: Sat, 12 Nov 2016 06:34:17 GMT
X-Powered-By: HPHP
X-UA-Compatible: IE=Edge,chrome=1
Strict-Transport-Security: max-age=604800
BDPAGETYPE: 1
BDQID: 0xf51e0c970000d938
BDUSERID: 0
Set-Cookie: __bsi=12824513216883597638_00_24_N_N_3_0303_C02F_N_N_N_0; expires=Sat, 12-Nov-16 06:34:59 GMT; domain=www.baidu.com; path=/

這裏header只加了一個User-Agent,防止服務器當做爬蟲屏蔽了,有時爲了對付防盜鏈也會加Referer,說明是本站過來的請求。還有跟蹤用戶的cookie。

3)提交用戶表單

1
2
3
>>> post_data = {"loginform-username":"test","loginform-password":"123456"}
>>> response = urllib2.urlopen("http://home.51cto.com/index", data=(urllib.urlencode(post_data)))
>>> response.read() # 登錄後網頁內容

提交用戶名和密碼錶單登錄到51cto網站,鍵是表單元素的id。其中用到了urlencode()方法,上面講過是用於轉爲字典格式爲URL接受的編碼格式。

例如:

1
2
>>> urllib.urlencode(post_data)
'loginform-password=123456&loginform-username=test'

4)保存cookie到變量中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib, urllib2
import cookielib
# 實例化CookieJar對象來保存cookie
cookie = cookielib.CookieJar()
# 創建cookie處理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 通過handler構造opener
opener = urllib2.build_opener(handler)
response = opener.open("http://www.baidu.com")
for item in cookie:
    print item.name, item.value
     
# python test.py
BAIDUID EB4BF619C95630EFD619B99C596744B0:FG=1
BIDUPSID EB4BF619C95630EFD619B99C596744B0
H_PS_PSSID 1437_20795_21099_21455_21408_21395_21377_21526_21190_21306
PSTM 1478936429
BDSVRTM 0
BD_HOME 0

urlopen()本身就是一個opener,無法滿足對Cookie處理,所有就要新構造一個opener。

這裏用到了cookielib庫,cookielib庫是一個可存儲cookie的對象。CookieJar類來捕獲cookie。

cookie存儲在客戶端,用來跟蹤瀏覽器用戶身份的會話技術。

5)保存cookie到文件

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
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib, urllib2
import cookielib
cookie_file = 'cookie.txt'
# 保存cookie到文件
cookie = cookielib.MozillaCookieJar(cookie_file)
# 創建cookie處理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 通過handler構造opener
opener = urllib2.build_opener(handler)
response = opener.open("http://www.baidu.com")
# 保存
cookie.save(ignore_discard=True, ignore_expires=True)  # ignore_discard默認是false,不保存將被丟失的。ignore_expires默認flase,如果cookie存在,則不寫入。
 
# python test.py
# cat cookie.txt
 
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file!  Do not edit.
.baidu.com    TRUE    /    FALSE    3626420835    BAIDUID    687544519EA906BD0DE5AE02FB25A5B3:FG=1
.baidu.com    TRUE    /    FALSE    3626420835    BIDUPSID    687544519EA906BD0DE5AE02FB25A5B3
.baidu.com    TRUE    /    FALSE        H_PS_PSSID    1420_21450_21097_18560_21455_21408_21395_21377_21526_21192_20927
.baidu.com    TRUE    /    FALSE    3626420835    PSTM    1478937189
www.baidu.com    FALSE    /    FALSE        BDSVRTM    0
www.baidu.com    FALSE    /    FALSE        BD_HOME    0

MozillaCookieJar()這個類用來保存cookie到文件。

6)使用cookie訪問URL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib2
import cookielib
# 實例化對象
cookie = cookielib.MozillaCookieJar()
# 從文件中讀取cookie
cookie.load("cookie.txt", ignore_discard=True, ignore_expires=True)
# 創建cookie處理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 通過handler構造opener
opener = urllib2.build_opener(handler)
# request = urllib2.Request("http://www.baidu.com")
response = opener.open("http://www.baidu.com")

7)使用代理服務器訪問URL

1
2
3
4
5
6
import urllib2
proxy_address = {"http""http://218.17.252.34:3128"}
handler = urllib2.ProxyHandler(proxy_address)
opener = urllib2.build_opener(handler)
response = opener.open("http://www.baidu.com")
print response.read()

8)URL訪問認證

1
2
3
4
5
6
import urllib2
auth = urllib2.HTTPBasicAuthHandler()
# (realm, uri, user, passwd)
auth.add_password(None'http://www.example.com','user','123456')
opener = urllib2.build_opener(auth)
response = opener.open('http://www.example.com/test.html')

10.14 json

JSON是一種輕量級數據交換格式,一般API返回的數據大多是JSON、XML,如果返回JSON的話,將獲取的數據轉換成字典,方面在程序中處理。

json庫經常用的有兩種方法dumps和loads():

# 將字典轉換爲JSON字符串

>>> dict = {'user':[{'user1': 123}, {'user2': 456}]}

>>> type(dict)

<type 'dict'>

>>> json_str = json.dumps(dict)

>>> type(json_str)

<type 'str'>

# 把JSON字符串轉換爲字典

>>> d = json.loads(json_str)

>>> type(d)

<type 'dict'>

 JSON與Python解碼後數據類型:

JSON

Python

object dict
array list
string unicode
number(int) init,long
number(real) float
true Ture
false False
null None

10.15 time

這個time庫提供了各種操作時間值。

方法

描述

示例

time.asctime([tuple]) 將一個時間元組轉換成一個可讀的24個時間字符串

>>> time.asctime(time.localtime())

'Sat Nov 12 01:19:00 2016'

time.ctime(seconds) 字符串類型返回當前時間

>>> time.ctime()

'Sat Nov 12 01:19:32 2016'

time.localtime([seconds])

默認將當前時間轉換成一個(struct_timetm_year,tm_mon,tm_mday,tm_hour,tm_min,

                              tm_sec,tm_wday,tm_yday,tm_isdst)

>>> time.localtime()

time.struct_time(tm_year=2016, tm_mon=11, tm_mday=12, tm_hour=1, tm_min=19, tm_sec=56, tm_wday=5, tm_yday=317, tm_isdst=0)

time.mktime(tuple) 將一個struct_time轉換成時間戳

>>> time.mktime(time.localtime())

1478942416.0

time.sleep(seconds) 延遲執行給定的秒數 >>> time.sleep(1.5)
time.strftime(format[, tuple]) 將元組時間轉換成指定格式。[tuple]不指定默認以當前時間

>>> time.strftime('%Y-%m-%d %H:%M:%S')

'2016-11-12 01:20:54'

time.time() 返回當前時間時間戳

>>> time.time()

1478942466.45977

strftime():

指令

描述

%a 簡化星期名稱,如Sat
%A 完整星期名稱,如Saturday
%b 簡化月份名稱,如Nov
%B 完整月份名稱,如November
%c 當前時區日期和時間
%d
%H 24小時制小時數(0-23)
%I 12小時制小時數(01-12)
%j 365天中第多少天
%m
%M 分鐘
%p AM或PM,AM表示上午,PM表示下午
%S
%U 一年中第幾個星期
%w 星期幾
%W 一年中第幾個星期
%x 本地日期,如'11/12/16'
%X 本地時間,如'17:46:20'
%y 簡寫年名稱,如16
%Y 完整年名稱,如2016
%Z 當前時區名稱(PST:太平洋標準時間)
%% 代表一個%號本身

10.16 datetime

datetime庫提供了以下幾個類:

描述

datetime.date() 日期,年月日組成
datetime.datetime() 包括日期和時間
datetime.time() 時間,時分秒及微秒組成
datetime.timedelta() 時間間隔
datetime.tzinfo()

datetime.date()類:

方法

描述

描述

date.max 對象所能表示的最大日期 datetime.date(9999, 12, 31)
date.min 對象所能表示的最小日期 datetime.date(1, 1, 1)
date.strftime() 根據datetime自定義時間格式

>>> date.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')

'2016-11-12 07:24:15

date.today() 返回當前系統日期

>>> date.today()

datetime.date(2016, 11, 12)

date.isoformat() 返回ISO 8601格式時間(YYYY-MM-DD)

>>> date.isoformat(date.today())

'2016-11-12'

date.fromtimestamp() 根據時間戳返回日期

>>> date.fromtimestamp(time.time())

datetime.date(2016, 11, 12)

date.weekday() 根據日期返回星期幾,週一是0,以此類推

>>> date.weekday(date.today())

5

date.isoweekday() 根據日期返回星期幾,週一是1,以此類推

>>> date.isoweekday(date.today())

6

date.isocalendar() 根據日期返回日曆(年,第幾周,星期幾)

>>> date.isocalendar(date.today())

(2016, 45, 6)

datetime.datetime()類:

方法

描述

示例

datetime.now()/datetime.today() 獲取當前系統時間

>>> datetime.now()

datetime.datetime(2016, 11, 12, 7, 39, 35, 106385)

date.isoformat() 返回ISO 8601格式時間

>>> datetime.isoformat(datetime.now())

'2016-11-12T07:42:14.250440'

datetime.date() 返回時間日期對象,年月日

>>> datetime.date(datetime.now())

datetime.date(2016, 11, 12)

datetime.time() 返回時間對象,時分秒

>>> datetime.time(datetime.now())                   

datetime.time(7, 46, 2, 594397) 

datetime.utcnow() UTC時間,比中國時間快8個小時

>>> datetime.utcnow()

datetime.datetime(2016, 11, 12, 15, 47, 53, 514210)

datetime.time()類:

方法

描述

示例

time.max 所能表示的最大時間

>>> time.max

datetime.time(23, 59, 59, 999999)

time.min 所能表示的最小時間

>>> time.min

datetime.time(0, 0)

time.resolution 時間最小單位,1微妙

>>> time.resolution

datetime.timedelta(0, 0, 1)

datetime.timedelta()類:

1
2
3
4
5
6
7
8
9
10
# 獲取昨天日期
>>> date.today() - timedelta(days=1)         
datetime.date(20161111)
>>> date.isoformat(date.today() - timedelta(days=1))
'2016-11-11'
# 獲取明天日期
>>> date.today() + timedelta(days=1)               
datetime.date(20161113)
>>> date.isoformat(date.today() + timedelta(days=1))
'2016-11-13'
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章