下載網頁使用 requests 模組

下載網頁使用 requests 模組

前言

本篇介紹如何在 Python 中使用 requests 模組建立各種 HTTP 請求,從網頁伺服器上取得想要的資料。

大綱

requests 模組重要功能

  • Keep-Alive & Connection Pooling 保持持續連接直到一方中斷和連接池
  • International Domains and URLs 支持國際域名和 URL
  • Sessions with Cookie Persistence 具有 Cookie 持久性的會話
  • Browser-style SSL Verification 瀏覽器式 SSL 驗證
  • Automatic Content Decoding 自動內容解碼
  • Basic/Digest Authentication 基本/摘要式身份驗證
  • Elegant Key/Value Cookies 優雅的鍵/值 (key/value) 結構的 Cookie
  • Automatic Decompression  自動解壓縮
  • Unicode Response Bodies  支援Unicode格式的伺服器回應文件主體
  • HTTP(S) Proxy Support 支持代理 IP 
  • Multipart File Uploads 分段文件上傳
  • Streaming Downloads 串流下載
  • Connection Timeouts 連接超時
  • Chunked Requests 支援 Chunked 編碼,也可想成分塊傳輸編碼請求
  • .netrc Support  支持.netrc 文件

安裝套件

pip install requests

使用方法

  1. 引入 
  2. import requests
  3. HTTP GET
  4. r = requests.get('https://api.github.com/events')
  5. HTTP POST
  6. r = requests.post('http://httpbin.org/post', data = {'key':'value'})
  7. PUT, DELETE, HEAD, OPTIONS 請求
  8. r = requests.put('http://httpbin.org/put', data = {'key':'value'})
    r = requests.delete('http://httpbin.org/delete')
    r = requests.head('http://httpbin.org/get')
    r = requests.options('http://httpbin.org/get')
    
  9. 傳遞 URLs 參數 : 當 URLs 參數的 dictionary 裡可以把 list 當成一個項目,但不可以加入 None的項目。
  10. payload = {'key1': 'value1', 'key2': 'value2'}
    r = requests.get('http://httpbin.org/get', params=payload)
    
    print(r.url) #查看傳送的 URL
    
  11. 回應物件 Response 分析 : 從回應中取出各種我們需要的資料
  12. print(r.text) #列出文字
    
    print(r.encoding) #列出編碼
    
    print(r.status_code) #列出 HTTP 狀態碼
    #如果顯示 200 就代表沒問題。我們也可以利用以下這個判斷式來檢查狀態碼:
    if r.status_code == requests.codes.ok:
      print("OK")
    print(r.headers) #列出 HTTP Response Headers
    
    print(r.headers['Content-Type']) #印出 Header 中的 Content-Type
    
  13. 解析 JSON 資料 : 如果取得的是 json 格式資料,requests 有內建解析函式。
  14. r = requests.get('https://api.github.com/events')
    r.json()
    
  15. 自訂 Header : 許多時候網站會擋掉 UA 是 python-request 的請求,因此我們很常需要自訂 Header。
  16. url = 'https://api.github.com/some/endpoint'
    headers = {'user-agent': 'Mozilla/5.0'}
    
    r = requests.get(url, headers=headers)
    
  17. 設定 Timeout : 避免程式在維修中或故障的網站停留太久,或是用來檢查是否可存取時很方便。
  18. requests.get('http://github.com', timeout=[SECOND])
    
  19. 指定編碼 : 通常網站會使用 UTF-8 編碼,但若不是,可用這個方法修改讀取編碼。
  20. r.encoding = 'ISO-8859-1'
    
  21. 取得 Cookie :
  22. url = 'http://example.com/some/cookie/setting/url'
    r = requests.get(url)
    
    r.cookies['example_cookie_name']
    
  23. 修改 Cookie :
  24. url = 'http://httpbin.org/cookies'
    cookies = dict(cookies_are='working')
    
    r = requests.get(url, cookies=cookies)
    r.text
    
  25. 帳號密碼登入 : 若遇到需要帳號與密碼登入後才能看的網頁,可以使用 auth 參數指定帳號與密碼。
  26. # 需要帳號登入的網頁
    r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
    

requests 基本應用範例

