1. 程式人生 > >python用 requests 模組從 Web 下載檔案

python用 requests 模組從 Web 下載檔案

requests 模組讓你很容易從 Web 下載檔案,不必擔心一些複雜的問題,諸如網路錯誤、連線問題和資料壓縮。requests 模組不是 Python 自帶的,所以必須先安裝。通過命令列,執行 pip install requests。編寫 requests 模組是因為 Python 的 urllib2 模組用起來太複雜。實際上,請拿一支記號筆塗黑這一段。忘記我曾提到 urllib2。如果你需要從 Web 下載東西,使用requests 模組就好了。接下來,做一個簡單的測試,確保 requests 模組已經正確安裝。在互動式環境中輸入以下程式碼:

>>> import requests

如果沒有錯誤資訊顯示,requests 模組就已經安裝成功了。

用 requests.get()函式下載一個網頁

requests.get()函式接受一個要下載的 URL 字串。通過在 requests.get()的返回值上呼叫 type(),你可以看到它返回一個 Response 物件,其中包含了 Web 伺服器對你的請求做出的響應。稍後我將更詳細地解釋 Response 物件,但現在請在互動式環境中輸入以下程式碼,並保持計算機與因特網的連線:

>>> import requests
>>> res = requests.get('http://www.gutenberg.org/cache/epub/1112/pg1112.txt')
>>> type(res)
<class 'requests.models.Response'>
>>> res.status_code == requests.codes.ok
True
>>> len(res.text)
178981
>>> print(res.text[:250])
The Project Gutenberg EBook of Romeo and Juliet, by William Shakespeare
This eBook is for the use of anyone anywhere at no cost and with
almost no restrictions whatsoever. You may copy it, give it away or
re-use it under the terms of the Proje

該 URL 指向一個文字頁面,其中包含整部羅密歐與朱麗葉,它是由古登堡計劃提供的。通過檢查 Response 物件的 status_code 屬性,你可以瞭解對這個網頁的請求是否成功。如果該值等於requests.codes.ok,那麼一切都好(順便說一下,HTTP協議中“OK”的狀態碼是 200。你可能已經熟悉404 狀態碼,它表示“沒找到”)。如果請求成功,下載的頁面就作為一個字串,儲存在 Response 物件的 text變數中。這個變數儲存了包含整部戲劇的一個大字串,呼叫 len(res.text)表明,它的長度超過 178000 個字元。最後,呼叫 print(res.text[:250])顯示前 250 個字元。

檢查錯誤

正如你看到的,Response 物件有一個 status_code 屬性,可以檢查它是否等於requests.codes.ok,瞭解下載是否成功。檢查成功有一種簡單的方法,就是在 Response物件上呼叫 raise_for_status()方法。如果下載檔案出錯,這將丟擲異常。如果下載成功,就什麼也不做。在互動式環境中輸入以下程式碼:

>>> res = requests.get('http://inventwithpython.com/page_that_does_not_exist')
>>> res.raise_for_status()
Traceback (most recent call last):
    File "<pyshell#138>", line 1, in <module>
        res.raise_for_status()
    File "C:\Python34\lib\site-packages\requests\models.py", line 773, in raise_for_status
        raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found

raise_for_status()方法是一種很好的方式,確保程式在下載失敗時停止。這是一件好事:你希望程式在發生未預期的錯誤時,馬上停止。如果下載失敗對程式來說不夠嚴重,可以用 try 和 except 語句將 raise_for_status()程式碼行包裹起來,處理這一錯誤,不讓程式崩潰。

import requests
res = requests.get('http://inventwithpython.com/page_that_does_not_exist')
try:
    res.raise_for_status()
except Exception as exc:
    print('There was a problem: %s' % (exc))

這次 raise_for_status()方法呼叫導致程式輸出以下內容:

There was a problem: 404 Client Error: Not Found

總是在呼叫 requests.get()之後再呼叫 raise_for_status()。你希望確保下載確實成功,然後再讓程式繼續。

將下載的檔案儲存到硬碟

現在,可以用標準的 open()函式和 write()方法,將 Web 頁面儲存到硬碟中的一個檔案。但是,這裡稍稍有一點不同。首先,必須用“寫二進位制”模式開啟該檔案,即向函式傳入字串'wb',作為 open()的第二引數。即使該頁面是純文字的(例如前面下載的羅密歐與朱麗葉的文字),你也需要寫入二進位制資料,而不是文字資料,目的是為了儲存該文字中的“Unicode 編碼”。

你可以通過以下網頁瞭解更多的Unicode相關內容:

• Joel on Software: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!): http://www.joelonsoftware.com/articles/Unicode.html
• Pragmatic Unicode: http://nedbatchelder.com/text/unipain.html

為了將 Web 頁面寫入到一個檔案,可以使用 for 迴圈和 Response 物件的iter_content()方法。

>>> import requests
>>> res = requests.get('http://www.gutenberg.org/cache/epub/1112/pg1112.txt')
>>> res.raise_for_status()
>>> playFile = open('RomeoAndJuliet.txt', 'wb')
>>> for chunk in res.iter_content(100000):
...    playFile.write(chunk)

100000
78981
>>> playFile.close()

iter_content()方法在迴圈的每次迭代中,返回一段內容。每一段都是 bytes 資料型別,你需要指定一段包含多少位元組。10 萬字節通常是不錯的選擇,所以將 100000作為引數傳遞給 iter_content()。檔案 RomeoAndJuliet.txt 將存在於當前工作目錄。請注意,雖然在網站上檔名是 pg1112.txt,但在你的硬碟上,該檔案的名字不同。requests 模組只處理下載網頁內容。一旦網頁下後,它就只是程式中的資料。即使在下載該網頁後斷開了因特網連線,該頁面的所有資料仍然會在你的計算機中。write()方法返回一個數字,表示寫入檔案的位元組數。在前面的例子中,第一段包含 100000 個位元組,檔案剩下的部分只需要 78981 個位元組。

回顧一下,下載並儲存到檔案的完整過程如下:
1.呼叫 requests.get()下載該檔案。
2.用'wb'呼叫 open(),以寫二進位制的方式開啟一個新檔案。
3.利用 Respose 物件的 iter_content()方法做迴圈。
4.在每次迭代中呼叫 write(),將內容寫入該檔案。
5.呼叫 close()關閉該檔案。

這就是關於 requests 模組的全部內容!相對於寫入文字檔案的 open()/write()/close()工作步驟,for 迴圈和 iter_content()的部分可能看起來比較複雜,但這是為了確保 requests 模組即使在下載巨大的檔案時也不會消耗太多記憶體。你可以訪問http://requests.readthedocs.org/,瞭解 requests 模組的其他功能。