python 基礎知識點整理 和詳細應用

Python教程


Python是一種簡單易學,功能強大的編程語言。它包含了高效的高級數據結構和簡單而有效的方法,面向對象編程。Python優雅的語法,動態類型,以及它自然的解釋能力,使其成爲理想的語言,腳本和應用程序高速開發在大多數平臺上的不少領域。html

Python解釋器及其擴展標準庫的源代碼和編譯版本號可以從Python的Web網站,http://www.python.org/所有主要平臺可自由查看,並且可以自由公佈。該網站上也包括了分配和指針到很是多免費的第三方Python模塊,程序,工具,以及附加的文檔。python

Python的解釋器很是easy擴展新的功能,並在C或C ++(或由C來調用其它語言)實現的數據類型。 Python也很是適於做爲定製應用的一種擴展語言。linux

本教程向讀者介紹了非正式的Python語言和系統的基本概念和功能。它有助於理解Python和實戰練習,固然所有的樣例都是自包括的,因此這本手冊可以離線閱讀爲好。web

有關標準對象和模塊的具體介紹,請參見Python標準庫。Python語言參考給出了語言的更正式的定義。需要編寫C或C + +擴展,請閱讀擴展和嵌入Python解釋器和Python/C的API參考手冊。也有幾本書涵蓋了各個深度的Python。算法

本教程並不試圖全面,涵蓋每一個功能,甚至每一個常用功能。相反,它介紹了不少Python中最引人注目的功能,會給Python語言的韻味和風格是一個好開始。看完以後,你就可以閱讀和編寫Python模塊和程序,將準備進一步瞭解Python標準庫描寫敘述的各類Python庫模塊shell

Python概述

Python是一種高層次的,解釋性的,交互式和麪向對象的腳本語言。Python被設計成具備很是強的可讀性,它使用英語如其它語言常用空白做爲標點符號,它比其它語言語法結構更少。數據庫

  • Python被解析:這意味着它是在執行時由解釋器處理,你並不需要在執行前編譯程序。這相似於Perl和PHP。express

  • Python是互動:這意味着你可以在Python的提示和解釋器進行交互,直接寫出你的程序。編程

  • Python是面向對象的:這意味着Python支持面向對象的方式或程序,它封裝了對象中的代碼的技術。數組

  • Python是剛開始學習的人的語言:Python是爲0基礎程序猿一種偉大的語言,並支持普遍的應用,從簡單的文本處理,WWW瀏覽器,以遊戲開發。

Python的歷史:

Python是由Guido van Rossum在八十年代末和九十年代初在全國研究所數學與計算機科學在荷蘭開發。

Python從更多語言,包含ABC,Modula-3語言,C語言,C+ +,Algol-68,Smalltalk和unix的shell等腳本語言獲得參考開發。

Python是有版權的。比方Perl,Python源碼現在是GNU通用公共許可證(GPL)下提供。

Python的現在是由一個核心開發團隊在維護,儘管Guido van Rossum仍然持有在指導其進展相當關鍵的數據。

Python的特色:

Python的功能亮點包含:

  • 易於學習:Python有相對較少的keyword,結構簡單,明白的語法。這讓學生學習的時間相對較短。

  • 易於閱讀:Python代碼是更加明白,可見。

  • 易於維護:Python的成功在於它的源碼是至關easy維護。

  • 普遍的標準庫:Python的最大長處是體積庫很是方便,在UNIX,Windows和Macintosh跨平臺兼容。

  • 交互模式:支持交互模式中,可以從終端輸入結果正確的語言,讓交互測試的代碼片斷和調試。

  • 便攜式:Python可以在多種硬件平臺上執行,並且對所有的平臺上使用一樣的接口。

  • 擴展:可以加入低級別的模塊在Python解釋器。這些模塊使程序猿可以加入或本身定義本身的工具來提升效率。

  • 數據庫:Python提供接口給所有基本的商業數據庫。

  • GUI編程:Python支持,可以建立並移植到不少系統調用,庫和Windows系統,如Windows MFC,Macintosh和Unix的X Window系統的GUI應用程序。

  • 可擴展性:Python提供了一個更好的結構,並支持比shell腳本大型程序。

除了上面提到的功能,Python也有很是好的功能,幾個列舉例如如下:

  • 支持功能和結構化的編程方法,以及面向對象。

  • 它可以做爲一種腳本語言,或者可以被編譯爲字節碼創建大型的應用程序。

  • 很高的動態數據類型,並且支持動態類型檢查。

  • 支持本身主動垃圾收集。

  • 它可以用C,C + +,COM和ActiveX,CORBA和Java很是easy地集成。

Python環境安裝

本地環境設置

假設願意設置您的Python環境,讓咱們瞭解怎樣創建Python環境。 Python可在各類平臺,包含Linux和Mac OS X,可嘗試打開一個終端窗體並輸入「python」,以檢查是否已經安裝了python,什麼版本號,假設已經有安裝。

  • Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, etc.)

  • Win 9x/NT/2000

  • Macintosh (Intel, PPC, 68K)

  • OS/2

  • DOS (multiple versions)

  • PalmOS

  • Nokia 手機

  • Windows CE

  • Acorn/RISC OS

  • BeOS

  • Amiga

  • VMS/OpenVMS

  • QNX

  • VxWorks

  • Psion

  • Python也可被移植到Java和.NET 虛擬機

得到Python

最新源碼,二進制文件,文檔,新聞等可在Python的官方站點:

Python官方站點:http://www.python.org/

可以從下面網站下載Python文檔。文件格式是HTML,PDF和PostScript。

Python文檔站點: www.python.org/doc/

安裝Python:

Python發行版適用於各類平臺。你僅僅需要下載適用於您的平臺的二進制代碼並安裝Python。

假設二進制代碼針對您的平臺沒法使用,你需要一個C編譯器來手動編譯源碼。編譯源碼提供了選擇,爲安裝功能方面更大的靈活性。

這裏是在各類平臺上安裝Python的高速概覽:

UNIX和Linux的安裝方式:

如下是簡單的步驟,在Unix/ Linux機器上安裝Python。

  • 打開Web瀏覽器並轉至http://www.python.org/download/

  • 依照連接下載壓縮的源碼在Unix/ Linux操做系統。

  • 下載並解壓文件。

  • 編輯模塊/安裝文件,假設你想本身定義一些選項。

  • 運行./configure 腳本

  • make

  • make install

這將安裝python的標準位置在 /usr/local/bin文件夾和它的庫安裝在/usr/local/lib/pythonXX,當中XX是Python使用的版本號。

Windows上安裝:

如下是Windows機器上安裝Python的步驟。

  • 打開Web瀏覽器並轉至 http://www.python.org/download/

  • 依照連接到Windows安裝python-XYZ.msi文件,當中XYZ是你要安裝的版本號。

  • 要使用此安裝程序python-XYZ.msi,Windows系統必須支持Microsoft安裝程序2.0。僅僅需安裝程序文件保存到本地計算機,而後執行它,看看是否你的機器支持MSI。

  • 經過雙擊它在Windows中執行下載的文件。這將出Python的安裝嚮導,這些都很是easy使用。僅僅需接受默認設置,等到安裝完畢後。