範例 pythonWebCrawler-04.py : 使用 requests.get() 之後會傳回一個資料型態 Response 物件

# pythonWebCrawler-04.py
import requests

url = 'https://tw.finance.yahoo.com/'
htmlfile = requests.get(url)
print(type(htmlfile))

執行結果

<class 'requests.models.Response'>

範例 pythonWebCrawler-05.py : 檢查回傳的網頁資料是否成功。

# pythonWebCrawler-05.py
import requests

url = 'https://tw.finance.yahoo.com/'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    print("取得網頁內容成功")
else:
    print("取得網頁內容失敗")

執行結果

取得網頁內容成功

範例 pythonWebCrawler-06.py : 檢查回傳的網頁資料是否成功,網頁內容大小。

# pythonWebCrawler-06.py
import requests

url = 'https://tw.finance.yahoo.com/'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    print("取得網頁內容成功")
    print("網頁內容大小 = ", len(htmlfile.text))
else:
    print("取得網頁內容失敗")

執行結果

取得網頁內容成功
網頁內容大小 = 597860

範例 pythonWebCrawler-07.py : 檢查回傳的網頁資料是否成功,列印網頁內容。

# pythonWebCrawler-07.py
import requests

url = 'https://tw.finance.yahoo.com/'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    print("取得網頁內容成功")
else:
    print("取得網頁內容失敗")
print(htmlfile.text)            # 列印網頁內容

執行結果

取得網頁內容成功
<!DOCTYPE html><html id="atomic" class="NoJs desktop" lang="zh-Hant-TW"><head prefix="og: http://ogp.me/ns#"><script>window.performance && window.performance.mark && window.performance.mark('PageStart');</script><meta charSet="utf-8"/><meta property="og:type" content="website"/><meta property="og:description" content="Yahoo奇摩股市提供國內外財經新聞,台股、期貨、選擇權、國際指數、外匯、港滬深股、美股等即時報價資訊.........

搜尋網頁特定內容

範例 pythonWebCrawler-08.py : 搜尋網頁特定內容。我們將在下在網頁內容中搜尋特定字串,將輸入的字串存放在變數 pattern 中,使用2種方法。方法 1 : if pattern in htmlfile.text.....,輸出搜尋成功或失敗。方法 2 : name = re.findall(pattern, htmlfile.text) .....,我們使用有關正則表達式操作的 內建 re 模組,會印出輸入的字串,出現的次數。

# pythonWebCrawler-08.py
import requests
import re

url = 'https://tw.finance.yahoo.com/'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    pattern = input("請輸入欲搜尋的字串 : ")    # pattern存放欲搜尋的字串
# 使用方法1
    if pattern in htmlfile.text:                # 方法1
        print("搜尋 %s 成功" % pattern)
    else:
        print("搜尋 %s 失敗" % pattern)
# 使用方法2, 如果找到放在串列name內
    name = re.findall(pattern, htmlfile.text)   # 方法2
    if name != None:
        print("%s 出現 %d 次" % (pattern, len(name)))
    else:
        print("%s 出現 0 次" % pattern)
else:
    print("網頁下載失敗")

執行結果

請輸入欲搜尋的字串 : 股市
搜尋 股市 成功
股市 出現 68 次

下載網頁失敗的異常處理

有時候輸入網址錯誤或是有反爬蟲機制,導致下載網頁失敗,所以程式中就必須要有異常處理機制。Response 物件中有 raise_for_status() 方法,可處理網址正確但後綴的檔名錯誤的異常,但無法處理網址錯誤

範例 pythonWebCrawler-09.py : 下載網頁失敗的異常處理,由於不存在後綴的檔名錯誤 file_not_existed ,異常 Exception 發生。

# pythonWebCrawler-09.py
import requests

url = 'https://tw.finance.yahoo.com/file_not_existed'  # 不存在的內容
htmlfile = requests.get(url)

try:
    htmlfile.raise_for_status()                 # 異常處理
    print("下載成功")
except Exception as err:                        # err是系統自訂的錯誤訊息
    print("網頁下載失敗: %s" % err)
print("程式結束")

執行結果

網頁下載失敗: 404 Client Error: Not Found for url: https://tw.finance.yahoo.com/file_not_existed
程式結束

