下載網頁使用 requests 模組
下載網頁使用 requests 模組
前言
本篇介紹如何在 Python 中使用 requests 模組建立各種 HTTP 請求,從網頁伺服器上取得想要的資料。
大綱
- requests 模組重要功能
- 安裝套件
- 使用方法
- requests 基本應用範例
- 搜尋網頁特定內容
- 下載網頁失敗的異常處理
- 網頁伺服器阻擋造成讀取錯誤
- 爬蟲程式偽裝成瀏覽器
- 認識 robots.txt
- 儲存下載的網頁
- 檢視網頁原始檔
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
使用方法
- 引入
- HTTP GET
- HTTP POST
- PUT, DELETE, HEAD, OPTIONS 請求
- 傳遞 URLs 參數 : 當 URLs 參數的 dictionary 裡可以把 list 當成一個項目,但不可以加入 None的項目。
- 回應物件 Response 分析 : 從回應中取出各種我們需要的資料
- 解析 JSON 資料 : 如果取得的是 json 格式資料,requests 有內建解析函式。
- 自訂 Header : 許多時候網站會擋掉 UA 是 python-request 的請求,因此我們很常需要自訂 Header。
- 設定 Timeout : 避免程式在維修中或故障的網站停留太久,或是用來檢查是否可存取時很方便。
- 指定編碼 : 通常網站會使用 UTF-8 編碼,但若不是,可用這個方法修改讀取編碼。
- 取得 Cookie :
- 修改 Cookie :
- 帳號密碼登入 : 若遇到需要帳號與密碼登入後才能看的網頁,可以使用 auth 參數指定帳號與密碼。
import requests
r = requests.get('https://api.github.com/events')
r = requests.post('http://httpbin.org/post', data = {'key':'value'})
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')
payload = {'key1': 'value1', 'key2': 'value2'} r = requests.get('http://httpbin.org/get', params=payload) print(r.url) #查看傳送的 URL
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
r = requests.get('https://api.github.com/events') r.json()
url = 'https://api.github.com/some/endpoint' headers = {'user-agent': 'Mozilla/5.0'} r = requests.get(url, headers=headers)
requests.get('http://github.com', timeout=[SECOND])
r.encoding = 'ISO-8859-1'
url = 'http://example.com/some/cookie/setting/url' r = requests.get(url) r.cookies['example_cookie_name']
url = 'http://httpbin.org/cookies' cookies = dict(cookies_are='working') r = requests.get(url, cookies=cookies) r.text
# 需要帳號登入的網頁 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 會有更多的說明。
- 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,
留言
張貼留言
Aron阿龍,謝謝您的留言互動!