Macintosh上安裝:

最新的Mac電腦配備安裝了Python,但可能好幾年前的機器沒有安裝。見http://www.python.org/download/mac/上得到的最新版本號以及額外的工具來支持在Mac上開發的指令。對於老的Mac OS的Mac OS X10.3以前(2003年推出),MacPython上是可用的。「

僅僅要到這個連接,完整Mac OS安裝安裝細節。

設置PATH:

程序和其它可運行文件可以住在不少文件夾,因此操做系統提供,列出文件夾的操做系統搜索可運行文件的搜索路徑。

路徑被存儲在環境變量,這是由操做系統維護的命名字符串。這些變量包括可用於命令行解釋器和其它程序的信息。

路徑變量名爲Path的Unix或路徑在Windows(UNIX是區分大寫和小寫的,Windows是沒有)。

在Mac OS中,安裝程序處理的道路細節。調用不論什麼特定文件夾Python解釋器,必須Python的文件夾加入到您的路徑。

設置路徑,在Unix/Linux上:

將Python文件夾加入到在Unix系統中的特定會話的路徑:

  • 在csh shell: 輸入
    SETENV PATH "$PATH:/usr/local/bin/python"  而後按回車鍵。

  • 在 bash shell (Linux): 輸入
    export PATH="$PATH:/usr/local/bin/python" 而後按回車鍵。

  • 在 sh 或  ksh shell: 輸入 
    PATH="$PATH:/usr/local/bin/python" 而後按回車鍵。

注: /usr/local/bin/python 爲Python文件夾的路徑

設置路徑Windows系統:

以Python文件夾加入到了 Windows 特定會話的路徑:

  • 在命令提示符下: 輸入 
    path %path%;C:\Python 而後按Enter鍵。

注意:C:\Python 是Python文件夾的路徑

Python環境變量:

這裏是重要的環境變量,其可以被Python確認:

變量 描寫敘述
PYTHONPATH 有相似路徑的做用。這個變量告訴Python解釋器在哪裏可以找到導入到程序中的模塊文件。 PYTHONPATH應包括Python源碼庫文件夾,包括Python源碼的文件夾。 PYTHONPATH是由Python安裝程序有時會預設。
PYTHONSTARTUP 包括了在每次啓動的解釋器(相似於Unix.profile或.login文件)時運行Python源碼的初始化文件的路徑。這個文件一般命名爲.pythonrc.py。在Unix中,一般包括載入有用程序或改動PYTHONPATH命令。
PYTHONCASEOK 在Windows中使用,以指示Python找到一個import語句,第一個不區分大寫和小寫的匹配。將此變量設置爲隨意值來激活它。
PYTHONHOME 備選模塊搜索路徑。它一般嵌入在PYTHONSTARTUP或PYTHONPATH文件夾,以使交換模塊庫的簡單。

執行Python:

有三種不一樣的方式來啓動Python:

(1) 交互式解釋器:

可以輸入python,並在開始經過命令行啓動在交互式解釋器它編碼的時候。從UNIX,DOS或其它系統提供了一個命令行解釋器或shell窗體。

$python             # Unix/Linux

or 

python%             # Unix/Linux

or 

C:>python           # Windows/DOS

如下是所有可用的命令行選項的列表:

選項 描寫敘述
-d 提供調試輸出
-O 生成優化代碼(結果爲.pyo文件)
-S 不執行導入站點,在啓動時查找Python路徑
-v 具體輸出(在導入語句具體的跟蹤)
-X 禁止基於類內置異常(僅僅使用字符串);開始1.6版本號過期
-c cmd 做爲cmd 字符串執行Python腳本發送
file 從給定的文件執行Python腳本

(2) 腳本的命令行:

Python腳本可以在命令行中經過調用應用程序中的解釋,如如下的運行:

$python  script.py          # Unix/Linux

or 

python% script.py           # Unix/Linux

or 

C:>python script.py         # Windows/DOS

注意:請確保該文件的權限模式可以運行。

(3)集成開發環境

您可以從圖形用戶界面(GUI)環境中執行Python。所有需要的是一個支持Python系統的GUI應用程序。

  • UNIX:IDLE也是早期的UNIX系統爲Python的IDE。

  • Windows:PythonWin是第一個Windows界面的Python和一個GUI的IDE。

  • Macintosh:Python的的Macintosh版本號隨着閒置的IDE可從主站下載,不是MACBINARY就是BinHex'd文件。

在繼續到下一個章節前,請確保您的環境已正確設置及全然正常工做。假設不能夠創建正常的環境,那麼能夠又一次安裝配置。 
 
所有在之後的章節中給出的樣例已經運行了可在Linux CentOS 上的 Python2.7.3版本號。

Python基本的語法

Python與Perl,C和Java語言等有不少類似之處。只是,也有語言之間有一些明白的差異。本章的目的是讓你迅速學習Python的語法。

第一個Python程序:

交互模式編程:

調用解釋器不通過腳本文件做爲參數,顯示下面提示:

$ python
Python 2.6.4 (#1, Nov 11 2014, 13:34:43)
[GCC 4.1.2 20120704 (Red Hat 5.6.2-48)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

鍵入下列文字在Python提示符,而後按Enter鍵:

>>> print "Hello, Python!";

假設您執行的是新的Python版本號,那麼需要使用打印語句括號像print ("Hello, Python!");。但是在Python版本號2.6.4,這將產生下面結果:

Hello, Python!

腳本模式編程:

調用解釋器及腳本做爲參數,並開始運行的腳本,並一直持續到腳本完畢。當腳本完畢時,解釋器再也不是活動的。

讓咱們在腳本中編寫一個簡單的Python程序。所有的Python文件將具備.py擴展。因此,把如下的代碼寫在一個test.py文件。

print "Hello, Python!";

在這裏,我若是你已經在PATH變量中設置Python解釋器。現在,嘗試例如如下執行這個程序:

$ python test.py

這將產生下面結果:

Hello, Python!

讓咱們嘗試還有一種方式來運行Python腳本。如下是改動後的test.py文件:

#!/usr/bin/python

print "Hello, Python!";

在這裏,若是Python解釋器在/usr/bin文件夾中可用。現在,嘗試例如如下執行這個程序:

$ chmod +x test.py     # This is to make file executable
$./test.py

這將產生下面結果:

Hello, Python!

Python標識符:

Python標識符是用來標識一個變量,函數,類,模塊或其它對象的名稱。一個標識符開始以字母A到Z或a〜z或後跟零個或多個字母下劃線(_),下劃線和數字(0〜9)。

Python中標識符內不一樣意標點符號,如@,$和%。 Python是一種區分大寫和小寫的編程語言。所以,Manpower 和manpower在Python中是兩個不一樣的標識符。

這裏有Python標識符命名約定:

  • 類名以大寫字母以及所有其餘標識符以小寫字母。

  • 開頭單個前導下劃線的標識符表示由該標識符約定意思是私有的。

  • 開頭兩個前導下劃線的標識符表示一個強烈的私有的標識符。

  • 假設標識符末尾還具備兩個下劃線結束時,該標識符是一個語言定義的特殊名稱。

保留字:

如下列出了在Python中的保留字。這些保留字不可以被用做常量或變量,或不論什麼其餘標識符。所有Pythonkeyword僅僅包括小寫字母。

and exec not
assert finally or
break for pass
class from print
continue global raise
def if return
del import try
elif in while
else is with
except lambda yield

行和縮進:

一個程序猿學習Python時,遇到的第一個需要注意的地方是,不使用括號來表示代碼的類和函數定義塊或流程控制。代碼塊是由行縮進,這是嚴格運行表示方式。

在縮進位的數目是可變的,但是在塊中的所有語句必須縮進一樣的量。在這個樣例中,兩個功能塊都很是好使用:

if True:
    print "True"
else:
  print "False"

然而,在本實施例中的第二塊將產生一個錯誤:

if True:
    print "Answer"
    print "True"
else:
    print "Answer"
  print "False"

所以,在Python中所有的連續線縮進的空格數相同的會結成塊。下面是各類語句塊中的樣例:

注意:不要試圖理解所使用的邏輯或不一樣的功能。僅僅要肯定你明確,即便他們各類模塊無需括號。

#!/usr/bin/python

import sys

try:
  # open file stream
  file = open(file_name, "w")
except IOError:
  print "There was an error writing to", file_name
  sys.exit()
print "Enter '", file_finish,
print "' When finished"
while file_text != file_finish:
  file_text = raw_input("Enter text: ")
  if file_text == file_finish:
    # close the file
    file.close
    break
  file.write(file_text)
  file.write("\n")
file.close()
file_name = raw_input("Enter filename: ")
if len(file_name) == 0:
  print "Next time please enter something"
  sys.exit()
try:
  file = open(file_name, "r")
except IOError:
  print "There was an error reading file"
  sys.exit()
file_text = file.read()
file.close()
print file_text

多行語句:

Python語句通常用一個新行結束。 但是,Python贊成使用續行字符(\)來表示,該行應該繼續下去(跨行)。好比:

total = item_one + \
        item_two + \
        item_three

包括在[],{}或()括號內的陳述並不需要使用續行符。好比:

days = ['Monday', 'Tuesday', 'Wednesday',
        'Thursday', 'Friday']

Python引號:

Python接受單引號('),雙引號(「)和三(''或」「」)引用,以表示字符串常量,僅僅要是同一類型的引號開始和結束的字符串。

三重引號可以用於跨越多個行的字符串。好比,所有下列是合法的:

word = 'word'
sentence = "This is a sentence."
paragraph = """This is a paragraph. It is
made up of multiple lines and sentences."""

Python凝視:

一個井號(#),這不是一個字符串文字開頭的凝視。「#」號以後字符和到物理行是凝視的一部分,Python解釋器會忽略它們。

#!/usr/bin/python

# First comment
print "Hello, Python!";  # second comment

這將產生下面結果:

Hello, Python!

凝視可能會在聲明中表達或同一行以後:

name = "Madisetti" # This is again comment

你可以使用多行凝視例如如下:

# This is a comment.
# This is a comment, too.
# This is a comment, too.
# I said that already.

使用空行:

一行僅僅含有空格,可能帶有凝視,假設是空行那麼Python全然忽略它。

在交互式解釋器會話中,必須輸入一個空的物理行終止多行語句。

等待用戶:

程序的如下一行顯示的提示,按回車鍵退出,等待用戶按下回車鍵:

#!/usr/bin/python

raw_input("\n\nPress the enter key to exit.")

在這裏,「\n\n已」被用來顯示實際行以前建立兩個換行。一旦用戶按下鍵時,程序結束。這是一個很是好的技巧,保持一個控制檯窗體打開,直到用戶完畢應用程序執行。

在一行中多個語句:

分號( ; ) 贊成在單行寫入多條語句,不管語句是否啓動一個新的代碼塊。如下是使用分號演示樣例:

import sys; x = 'foo'; sys.stdout.write(x + '\n')

多個語句組做爲套件:

一組單獨的語句,在Python單一的代碼塊被稱爲序列。複雜的語句,如if, while, def, and class,那些需要一個標題行和套件。

標題行開始的聲明(與keyword),並終止與冒號(:)),接着是一個或多個線構成該套件。好比:

if expression : 
   suite
elif expression : 
   suite 
else : 
   suite

命令行參數:

咱們可能已經看到了,比方,很是多程序可以執行,它們提供有關怎樣執行的一些基本信息。 Python中可以使用 -h 作到這一點:

$ python -h
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-c cmd : program passed in as string (terminates option list)
-d     : debug output from parser (also PYTHONDEBUG=x)
-E     : ignore environment variables (such as PYTHONPATH)
-h     : print this help message and exit

[ etc. ]

您也可以設定您的腳本,它應該以這種方式接受各類選項。  命令行參數是一個高級主題並在之後學習,當您經過其餘的Python概念後。

Python變量類型

變量是僅僅只是保留的內存位置用來存儲值。這意味着,當建立一個變量,那麼它在內存中保留一些空間。

依據一個變量的數據類型,解釋器分配內存,並決定怎樣可以被存儲在所保留的內存中。所以,經過分配不一樣的數據類型的變量,你可以存儲整數,小數或字符在這些變量中。

變量賦值:

Python的變量沒必要顯式地聲明保留的存儲器空間。當分配一個值給一個變量的聲明將本身主動發生。等號(=)來賦值給變量。

操做數=操做符的左邊是變量,操做數=操做符的右側的名稱在變量中存儲的值。好比:

#!/usr/bin/python

counter = 100          # An integer assignment
miles   = 1000.0       # A floating point
name    = "John"       # A string

print counter
print miles
print name

在這裏,分配值100,1000.0和「John」分別給變量counter,miles和respectively。當執行這個程序,這將產生下面結果:

100
1000.0
John

多重賦值:

Python贊成您同一時候指定一個值給幾個變量。好比:

a = b = c = 1

這裏,整數對象建立的值1,並且所有三個變量被分配到一樣的內存位置。您也可以將多個對象分別到多個變量。好比:

	a, b, c = 1, 2, "john"

這裏,兩個整對象用值1和2分配給變量a和b,並且值爲「john」的字符串對象被分配到變量c。

標準的數據類型:

存儲在內存中的數據可以是多種類型的。好比,一我的的年齡被存儲爲一個數字值和他的地址被存儲爲字母數字字符。Python用於對每個人的操做的各類標準類型定義在存儲方法。

Python有五個標準的數據類型:

  • 數字

  • 字符串

  • 列表

  • 元組

  • 字典

Python數字:

數字數據類型存儲數值。它們是不可變的數據類型,這意味着改變一個新分配的對象的數字數據類型的結果值。

當分配一個值給他們建立的對象。好比:

var1 = 1
var2 = 10

也可以使用del語句刪去有關一些對象。 del語句的語法是:

del var1[,var2[,var3[....,varN]]]]

也可以使用del語句刪除單個或多個對象。好比:

del var
del var_a, var_b

Python支持四種不一樣的數值類型:

  • int (有符號整數)

  • long (長整數[也可以以八進制和十六進制表示])

  • float (浮點實數值)

  • complex (複數)

好比:

這裏是數字的一些樣例:

int long float complex
10 51924361L 0.0 3.14j
100 -0x19323L 15.20 45.j
-786 0122L -21.9 9.322e-36j
080 0xDEFABCECBDAECBFBAEl 32.3+e18 .876j
-0490 535633629843L -90. -.6545+0J
-0x260 -052318172735L -32.54e100 3e+26J
0x69 -4721885298529L 70.2-E12 4.53e-7j
  • Python贊成使用一個小寫L表示長整型,但建議您僅僅使用一個大寫的L到避免和數字1 長得同樣不easy分辨,Python顯示長整數用一個大寫L。

  • 複數包括一個有序對錶示爲a + bj,當中,a是實部,b是複數的虛部實浮點數。

Python字符串:

在Python中的字符串被肯定爲一組連續的字符在引號之間。 Python贊成在不論什麼對單引號或雙引號。串的子集,可以使用切片操做符可採用([]和[:]),索引從0開始的字符串的開始和結束(-1)。

加號(+)符號的字符串鏈接操做符,而星號(*)表示反覆操做。好比:

#!/usr/bin/python

str = 'Hello World!'

print str          # Prints complete string
print str[0]       # Prints first character of the string
print str[2:5]     # Prints characters starting from 3rd to 5th
print str[2:]      # Prints string starting from 3rd character
print str * 2      # Prints string two times
print str + "TEST" # Prints concatenated string

這將產生下面結果:

Hello World!
H
llo
llo World!
Hello World!Hello World!
Hello World!TEST

Python列表:

列表是最通用的Python複合數據類型。列表中包括以逗號分隔,並在方括號([])包括的項目。在必定程度上,列表類似C語言中的數組,它們之間的一個差異是,所有屬於一個列表中的項目可以是不一樣的數據類型的。

存儲在一個列表中的值可以使用切片操做符來訪問([]和[:])用索引從0開始,在列表的開始位置和結束爲-1。加號(+)符號列表鏈接運算符,星號(*)反覆操做。好比:

#!/usr/bin/python

list = [ 'abcd', 786 , 2.23, 'john', 70.2 ]
tinylist = [123, 'john']

print list          # Prints complete list
print list[0]       # Prints first element of the list
print list[1:3]     # Prints elements starting from 2nd till 3rd 
print list[2:]      # Prints elements starting from 3rd element
print tinylist * 2  # Prints list two times
print list + tinylist # Prints concatenated lists

這將產生下面結果:

['abcd', 786, 2.23, 'john', 70.200000000000003]
abcd
[786, 2.23]
[2.23, 'john', 70.200000000000003]
[123, 'john', 123, 'john']
['abcd', 786, 2.23, 'john', 70.200000000000003, 123, 'john']

Python元組:

元組是相似於列表中的序列數據類型。一個元組由數個逗號分隔的值。不一樣於列表,只是,元組圓括號括起來。

列表和元組之間的主要差異是:列表括在括號([])和它們的元素和大小是可以改變的,而元組在圓括號(),不能被更新。元組可以被以爲是僅僅讀列表。好比:

#!/usr/bin/python

tuple = ( 'abcd', 786 , 2.23, 'john', 70.2  )
tinytuple = (123, 'john')

print tuple           # Prints complete list
print tuple[0]        # Prints first element of the list
print tuple[1:3]      # Prints elements starting from 2nd till 3rd 
print tuple[2:]       # Prints elements starting from 3rd element
print tinytuple * 2   # Prints list two times
print tuple + tinytuple # Prints concatenated lists

這將產生下面結果:

('abcd', 786, 2.23, 'john', 70.200000000000003)
abcd
(786, 2.23)
(2.23, 'john', 70.200000000000003)
(123, 'john', 123, 'john')
('abcd', 786, 2.23, 'john', 70.200000000000003, 123, 'john')

下面是元組無效的,因爲咱們嘗試更新一個元組,這是不一樣意的。相似的操做在列表中是可以的:

#!/usr/bin/python

tuple = ( 'abcd', 786 , 2.23, 'john', 70.2  )
list = [ 'abcd', 786 , 2.23, 'john', 70.2  ]
tuple[2] = 1000    # Invalid syntax with tuple
list[2] = 1000     # Valid syntax with list

Python字典:

Python字典是一種哈希表型。他們像關聯數組或哈希在Perl中同樣,由鍵 - 值對組成。字典鍵差點兒可以是不論什麼Python類型,但通常是數字或字符串。值可以是隨意Python的對象。

字典是由花括號括號({}),可分配值,並用方括號([])訪問。好比:

#!/usr/bin/python

dict = {}
dict['one'] = "This is one"
dict[2]     = "This is two"

tinydict = {'name': 'john','code':6734, 'dept': 'sales'}


print dict['one']       # Prints value for 'one' key
print dict[2]           # Prints value for 2 key
print tinydict          # Prints complete dictionary
print tinydict.keys()   # Prints all the keys
print tinydict.values() # Prints all the values

這將產生下面結果:

This is one
This is two
{'dept': 'sales', 'code': 6734, 'name': 'john'}
['dept', 'code', 'name']
['sales', 6734, 'john']

字典有元素順序的概念。它的元素是無序的。

數據類型轉換:

有時候,可能需要運行的內置類型之間的轉換。類型之間的轉換,僅僅需使用類名做爲函數。

有幾個內置的功能,從一種數據類型進行轉換爲還有一種。這些函數返回一個表示轉換值的新對象。

函數 描寫敘述

int(x [,base])

將x轉換爲一個整數。基數指定爲base,假設x是一個字符串。

long(x [,base] )

將x轉換爲一個長整數。基數指定爲base,假設x是一個字符串。

float(x)

將x轉換到一個浮點數。

complex(real [,imag])

建立一個複數。

str(x)

轉換對象x爲字符串表示形式。

repr(x)

對象x轉換爲一個表達式字符串。

eval(str)

計算一個字符串,並返回一個對象。

tuple(s)

把s轉換爲一個元組。

list(s)

把s轉換爲一個列表。

set(s)

把s轉換爲一個集合。

dict(d)

建立一個字典。 d必須的(鍵,值)元組序列。

frozenset(s)

把s轉換爲凍結集。

chr(x)

整數轉換爲一個字符。

unichr(x)

整數轉換爲一個Unicode字符。

ord(x)

轉換單個字符爲整數值。

hex(x)

將整數轉換爲十六進制字符串。

oct(x)

將整數轉換爲以八進制的字符串。

Python 3開發網絡爬蟲(一)

選擇Python版本號

有2和3兩個版本號, 3比較新, 據說修改大. 依據我在知乎上搜集的觀點來看, 我仍是傾向於使用」在趨勢中將會愈來愈火」的版本號, 而非」眼下已經很是穩定而且很是成熟」的版本號. 這是我的喜愛, 而且預測不必定準確. 但是假設Python3沒法像Python2那麼火, 那麼整個Python語言就不可避免的隨着時間的推移愈來愈落後, 所以我想事實上選哪一個的最壞風險都同樣, 但是最好回報倒是Python3的大. 事實上二者差異也可以說大也可以說不大, 終於都不是什麼大問題. 我選擇的是Python 3.

 

選擇參考資料

由於我是一邊學一邊寫, 而不是我全然學會了以後才開始很是有條理的寫, 因此參考資料就很是重要(原本應該是我的開發經驗很是重要, 但我是零基礎).

寫到這裏的時候, 上面第二第三個連接的票數第一的回答已經看完了, 他們提到的有些部分(比方爬行的路線不能有迴路)我就不寫了。
 

一個簡單的僞代碼

下面這個簡單的僞代碼用到了set和queue這兩種經典的數據結構, 集與隊列. 集的做用是記錄那些已經訪問過的頁面, 隊列的做用是進行廣度優先搜索.

queue Q
set S
StartPoint = "http://jecvay.com"
Q.push(StartPoint)  # 經典的BFS開頭
S.insert(StartPoint)  # 訪問一個頁面以前先標記他爲已訪問
while (Q.empty() == false)  # BFS循環體
  T = Q.top()  # 並且pop
  for point in PageUrl(T)  # PageUrl(T)是指頁面T中所有url的集合, point是這個集合中的一個元素.
    if (point not in S)
      Q.push(point)
      S.insert(point)

這個僞代碼不能運行,  我認爲我寫的有的不三不四, 不類Python也不類C++.. 但是我相信看懂是沒問題的, 這就是個最簡單的BFS結構. 我是看了知乎裏面的那個僞代碼以後, 本身用個人風格寫了一遍. 你也需要用你的風格寫一遍.

這裏用到的Set其內部原理是採用了Hash表, 傳統的Hash對爬蟲來講佔用空間太大, 所以有一種叫作Bloom Filter的數據結構更適合用在這裏替代Hash版本號的set. 我打算之後再看這個數據結構怎麼使用, 現在先跳過, 因爲對於零基礎的我來講, 這不是重點.

代碼實現(一): 用Python抓取指定頁面

我使用的編輯器是Idle, 安裝好Python3後這個編輯器也安裝好了, 小巧輕便, 按一個F5就能執行並顯示結果. 代碼例如如下:

#encoding:UTF-8
import urllib.request
 
url = "http://www.baidu.com"
data = urllib.request.urlopen(url).read()
data = data.decode('UTF-8')
print(data)

演示樣例圖片

urllib.request是一個庫, 隸屬urllib. 點此打開官方相關文檔. 官方文檔應該怎麼使用呢? 首先點剛剛提到的這個連接進去的頁面有urllib的幾個子庫, 咱們臨時用到了request, 因此咱們先看urllib.request部分. 首先看到的是一句話介紹這個庫是幹什麼用的:

The urllib.request module defines functions and classes which help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, cookies and more.

而後把咱們代碼中用到的urlopen()函數部分閱讀完.

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False)

重點部分是返回值, 這個函數返回一個 http.client.HTTPResponse 對象, 這個對象又有各類方法, 比方咱們用到的read()方法, 這些方法都可以依據官方文檔的連接鏈過去. 依據官方文檔所寫, 我用控制檯執行完成上面這個程序後, 又繼續執行例如如下代碼, 以更熟悉這些亂七八糟的方法是幹什麼的.

>>> a = urllib.request.urlopen(full_url)
>>> type(a)
<class ‘http.client.HTTPResponse’>

>>> a.geturl()
‘http://www.baidu.com/s?word=Jecvay’

>>> a.info()
<http.client.HTTPMessage object at 0x03272250>

>>> a.getcode()
200

代碼實現(二): 用Python簡單處理URL

假設要抓取百度上面搜索關鍵詞爲Jecvay Notes的網頁, 則代碼例如如下

import urllib
import urllib.request
 
data={}
data['word']='Jecvay Notes'
 
url_values=urllib.parse.urlencode(data)
url="http://www.baidu.com/s?"
full_url=url+url_values
 
data=urllib.request.urlopen(full_url).read()
data=data.decode('UTF-8')
print(data)

data是一個字典, 而後經過urllib.parse.urlencode()來將data轉換爲 ‘word=Jecvay+Notes’的字符串, 最後和url合併爲full_url, 其他和上面那個最簡單的樣例相同. 關於urlencode(), 相同經過官方文檔學習一下他是幹什麼的. 經過查看

  1. urllib.parse.urlencode(query, doseq=False, safe=」, encoding=None, errors=None)
  2. urllib.parse.quote_plus(string, safe=」, encoding=None, errors=None)
大概知道他是把一個通俗的字符串, 轉化爲url格式的字符串。

Python 3開發網絡爬蟲(二)

上一回, 我學會了

  1. 用僞代碼寫出爬蟲的主要框架;
  2. 用Python的urllib.request庫抓取指定url的頁面;
  3. 用Python的urllib.parse庫對普通字符串轉符合url的字符串.

這一回, 開始用Python將僞代碼中的所有部分實現. 由於文章的標題就是」零基礎」, 所以會先把用到的兩種數據結構隊列集合介紹一下. 而對於」正則表達式「部分, 限於篇幅不能介紹, 但給出我比較喜歡的幾個參考資料.

Python的隊列

在爬蟲程序中, 用到了廣度優先搜索(BFS)算法. 這個算法用到的數據結構就是隊列.

Python的List功能已經足夠完畢隊列的功能, 可以用 append() 來向隊尾加入元素, 可以用相似數組的方式來獲取隊首元素, 可以用 pop(0) 來彈出隊首元素. 但是List用來完畢隊列功能事實上是低效率的, 因爲List在隊首使用 pop(0) 和 insert() 都是效率比較低的, Python官方建議使用collection.deque來高效的完畢隊列任務.

from collections import deque
queue = deque(["Eric", "John", "Michael"])
queue.append("Terry")           # Terry 入隊
queue.append("Graham")          # Graham 入隊
queue.popleft()                 # 隊首元素出隊
#輸出: 'Eric'
queue.popleft()                 # 隊首元素出隊
#輸出: 'John'
queue                           # 隊列中剩下的元素
#輸出: deque(['Michael', 'Terry', 'Graham'])

(以上樣例引用自官方文檔)

Python的集合

在爬蟲程序中, 爲了避免反覆爬那些已經爬過的站點, 咱們需要把爬過的頁面的url放進集合中, 在每一次要爬某一個url以前, 先看看集合裏面是否已經存在. 假設已經存在, 咱們就跳過這個url; 假設不存在, 咱們先把url放入集合中, 而後再去爬這個頁面.

Python提供了set這樣的數據結構. set是一種無序的, 不包括反覆元素的結構. 通常用來測試是否已經包括了某元素, 或者用來對衆多元素們去重. 與數學中的集合論相同, 他支持的運算有交, 並, 差, 對稱差.

建立一個set可以用 set() 函數或者花括號 {} . 但是建立一個空集是不能使用一個花括號的, 僅僅能用 set() 函數. 因爲一個空的花括號建立的是一個字典數據結構. 下面相同是Python官網提供的演示樣例.

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # 這裏演示的是去重功能
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket                 # 高速推斷元素是否在集合內
True
>>> 'crabgrass' in basket
False
 
>>> # 如下展現兩個集合間的運算.
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # 集合a中包括元素
{'r', 'd', 'b'}
>>> a | b                              # 集合a或b中包括的所有元素
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # 集合a和b中都包括了的元素
{'a', 'c'}
>>> a ^ b                              # 不一樣一時候包括於a和b的元素
{'r', 'd', 'b', 'm', 'z', 'l'

事實上咱們僅僅是用到當中的高速推斷元素是否在集合內的功能, 以及集合的並運算.

Python的正則表達式

在爬蟲程序中, 爬回來的數據是一個字符串, 字符串的內容是頁面的html代碼. 咱們要從字符串中, 提取出頁面提到過的所有url. 這就要求爬蟲程序要有簡單的字符串處理能力, 而正則表達式可以很是輕鬆的完畢這一任務.

參考資料

儘管正則表達式功能異常強大, 很是多實際上用的規則也很是巧妙, 真正熟練正則表達式需要比較長的實踐鍛鍊. 只是咱們僅僅需要掌握怎樣使用正則表達式在一個字符串中, 把所有的url都找出來, 就可以了. 假設實在想要跳過這一部分, 可以在網上找到很是多現成的匹配url的表達式, 拿來用就能夠.

 

Python網絡爬蟲Ver 1.0 alpha

有了以上鋪墊, 最終可以開始寫真正的爬蟲了. 我選擇的入口地址是Fenng叔的Startup News, 我想Fenng叔剛剛拿到7000萬美金融資, 不會介意你們的爬蟲去光臨他家的小站吧. 這個爬蟲儘管可以勉強執行起來, 但是由於缺少異常處理, 僅僅能爬些靜態頁面, 也不會分辨什麼是靜態什麼是動態, 碰到什麼狀況應該跳過, 因此工做一下子就要敗下陣來.

import re
import urllib.request
import urllib
 
from collections import deque
 
queue = deque()
visited = set()
 
url = 'http://news.dbanotes.net'  # 入口頁面, 可以換成別的
 
queue.append(url)
cnt = 0
 
while queue:
  url = queue.popleft()  # 隊首元素出隊
  visited |= {url}  # 標記爲已訪問
 
  print('已經抓取: ' + str(cnt) + '   正在抓取 <---  ' + url)
  cnt += 1
  urlop = urllib.request.urlopen(url)
  if 'html' not in urlop.getheader('Content-Type'):
    continue
 
  # 避免程序異常停止, 用try..catch處理異常
  try:
    data = urlop.read().decode('utf-8')
  except:
    continue
 
  # 正則表達式提取頁面中所有隊列, 並推斷是否已經訪問過, 而後增長待爬隊列
  linkre = re.compile('href=\"(.+?)\"')
  for x in linkre.findall(data):
    if 'http' in x and x not in visited:
      queue.append(x)
      print('增長隊列 --->  ' + x)

這個版本號的爬蟲使用的正則表達式是

'href=\"(.+?)\"'

因此會把那些.ico或者.jpg的連接都爬下來. 這樣read()了以後碰上decode(‘utf-8′)就要拋出異常. 所以咱們用getheader()函數來獲取抓取到的文件類型, 是html再繼續分析當中的連接.

if 'html' not in urlop.getheader('Content-Type'):
    continue

但是即便是這樣, 依舊有些站點執行decode()會異常. 所以咱們把decode()函數用try..catch語句包圍住, 這樣他就不會致使程序停止. 程序執行效果圖例如如下:

webbugv1ahpha

爬蟲是可以工做了, 但是在碰到連不上的連接的時候, 它並不會超時跳過. 而且爬到的內容並無進行處理, 沒有獲取對咱們有價值的信息, 也沒有保存到本地. 下次咱們可以無缺這個alpha版本號.

Python3網絡爬蟲(三): 假裝瀏覽器

上一次我自學爬蟲的時候, 寫了一個簡陋的勉強能執行的爬蟲alpha. alpha版有很是多問題. 比方一個站點上不了, 爬蟲卻一直在等待鏈接返回response, 不知道超時跳過; 或者有的站點專門攔截爬蟲程序, 咱們的爬蟲也不會假裝本身成爲瀏覽器正規部隊; 並且抓取的內容沒有保存到本地, 沒有什麼做用. 此次咱們一個個解決這些小問題.

此外, 在我寫這系列文章的第二篇的時候, 我仍是一個對http的get和post以及response這些名詞一無所知的人, 但是我認爲這樣是寫很差爬蟲的. 因而我參考了 <<計算機網絡–自頂向下方法>> 這本書的第二章的大部份內容. 假設你也同樣對http的機制一無所知, 我也推薦你找一找這方面的資料來看. 在看的過程當中, 安裝一個叫作Fiddler的軟件, 邊學邊實踐, 觀察瀏覽器是怎樣訪問一個站點的, 怎樣發出請求, 怎樣處理響應, 怎樣進行跳轉, 甚至怎樣經過登陸認證. 有句老話說得好, 越會用Fiddler, 就對理論理解更深入; 越對理論理解深入, Fiddler就用得越順手. 最後咱們在用爬蟲去作各類各樣的事情的時候, Fiddler老是最得力的助手之中的一個.

加入超時跳過功能

首先, 我簡單地將

urlop = urllib.request.urlopen(url)

改成

urlop = urllib.request.urlopen(url, timeout = 2)

執行後發現, 當發生超時, 程序因爲exception中斷. 因而我把這一句也放在try .. except 結構裏, 問題解決.

 

支持本身主動跳轉

在爬 http://baidu.com 的時候, 爬回來一個沒有什麼內容的東西, 這個東西告訴咱們應該跳轉到 http://www.baidu.com . 但是咱們的爬蟲並不支持本身主動跳轉, 現在咱們來加上這個功能, 讓爬蟲在爬 baidu.com 的時候能夠抓取 www.baidu.com 的內容.

首先咱們要知道爬 http://baidu.com 的時候他返回的頁面是怎麼樣的, 這個咱們既可以用 Fiddler 看, 也可以寫一個小爬蟲來抓取. 這裏我抓到的內容例如如下, 你也應該嘗試一下寫幾行 python 來抓一抓.

<html>
<meta http-equiv=」refresh」 content=」0;url=http://www.baidu.com/」>
</html>

看代碼咱們知道這是一個利用 html 的 meta 來刷新與重定向的代碼, 當中的0是等待0秒後跳轉, 也就是立刻跳轉. 這樣咱們再像上一次說的那樣用一個正則表達式把這個url提取出來就可以爬到正確的地方去了. 事實上咱們上一次寫的爬蟲已經可以具備這個功能, 這裏僅僅是單獨拿出來講明一下 http 的 meta 跳轉.

假裝瀏覽器正規軍

前面幾個小內容都寫的比較少. 現在具體研究一下怎樣讓站點們把咱們的Python爬蟲當成正規的瀏覽器來訪. 因爲假設不這麼假裝本身, 有的站點就爬不回來了. 假設看過理論方面的知識, 就知道咱們是要在 GET 的時候將 User-Agent 加入到header裏.

假設沒有看過理論知識, 依照下面keyword搜索學習吧 :D

  • HTTP 報文分兩種: 請求報文響應報文
  • 請求報文的請求行首部行
  • GETPOST, HEAD, PUT, DELETE 方法

我用 IE 瀏覽器訪問百度首頁的時候, 瀏覽器發出去的請求報文例如如下:

GET http://www.baidu.com/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: www.baidu.com
DNT: 1
Connection: Keep-Alive
Cookie: BAIDUID=57F4D171573A6B88A68789EF5DDFE87:FG=1; uc_login_unique=ccba6e8d978872d57c7654130e714abd; BD_UPN=11263145; BD

而後百度收到這個消息後, 返回給個人的響應報文例如如下(有刪節):

HTTP/1.1 200 OK
Date: Mon, 29 Sep 2014 13:07:01 GMT
Content-Type: text/html; charset=utf-8
Connection: Keep-Alive
Vary: Accept-Encoding
Cache-Control: private
Cxy_all: baidu+8b13ba5a7289a37fb380e0324ad688e7
Expires: Mon, 29 Sep 2014 13:06:21 GMT
X-Powered-By: HPHP
Server: BWS/1.1
BDPAGETYPE: 1
BDQID: 0x8d15bb610001fe79
BDUSERID: 0
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Content-Length: 80137

<!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」><link rel=」dns-prefetch」 href=」//s1.bdstatic.com」/><link rel=」dns-prefetch」 href=」//t1.baidu.com」/><link rel=」dns-prefetch」 href=」//t2.baidu.com」/><link rel=」dns-prefetch」 href=」//t3.baidu.com」/><link rel=」dns-prefetch」 href=」//t10.baidu.com」/><link rel=」dns-prefetch」 href=」//t11.baidu.com」/><link rel=」dns-prefetch」 href=」//t12.baidu.com」/><link rel=」dns-prefetch」 href=」//b1.bdstatic.com」/><title>百度一下,你就知道</title><style index=」index」 > ……….這裏省略兩萬字……………. </script></body></html>

假設能夠看懂這段話的第一句就OK了, 別的能夠之後再配合 Fiddler 慢慢研究. 因此咱們要作的就是在 Python 爬蟲向百度發起請求的時候, 順便在請求裏面寫上 User-Agent, 代表本身是瀏覽器君.

在 GET 的時候加入 header 有很是多方法, 如下介紹兩種方法.

第一種方法比較簡便直接, 但是很差擴展功能, 代碼例如如下:

import urllib.request
 
url = 'http://www.baidu.com/'
req = urllib.request.Request(url, headers = {
    'Connection': 'Keep-Alive',
    'Accept': 'text/html, application/xhtml+xml, */*',
    'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
})
oper = urllib.request.urlopen(req)
data = oper.read()
print(data.decode())	

 

另一種方法使用了 build_opener 這種方法, 用來本身定義 opener, 這樣的方法的優勢是可以方便的拓展功能, 好比如下的代碼就拓展了本身主動處理 Cookies 的功能.

import urllib.request
import http.cookiejar
 
# head: dict of header
def makeMyOpener(head = {
    'Connection': 'Keep-Alive',
    'Accept': 'text/html, application/xhtml+xml, */*',
    'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
}):
    cj = http.cookiejar.CookieJar()
    opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
    header = []
    for key, value in head.items():
        elem = (key, value)
        header.append(elem)
    opener.addheaders = header
    return opener
 
oper = makeMyOpener()
uop = oper.open('http://www.baidu.com/', timeout = 1000)
data = uop.read()
print(data.decode())

上述代碼執行後經過 Fiddler 抓到的 GET 報文例如如下所看到的:

GET http://www.baidu.com/ HTTP/1.1
Accept-Encoding: identity
Connection: close
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3

可見咱們在代碼裏寫的東西都加入到請求報文裏面了.

保存抓回來的報文

順便說說文件操做. Python 的文件操做仍是至關方便的. 咱們可以講抓回來的數據 data 以二進制形式保存, 也可以通過 decode() 處理成爲字符串後以文本形式保存. 修改一下打開文件的方式就能用不一樣的姿式保存文件了. 如下是參考代碼:

def saveFile(data):
    save_path = 'D:\\temp.out'
    f_obj = open(save_path, 'wb') # wb 表示打開方式
    f_obj.write(data)
    f_obj.close()
 
# 這裏省略爬蟲代碼
# ...
 
# 爬到的數據放到 dat 變量裏
# 將 dat 變量保存到 D 盤下
saveFile(dat)

 

下回咱們會用 Python 來爬那些需要登陸以後才幹看到的信息. 在那以前, 我已經對 Fiddler 略微熟悉了. 但願一塊兒學習的也提早安裝個 Fiddler 玩一下.

Python3網絡爬蟲(四): 登陸

今天的工做很是有意思, 咱們用 Python 來登陸站點, 用Cookies記錄登陸信息, 而後就可以抓取登陸以後才幹看到的信息. 今天咱們拿知乎網來作示範. 爲何是知乎? 這個很是難解釋, 但是確定的是知乎這麼大這麼成功的站點全然不用我來幫他打廣告. 知乎網的登陸比較簡單, 傳輸的時候沒有對username和password加密, 卻又不失表明性, 有一個必須從主頁跳轉登陸的過程.

不得不說一下, Fiddler 這個軟件是 Tpircsboy 告訴個人. 感謝他給我帶來這麼好玩的東西.

第一步: 使用 Fiddler 觀察瀏覽器行爲

在開着 Fiddler 的條件下執行瀏覽器, 輸入知乎網的網址 http://www.zhihu.com 回車後到 Fiddler 中就能看到捕捉到的鏈接信息. 在左邊選中一條 200 鏈接, 在右邊打開 Inspactors 透視圖, 上方是該條鏈接的請求報文信息, 下方是響應報文信息.

當中 Raw 標籤是顯示報文的原文. 下方的響應報文很是有多是沒有通過解壓或者解碼的, 這樣的狀況他會在中間部位有一個小提示, 點擊一下就能解碼顯示出原文了.

QQ截圖20141003151553

 

以上這個截圖是在未登陸的時候進入 http://www.zhihu.com 獲得的. 現在咱們來輸入username和password登錄知乎網, 再看看瀏覽器和知乎server之間發生了什麼.

QQ截圖20141003152308

 

點擊登錄後, 回到 Fiddler 裏查看新出現的一個 200 連接. 咱們瀏覽器攜帶者個人賬號password給知乎server發送了一個 POST, 內容例如如下:

POST http://www.zhihu.com/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: */*
X-Requested-With: XMLHttpRequest
Referer: http://www.zhihu.com/#signin
Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.4; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Length: 97
DNT: 1
Host: www.zhihu.com
Connection: Keep-Alive
Pragma: no-cache
Cookie: __utma=51854390.1539896551.1412320246.1412320246.1412320246.1; __utmb=51854390.6.10.1412320246; __utmc=51854390; __utmz=51854390.1412320246.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmv=51854390.000–|3=entry_date=20141003=1

_xsrf=4b41f6c7a9668187ccd8a610065b9718&email=此處塗黑%40gmail.com&password=此處不可見&rememberme=y

截圖例如如下:

QQ截圖20141003153243

個人瀏覽器給 http://www.zhihu.com/login 這個網址(多了一個/login) 發送了一個POST, 內容都已經在上面列出來了, 實username, 有password, 有一個」記住我」的 yes, 當中這個 WebForms 標籤下 Fiddler 能夠比較層次分明的列出來 POST 的內容. 因此咱們用 Python 也發送一樣的內容就能登陸了. 但是這裏出現了一個 Name 爲 _xsrf 的項, 他的值是 4b41f6c7a9668187ccd8a610065b9718. 咱們要先獲取這個值, 而後才幹給他發.

瀏覽器是怎樣獲取的呢, 咱們剛剛是先訪問了 http://www.zhihu.com/ 這個網址, 就是首頁, 而後登陸的時候他卻給 http://www.zhihu.com/login 這個網址發信息. 因此用偵探通常的思惟去思考這個問題, 就會發現確定是首頁把 _xsrf 生成發送給咱們, 而後咱們再把這個 _xsrf 發送給 /login 這個 url. 這樣一下子事後咱們就要從第一個 GET 獲得的響應報文裏面去尋找 _xsrf

截圖下方的方框說明, 咱們不只登陸成功了, 而且server還告訴咱們的瀏覽器怎樣保存它給出的 Cookies 信息. 因此咱們也要用 Python 把這些 Cookies 信息記錄下來.

這樣 Fiddler 的工做就基本結束了!

第二步: 解壓縮

簡單的寫一個 GET 程序, 把知乎首頁 GET 下來, 而後 decode() 一下解碼, 結果報錯. 細緻一看, 發現知乎網傳給咱們的是通過 gzip 壓縮以後的數據. 這樣咱們就需要先對數據解壓. Python 進行 gzip 解壓很是方便, 因爲內置有庫可以用. 代碼片斷例如如下:

import gzip
def ungzip(data):
    try:        # 嘗試解壓
        print('正在解壓.....')
        data = gzip.decompress(data)
        print('解壓完成!')
    except:
        print('未經壓縮, 無需解壓')
    return data

經過 opener.read() 讀取回來的數據, 通過 ungzip 本身主動處理後, 再來一遍 decode() 就可以獲得解碼後的 str 了

第二步: 使用正則表達式獲取沙漠之舟

_xsrf 這個鍵的值在茫茫無際的互聯網沙漠之中指引咱們用正確的姿式來登陸知乎, 因此 _xsrf 可謂沙漠之舟. 假設沒有 _xsrf, 咱們也許實username和password也沒法登陸知乎(我沒試過, 只是咱們學校的教務系統確實如此) 如上文所說, 咱們在第一遍 GET 的時候可以從響應報文中的 HTML 代碼裏面獲得這個沙漠之舟. 例如如下函數實現了這個功能, 返回的 str 就是 _xsrf 的值.

import re
def getXSRF(data):
    cer = re.compile('name=\"_xsrf\" value=\"(.*)\"', flags = 0)
    strlist = cer.findall(data)
    return strlist[0]

 

第三步: 發射 POST !!

集齊 _xsrf, id, password 三大法寶, 咱們可以發射 POST 了. 這個 POST 一旦發射過去, 咱們就登錄上了server, server就會發給咱們 Cookies. 原本處理 Cookies 是個麻煩的事情, 只是 Python 的 http.cookiejar 庫給了咱們很是方便的解決方式, 僅僅要在建立 opener 的時候將一個 HTTPCookieProcessor 放進去, Cookies 的事情就不用咱們管了. 如下的代碼體現了這一點.

import http.cookiejar
import urllib.request
def getOpener(head):
    # deal with the Cookies
    cj = http.cookiejar.CookieJar()
    pro = urllib.request.HTTPCookieProcessor(cj)
    opener = urllib.request.build_opener(pro)
    header = []
    for key, value in head.items():
        elem = (key, value)
        header.append(elem)
    opener.addheaders = header
    return opener

getOpener 函數接收一個 head 參數, 這個參數是一個字典. 函數把字典轉換成元組集合, 放進 opener. 這樣咱們創建的這個 opener 就有兩大功能:

  1. 本身主動處理使用 opener 過程當中遇到的 Cookies
  2. 本身主動在發出的 GET 或者 POST 請求中加上本身定義的 Header

第四部: 正式執行

正式執行還差一點點, 咱們要把要 POST 的數據弄成 opener.open() 支持的格式. 因此還要  urllib.parse 庫裏的 urlencode() 函數. 這個函數可以把 字典 或者 元組集合 類型的數據轉換成 & 鏈接的 str.

str 還不行, 還要經過 encode() 來編碼, 才幹看成 opener.open() 或者 urlopen() 的 POST 數據參數來使用. 代碼例如如下:

url = 'http://www.zhihu.com/'
opener = getOpener(header)
op = opener.open(url)
data = op.read()
data = ungzip(data)     # 解壓
_xsrf = getXSRF(data.decode())
 
url += 'login'
id = '這裏填你的知乎賬號'
password = '這裏填你的知乎密碼'
postDict = {
        '_xsrf':_xsrf,
        'email': id,
        'password': password,
        'rememberme': 'y'
}
postData = urllib.parse.urlencode(postDict).encode()
op = opener.open(url, postData)
data = op.read()
data = ungzip(data)
 
print(data.decode())  # 你可以依據你的喜歡來處理抓取回來的數據了!

代碼執行後, 咱們發現本身關注的人的動態(顯示在登錄後的知乎首頁的那些), 都被抓取回來了. 下一步作一個統計分析器, 或者本身主動推送器, 或者內容分級本身主動分類器, 都可以.

完整代碼例如如下:

import gzip
import re
import http.cookiejar
import urllib.request
import urllib.parse
 
def ungzip(data):
    try:        # 嘗試解壓
        print('正在解壓.....')
        data = gzip.decompress(data)
        print('解壓完成!')
    except:
        print('未經壓縮, 無需解壓')
    return data
 
def getXSRF(data):
    cer = re.compile('name=\"_xsrf\" value=\"(.*)\"', flags = 0)
    strlist = cer.findall(data)
    return strlist[0]
 
def getOpener(head):
    # deal with the Cookies
    cj = http.cookiejar.CookieJar()
    pro = urllib.request.HTTPCookieProcessor(cj)
    opener = urllib.request.build_opener(pro)
    header = []
    for key, value in head.items():
        elem = (key, value)
        header.append(elem)
    opener.addheaders = header
    return opener
 
header = {
    'Connection': 'Keep-Alive',
    'Accept': 'text/html, application/xhtml+xml, */*',
    'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko',
    'Accept-Encoding': 'gzip, deflate',
    'Host': 'www.zhihu.com',
    'DNT': '1'
}
 
url = 'http://www.zhihu.com/'
opener = getOpener(header)
op = opener.open(url)
data = op.read()
data = ungzip(data)     # 解壓
_xsrf = getXSRF(data.decode())
 
url += 'login'
id = '這裏填你的知乎賬號'
password = '這裏填你的知乎密碼'
postDict = {
        '_xsrf':_xsrf,
        'email': id,
        'password': password,
        'rememberme': 'y'
}
postData = urllib.parse.urlencode(postDict).encode()
op = opener.open(url, postData)
data = op.read()
data = ungzip(data)
 
print(data.decode())
相關文章
相關標籤/搜索