範例 pythonWebCrawler-10.py : 下載網頁失敗的異常處理,由於網址錯誤出現一連串錯誤碼,程式沒有正常結束。

# pythonWebCrawler-10.py
import requests

url = 'https://xxxyyy.com/file_not_existed'  # 錯誤的網址
htmlfile = requests.get(url)
try:
    htmlfile.raise_for_status()         # 異常處理
    print("下載成功")
except Exception as err:                 # err是系統自訂的錯誤訊息
    print("網頁下載失敗: %s" % err)
print("程式結束")

執行結果

Traceback (most recent call last):
  File "C:\Python\Python39\lib\site-packages\urllib3\connection.py", line 174, in _new_conn
    conn = connection.create_connection(
  File "C:\Python\Python39\lib\site-packages\urllib3\util\connection.py", line 96, in create_connection
    raise err

範例 pythonWebCrawler-11.py : 下載網頁失敗的異常處理,由於網址錯誤出現一連串錯誤碼,會造成中斷。但將 requests.get() 移到 try ....後面,就不會中斷。

# pythonWebCrawler-11.py
import requests

url = 'https://xxxyyy.com/file_not_existed'  # 錯誤的網址
try:
    htmlfile = requests.get(url)
    htmlfile.raise_for_status()                 # 異常處理
    print("下載成功")
except Exception as err:                        # err是系統自訂的錯誤訊息
    print("網頁下載失敗: %s" % err)
print("程式結束")

執行結果

網頁下載失敗: HTTPSConnectionPool(host='xxxyyy.com', port=443): Max retries exceeded with url: /file_not_existed (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x0000020BA38454F0>: Failed to establish a new connection: [WinError 10060] 連線嘗試失敗,因為連線對象有一段時間並未正確回應,或是連線建立失敗
,因為連線的主機無法回應。'))
程式結束

網頁伺服器阻擋造成讀取錯誤

現在有些網站有許多理由會對一些網路爬蟲進行封鎖、阻擋,這就是 "反爬蟲機制" 。下列是一些常見的理由 :

  • 基於安全理由 : 特別是瞬間索取大量資料,常常會被視為是惡意程式。這時,可以放慢步伐,在爬蟲程式爬取文件的迴圈間放入 time.sleep() 的指令慢慢步法。在製作股票爬蟲時,常常執行幾次後,就無法再向網站請求,尤其是在公開資訊觀測站中,其問題發生的原因,是因為公開資訊觀測站的伺服器,將您的IP給禁止了,原因很簡單,您可能被誤認為「駭客」。
  • 不太想讓太多網路爬蟲造訪,造成網路流量增加,因此會設計程式阻擋網路爬蟲擷取資料。
  • 某些網站對於你存取資料受歡迎的,但是希望你可以遵循相關規範,如果沒有遵循規範將無法取得資料。
  • 有一些大型的網站,常常可以看到 robots.txt。在這個檔案內,會有一些規範,希望所有爬蟲程式或者瀏覽器可以遵循。主要內容是註明不要嘗試存取 disallowed 相關資料,這在將 後面 認識 robots.txt 會有更多的說明。 
以下是 Client Request 請求時,常出現的 HTTP 錯誤碼:
  • 400 Bad Request : 由於明顯的客戶端錯誤(例如,格式錯誤的請求語法,太大的大小,無效的請求訊息或欺騙性路由請求),伺服器不能或不會處理該請求。
  • 401 Unauthorized : 「未認證」,即使用者沒有必要的憑據。
  • 403 Forbidden : 伺服器已經理解請求,但是拒絕執行它。
  • 404 Not Found : 請求失敗,請求所希望得到的資源未被在伺服器上發現。
  • 405 Method Not Allowed : 請求行中指定的請求方法不被允許用於請求相應的資源。
  • 406 Not Acceptable : 該請求不可接受。
  • 408 Request Timeout : 請求逾時。

範例 pythonWebCrawler-12.py : 程式第6行 raise_for_status() ,主要是如果第5行 Response 物件 htmlfile 擷取網頁內容時出現錯誤,將可列出錯誤原因,觀察結果出現 錯誤碼 406 Not Acceptable 該請求不可接受。

# pythonWebCrawler-12.py
import requests

url = 'http://aaa.24ht.com.tw/'
htmlfile = requests.get(url)
htmlfile.raise_for_status()

執行結果 :

Traceback (most recent call last):
  File "f:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\pythonWebCrawler-12.py", line 6, in <module>
    htmlfile.raise_for_status()
  File "C:\Python\Python39\lib\site-packages\requests\models.py", line 953, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 406 Client Error: Not Acceptable for url: http://aaa.24ht.com.tw/

爬蟲程式偽裝成瀏覽器

其實我們使用 requests.get() 這個方法,到網路上去讀取網頁資料,這類程式就稱為網路爬蟲程式,甚至你也可以將各大公司所設計的搜尋引擎稱為網路爬蟲程式。為了解決爬蟲程式被伺服器阻擋的困擾,我們可以將所設計的爬蟲程式偽裝成瀏覽器,方法是在程式前端加上 表頭 headers 的內容。表投資料是一個 字典型式 的資料,最常見的表頭欄位是 “User-Agent”。 

表頭最主要的欄位是 "User-Agent" ,這是網路爬蟲程式或是瀏覽器校網站連結時表明身份,有些網站會先查詢此表頭欄位,以便確認這是瀏覽器發出的訊息,或是著名搜尋引擎公司會在此欄位表明自己身份。

範例 pythonWebCrawler-13.py : 爬蟲程式偽裝成瀏覽器。重點是第4-6行的敘述,其實這是一個 表頭headers 宣告,第4和第5行末端的反斜線 "\",主要表達下一行與這行是相同敘述,這是處理同一個敘述太長時分別撰寫,Python 會將4到6行視為同一行敘述。然後將第8行呼叫 requests.get() 時,第二個參數需要加入 “headers=headers” ,這樣這個程式就可以偽裝成遊覽器可以順利取得網頁資料。

# pythonWebCrawler-13.py
import requests

headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
            AppleWebKit/537.36 (KHTML, like Gecko) \
            Chrome/94.0.4606.61 Safari/537.36', }
url = 'https://www.twse.com.tw/zh/'
htmlfile = requests.get(url, headers=headers)
htmlfile.raise_for_status()
print("偽裝瀏覽器擷取網路資料成功")

執行結果 :

偽裝瀏覽器擷取網路資料成功

從上述,可以得到我們可以正常存取所要的資料了,未來還會擴充上述功能。其實 Python 程式偽裝成瀏覽器比想像的複雜,上述 headers 宣告碰上安全機制強大的網站也可能失效。

此外,在使用Google瀏覽器Chrome時,可以按 F12鍵 進入開發者模式,在此請按 Network 頁面標籤,Name 欄位 選取一個項目,點選 Header 頁面標籤,適度捲動下方視窗可以看到 User Agent,如下所示 :

認識 robots.txt

robots.txt 是一個儲存在網站根目錄下的文字格式檔案,因為在網路的 URL 習慣對於大小寫是敏感的,所以統一用小寫。這個檔案會告訴網路爬蟲,哪些內容不要去取得,或是哪些內容可以取得。 請參考 :  robots.txt用途與使用範例教學,釐清SEO收錄觀念!

提醒 robots.txt 並不是一個規範,而是長久被使用約定成俗的習慣。

解析 Robots.txt 幾個參數

基本會用的幾個參數分別如下:

  • User-agent => 定義下述規則對哪些搜尋引擎生效,即是對象。
  • Disallow => 指定哪些目錄或檔案類型不想被檢索,需指名路徑,否則將會被忽略。
  • Allow => 指定哪些目錄或檔案類型可能被檢索,需指名路徑,否則將會被忽略。
  • Sitemap => 指定網站內的sitemap檔案放置位置,需使用絕對路徑。

範例參考

  • 允許所有搜尋引擎檢索所有內容(通常建議使用)
    • User-agent: *
    • Disallow:
  • 拒絕所有搜尋引擎檢索所有內容(正式環境請避免使用)
    • User-agent: *
    • Disallow: /
  • 拒絕所有搜尋引擎檢索/members/底下所有內容。
    • User-agent: *
    • Disallow: /members/
  • 拒絕Google搜圖的爬蟲檢索/images/底下所有內容。
    • User-agent: Googlebot-image
    • Disallow:/images/
  • [萬用字元]拒絕所有搜尋引擎檢索網站內png為副檔名的圖檔。
    • User-agent: *
    • Disallow: *.png$
  • [萬用字元]拒絕Bing搜尋引擎檢索網站內/wp-admin目錄底下所有內容及網站內開頭為test的所有檔名。
    • User-agent: bingbot
    • Disallow: /wp-admin/
    • Disallow: ^test*

檢視Facebook公司 robots.txt 部分內容

儲存下載的網頁

使用 requests.get() 獲得的網頁內容,儲存在 response 物件類型內,如果要將這類型的物件存在硬碟內,需使用 response 物件的 iter_content() 方法。這個方法是採用重複迭代方式將 response 物件 內容寫入指定的檔案內,每次寫入指定磁區大小是 bytes 為單位,一般可以設定1024 x 5 或1024 x 10 或更多。

範例 pythonWebCrawler-14.py : 儲存下載的網頁。由於這個網頁檔案內容比較大,所以將每次寫入檔案大小設為40960 bytes,程式第12行所開啟的是以二進為可寫入 wb 方式開啟,這是為了怕網頁內容有 unicode 碼。程式第13-15行是一個迴圈,這個迴圈會將 Response 物件 htmlfile 以迴圈方式寫入。所開啟的 fileObj 最後是存入第11行設定的 "....."檔案內。程式第14行每次使用 write() 寫入 Response 物件時,會回傳所寫入網頁內容的大小,所以,第15行會列出每次迴圈所寫入的大小。

# pythonWebCrawler-14.py
import requests

url = 'https://www.twse.com.tw/zh/'                    # 網址
try:
    htmlfile = requests.get(url)
    print("下載成功")
except Exception as err:                                # err是系統自訂的錯誤訊息
    print("網頁下載失敗: %s" % err)
# 儲存網頁內容
fileName = 'output-pythonWebCrawler-14.txt'
with open(fileName, 'wb') as file_Obj:                  # 以二進位儲存
    for diskStorage in htmlfile.iter_content(40960):    # Response物件處理
        size = file_Obj.write(diskStorage)              # Response物件寫入
        print(size)                                     # 列出每次寫入大小
    print("以 %s 儲存網頁HTML檔案成功" % fileName)

執行結果 :

下載成功
40960
40960
13039
以 output-pythonWebCrawler-14.txt 儲存網頁HTML檔案成功

檢視網頁原始檔

之前教導讀者利用 requests.get() 取得網頁內容的原始HTML檔,其實也可以使用瀏覽器取得網頁內容的原始檔。檢視網頁的原始檔,目的不是要模仿設計相同的網頁,主要是掌握幾個關鍵重點的網頁結構,然後截取我們想要的資料。

有的網頁簡短,我們可以輕易檢視原始檔案獲得重要訊息,有動網頁複雜,我們需使用 chrome瀏覽器分析網頁。

Chrome瀏覽器為例

此例使用 Chrome 開啟網頁,在網頁內按一下滑鼠右鍵,出現快顯功能時,執行 View page source(檢視網頁原始檔)指令,就可以看到此網頁的原始HTML檔案了。有關如何使用Chrome瀏覽器解析網站,詳細說明請參考 :  使用 Chrome 開發者工具解析網站


參考資料

  • Requests - PyPI : Requests 允許您非常輕鬆地發送 HTTP/1.1 請求。無需手動向 URL 添加查詢字符串,或對 PUT 和 POST 數據進行表單編碼——但現在,只需使用 json 方法!
  • Python Requests :  https://docs.python-requests.org/ Requests是一個Python HTTP庫,在Apache許可證2.0下發行。這個專案的目標是使得HTTP請求更加簡單和更對常人友好。

特色、摘要,Feature、Summary:

關鍵字、標籤,Keyword、Tag:

  • Web-Crawler,Data-Mining,Data-Science,Python-Tutorial,

留言

這個網誌中的熱門文章

Ubuntu 常用指令、分類與簡介

iptables的觀念與使用

網路設定必要參數IP、netmask(遮罩)、Gateway(閘道)、DNS

了解、分析登錄檔 - log

Python 與SQLite 資料庫

Blogger文章排版範本

Pandas 模組

如何撰寫Shell Script

查詢指令或設定 -Linux 線上手冊 - man