Python爬蟲技術--基礎篇--模組
模組概述
在計算機程式的開發過程中,隨著程式程式碼越寫越多,在一個檔案裡程式碼就會越來越長,越來越不容易維護。
為了編寫可維護的程式碼,我們把很多函式分組,分別放到不同的檔案裡,這樣,每個檔案包含的程式碼就相對較少,很多程式語言都採用這種組織程式碼的方式。在Python中,一個.py檔案就稱之為一個模組(Module)。
使用模組有什麼好處?
最大的好處是大大提高了程式碼的可維護性。其次,編寫程式碼不必從零開始。當一個模組編寫完畢,就可以被其他地方引用。我們在編寫程式的時候,也經常引用其他模組,包括Python內建的模組和來自第三方的模組。
使用模組還可以避免函式名和變數名衝突。相同名字的函式和變數完全可以分別存在不同的模組中,因此,我們自己在編寫模組時,不必考慮名字會與其他模組衝突。但是也要注意,儘量不要與內建函式名字衝突。點
你也許還想到,如果不同的人編寫的模組名相同怎麼辦?為了避免模組名衝突,Python又引入了按目錄來組織模組的方法,稱為包(Package)。
舉個例子,一個abc.py
的檔案就是一個名字叫abc
的模組,一個xyz.py
的檔案就是一個名字叫xyz
的模組。
現在,假設我們的abc
和xyz
這兩個模組名字與其他模組衝突了,於是我們可以通過包來組織模組,避免衝突。方法是選擇一個頂層包名,比如mycompany
,按照如下目錄存放:
mycompany ├─ __init__.py ├─ abc.py └─ xyz.py
引入了包以後,只要頂層的包名不與別人衝突,那所有模組都不會與別人衝突。現在,abc.py
模組的名字就變成了mycompany.abc
,類似的,xyz.py
的模組名變成了mycompany.xyz
。
請注意,每一個包目錄下面都會有一個__init__.py
的檔案,這個檔案是必須存在的,否則,Python就把這個目錄當成普通目錄,而不是一個包。__init__.py
可以是空檔案,也可以有Python程式碼,因為__init__.py
本身就是一個模組,而它的模組名就是mycompany
。
類似的,可以有多級目錄,組成多級層次的包結構。比如如下的目錄結構:
mycompany ├─ web │ ├─ __init__.py │ ├─ utils.py │ └─ www.py ├─ __init__.py ├─ abc.py └─ utils.py
檔案www.py
的模組名就是mycompany.web.www
,兩個檔案utils.py
的模組名分別是mycompany.utils
和mycompany.web.utils
。
mycompany.web
也是一個模組,請指出該模組對應的.py檔案。
總結
模組是一組Python程式碼的集合,可以使用其他模組,也可以被其他模組使用。
建立自己的模組時,要注意:
模組名要遵循Python變數命名規範,不要使用中文、特殊字元;
模組名不要和系統模組名衝突,最好先檢視系統是否已存在該模組,檢查方法是在Python互動環境執行import abc
,若成功則說明系統存在此模組。
1.使用模組
Python本身就內建了很多非常有用的模組,只要安裝完畢,這些模組就可以立刻使用。
我們以內建的sys
模組為例,編寫一個hello
的模組:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
第1行和第2行是標準註釋,第1行註釋可以讓這個hello.py
檔案直接在Unix/Linux/Mac上執行,第2行註釋表示.py檔案本身使用標準UTF-8編碼;
第4行是一個字串,表示模組的文件註釋,任何模組程式碼的第一個字串都被視為模組的文件註釋;
第6行使用__author__
變數把作者寫進去,這樣當你公開原始碼後別人就可以瞻仰你的大名;
以上就是Python模組的標準檔案模板,當然也可以全部刪掉不寫,但是,按標準辦事肯定沒錯。
後面開始就是真正的程式碼部分。
你可能注意到了,使用sys
模組的第一步,就是匯入該模組:
import sys
匯入sys
模組後,我們就有了變數sys
指向該模組,利用sys
這個變數,就可以訪問sys
模組的所有功能。
sys
模組有一個argv
變數,用list儲存了命令列的所有引數。argv
至少有一個元素,因為第一個引數永遠是該.py檔案的名稱,例如:
執行python3 hello.py
獲得的sys.argv
就是['hello.py']
;
執行python3 hello.py Michael
獲得的sys.argv
就是['hello.py', 'Michael']
。
最後,注意到這兩行程式碼:
if __name__=='__main__':
test()
當我們在命令列執行hello
模組檔案時,Python直譯器把一個特殊變數__name__
置為__main__
,而如果在其他地方匯入該hello
模組時,if
判斷將失敗,因此,這種if
測試可以讓一個模組通過命令列執行時執行一些額外的程式碼,最常見的就是執行測試。
我們可以用命令列執行hello.py
看看效果:
$ python3 hello.py
Hello, world!
$ python hello.py Michael
Hello, Michael!
如果啟動Python互動環境,再匯入hello
模組:
$ python3
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>>
匯入時,沒有列印Hello, word!
,因為沒有執行test()
函式。
呼叫hello.test()
時,才能打印出Hello, word!
:
>>> hello.test()
Hello, world!
作用域
在一個模組中,我們可能會定義很多函式和變數,但有的函式和變數我們希望給別人使用,有的函式和變數我們希望僅僅在模組內部使用。在Python中,是通過_
字首來實現的。
正常的函式和變數名是公開的(public),可以被直接引用,比如:abc
,x123
,PI
等;
類似__xxx__
這樣的變數是特殊變數,可以被直接引用,但是有特殊用途,比如上面的__author__
,__name__
就是特殊變數,hello
模組定義的文件註釋也可以用特殊變數__doc__
訪問,我們自己的變數一般不要用這種變數名;
類似_xxx
和__xxx
這樣的函式或變數就是非公開的(private),不應該被直接引用,比如_abc
,__abc
等;
之所以我們說,private函式和變數“不應該”被直接引用,而不是“不能”被直接引用,是因為Python並沒有一種方法可以完全限制訪問private函式或變數,但是,從程式設計習慣上不應該引用private函式或變數。
private函式或變數不應該被別人引用,那它們有什麼用呢?請看例子:
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
我們在模組裡公開greeting()
函式,而把內部邏輯用private函式隱藏起來了,這樣,呼叫greeting()
函式不用關心內部的private函式細節,這也是一種非常有用的程式碼封裝和抽象的方法,即:
外部不需要引用的函式全部定義成private,只有外部需要引用的函式才定義為public。
2.安裝第三方模組
在Python中,安裝第三方模組,是通過包管理工具pip完成的。
如果你正在使用Mac或Linux,安裝pip本身這個步驟就可以跳過了。
如果你正在使用Windows,請參考安裝Python一節的內容,確保安裝時勾選了pip
和Add python.exe to Path
。
在命令提示符視窗下嘗試執行pip
,如果Windows提示未找到命令,可以重新執行安裝程式新增pip
。
注意:Mac或Linux上有可能並存Python 3.x和Python 2.x,因此對應的pip命令是pip3
。
例如,我們要安裝一個第三方庫——Python Imaging Library,這是Python下非常強大的處理影象的工具庫。不過,PIL目前只支援到Python 2.7,並且有年頭沒有更新了,因此,基於PIL的Pillow專案開發非常活躍,並且支援最新的Python 3。
一般來說,第三方庫都會在Python官方的pypi.python.org網站註冊,要安裝一個第三方庫,必須先知道該庫的名稱,可以在官網或者pypi上搜索,比如Pillow的名稱叫Pillow,因此,安裝Pillow的命令就是:
pip install Pillow
耐心等待下載並安裝後,就可以使用Pillow了。
安裝常用模組
在使用Python時,我們經常需要用到很多第三方庫,例如,上面提到的Pillow,以及MySQL驅動程式,Web框架Flask,科學計算Numpy等。用pip一個一個安裝費時費力,還需要考慮相容性。我們推薦直接使用Anaconda,這是一個基於Python的資料處理和科學計算平臺,它已經內建了許多非常有用的第三方庫,我們裝上Anaconda,就相當於把數十個第三方模組自動安裝好了,非常簡單易用。
可以從Anaconda官網下載GUI安裝包,安裝包有500~600M,所以需要耐心等待下載。下載後直接安裝,Anaconda會把系統Path中的python指向自己自帶的Python,並且,Anaconda安裝的第三方模組會安裝在Anaconda自己的路徑下,不影響系統已安裝的Python目錄。
安裝好Anaconda後,重新開啟命令列視窗,輸入python,可以看到Anaconda的資訊:
┌────────────────────────────────────────────────────────┐
│Command Prompt - python - □ x │
├────────────────────────────────────────────────────────┤
│Microsoft Windows [Version 10.0.0] │
│(c) 2015 Microsoft Corporation. All rights reserved. │
│ │
│C:\> python │
│Python 3.6.3 |Anaconda, Inc.| ... on win32 │
│Type "help", ... for more information. │
│>>> import numpy │
│>>> _ │
│ │
│ │
│ │
└────────────────────────────────────────────────────────┘
可以嘗試直接import numpy
等已安裝的第三方模組。
模組搜尋路徑
當我們試圖載入一個模組時,Python會在指定的路徑下搜尋對應的.py檔案,如果找不到,就會報錯:
>>> import mymodule
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named mymodule
預設情況下,Python直譯器會搜尋當前目錄、所有已安裝的內建模組和第三方模組,搜尋路徑存放在sys
模組的path
變數中:
>>> import sys
>>> sys.path
['', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', ..., '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']
如果我們要新增自己的搜尋目錄,有兩種方法:
一是直接修改sys.path
,新增要搜尋的目錄:
>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')
這種方法是在執行時修改,執行結束後失效。
第二種方法是設定環境變數PYTHONPATH
,該環境變數的內容會被自動新增到模組搜尋路徑中。設定方式與設定Path環境變數類似。注意只需要新增你自己的搜尋路徑,Python自己本身的搜尋路徑不受影響。
Python本身就內建了很多非常有用的模組,只要安裝完畢,這些模組就可以立刻使用。
我們以內建的sys
模組為例,編寫一個hello
的模組:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
第1行和第2行是標準註釋,第1行註釋可以讓這個hello.py
檔案直接在Unix/Linux/Mac上執行,第2行註釋表示.py檔案本身使用標準UTF-8編碼;
第4行是一個字串,表示模組的文件註釋,任何模組程式碼的第一個字串都被視為模組的文件註釋;
第6行使用__author__
變數把作者寫進去,這樣當你公開原始碼後別人就可以瞻仰你的大名;
以上就是Python模組的標準檔案模板,當然也可以全部刪掉不寫,但是,按標準辦事肯定沒錯。
後面開始就是真正的程式碼部分。
你可能注意到了,使用sys
模組的第一步,就是匯入該模組:
import sys
匯入sys
模組後,我們就有了變數sys
指向該模組,利用sys
這個變數,就可以訪問sys
模組的所有功能。
sys
模組有一個argv
變數,用list儲存了命令列的所有引數。argv
至少有一個元素,因為第一個引數永遠是該.py檔案的名稱,例如:
執行python3 hello.py
獲得的sys.argv
就是['hello.py']
;
執行python3 hello.py Michael
獲得的sys.argv
就是['hello.py', 'Michael']
。
最後,注意到這兩行程式碼:
if __name__=='__main__':
test()
當我們在命令列執行hello
模組檔案時,Python直譯器把一個特殊變數__name__
置為__main__
,而如果在其他地方匯入該hello
模組時,if
判斷將失敗,因此,這種if
測試可以讓一個模組通過命令列執行時執行一些額外的程式碼,最常見的就是執行測試。
我們可以用命令列執行hello.py
看看效果:
$ python3 hello.py
Hello, world!
$ python hello.py Michael
Hello, Michael!
如果啟動Python互動環境,再匯入hello
模組:
$ python3
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>>
匯入時,沒有列印Hello, word!
,因為沒有執行test()
函式。
呼叫hello.test()
時,才能打印出Hello, word!
:
>>> hello.test()
Hello, world!
作用域
在一個模組中,我們可能會定義很多函式和變數,但有的函式和變數我們希望給別人使用,有的函式和變數我們希望僅僅在模組內部使用。在Python中,是通過_
字首來實現的。
正常的函式和變數名是公開的(public),可以被直接引用,比如:abc
,x123
,PI
等;
類似__xxx__
這樣的變數是特殊變數,可以被直接引用,但是有特殊用途,比如上面的__author__
,__name__
就是特殊變數,hello
模組定義的文件註釋也可以用特殊變數__doc__
訪問,我們自己的變數一般不要用這種變數名;
類似_xxx
和__xxx
這樣的函式或變數就是非公開的(private),不應該被直接引用,比如_abc
,__abc
等;
之所以我們說,private函式和變數“不應該”被直接引用,而不是“不能”被直接引用,是因為Python並沒有一種方法可以完全限制訪問private函式或變數,但是,從程式設計習慣上不應該引用private函式或變數。
private函式或變數不應該被別人引用,那它們有什麼用呢?請看例子:
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
我們在模組裡公開greeting()
函式,而把內部邏輯用private函式隱藏起來了,這樣,呼叫greeting()
函式不用關心內部的private函式細節,這也是一種非常有用的程式碼封裝和抽象的方法,即:
外部不需要引用的函式全部定義成private,只有外部需要引用的函式才定義為public。