使用 urllib 模組下載網頁

使用 urllib 模組下載網頁

前言

urllib模組是 Python 內建下載網頁資訊的模組,它包含下列4個主要模組:

  • request 模組 : 可開啟指定網址的HTML文件內容。
  • parse 模組 : 可解飢 URL。
  • error 模組 : 內有 urllib.request() 物件發生錯誤或異常的原因。
  • robotparse 模組 : 可議解析 robots.txt 文件。

大綱

urllib.request 打開和讀取URL

urllib.request 模塊定義了適用於在各種複雜情況下打開 URL(主要為 HTTP)的函數和類別, 例如基本認證、摘要認證、重定向、cookies 及其它。

範例 pythonUrllib-01.py : 利用 urllib.request.urlopen() 讀取網頁,印出所回傳的的資料形態與內容

# pythonUrllib-01.py
import urllib.request

url = 'https://tw.finance.yahoo.com/'
htmlfile = urllib.request.urlopen(url)
print(type(htmlfile))
print(htmlfile)

執行結果

<class 'http.client.HTTPResponse'>
<http.client.HTTPResponse object at 0x00000249F14D4A60>

範例 pythonUrllib-02.py : 利用 urlopen() 讀取網頁,回傳的的資料形態為 http.client.HTTPResponse,可使用 read()讀取,但是中文內容以二進位顯示

# pythonUrllib-02py
import urllib.request

url = 'https://tw.finance.yahoo.com/'
htmlfile = urllib.request.urlopen(url)
print(htmlfile.read())

執行結果

b'<!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\xe5\xa5\x87\xe6\x91\xa9\xe8\x82\xa1\xe5\xb8\x82\xe6\x8f\x90\xe4\xbe\x9b\xe5\x9c\x8b\xe5\x85\xa7\xe5\xa4\x96\xe8\xb2\xa1\xe7\xb6\x93\xe6\x96\......

範例 pythonUrllib-03.py : 延續 範例 pythonUrllib-02.py,但是中文內容以二進位顯示,使用 decode('utf-8') 方法處理。

# pythonUrllib-03.py
import urllib.request

url = 'https://tw.finance.yahoo.com/'
htmlfile = urllib.request.urlopen(url)
print(htmlfile.read().decode('utf-8'))

執行結果

<!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奇摩股市提供國內外財經新聞,台股、期貨、選擇權、國際指數
、外匯、港滬深股、美股等即時報價資訊,以及自選股、......

urlopen(url, timeout=30) 另外有個參數 timeout  預設是 20秒,開啟網頁逾時會跳出異常。

http.client.HTTPResponse 物件屬性

從上述,我們了解開啟網頁後會傳回 http.client.HTTPResponse 物件,接著我們來介紹幾個此物件常用的屬性。

  • HTTPResponse.version : 版本編號。
  • TTPResponse.url : 物件的 URL。 (HTTPResponse.geturl(),3.9 版後已棄用)
  • HTTPResponse.status : 由服務器返回的狀態碼。(HTTPResponse.getstatus() 3.9 版後已棄用)
  • HTTPResponse.getheaders() : 取得用 (header, value) 元組構成的串列方式保存的表頭 header 內容。

範例 pythonUrllib-04.py : http.client.HTTPResponse 物件常用的屬性。

# pythonUrllib-04.py
import urllib.request

url = 'https://tw.finance.yahoo.com/'
htmlfile = urllib.request.urlopen(url)
print('版本 : ', htmlfile.version)
print('網址 : ', htmlfile.geturl())
print('下載 : ', htmlfile.status)
print('表頭 : ')
for header in htmlfile.getheaders():
    print(header)

執行結果

版本 :  11
網址 :  https://tw.finance.yahoo.com/
下載 :  200
表頭 :
('expect-ct', 'max-age=31536000, report-uri="http://csp.yahoo.com/beacon/csp?src=yahoocom-expect-ct-report-only"')
('referrer-policy', 'no-referrer-when-downgrade')
('strict-transport-security', 'max-age=31536000')......

使用urllib.request.urlretrieve()下載圖片

範例 pythonUrllib-05.py : 使用urllib.request.urlretrieve()下載圖片。

# pythonUrllib-05.py
import urllib.request

url_pict = 'http://www.python.org/images/success/nasa.jpg'
fn = 'output-pythonUrllib-05.png'
pict = urllib.request.urlretrieve(url_pict,fn)

執行結果

產生一個圖檔 output-pythonUrllib-05.png

urllib.parse 用於解析 URL

urllib.parse模組對資料內容進行格式處理,有以下幾個方法

  • urllib.parse.quote(url):(URL編碼處理)主要對URL中的非ASCII碼編碼處理
  • urllib.parse.unquote(url):(URL解碼處理)URL上的特殊字元還原
  • urllib.parse.urlencode(data):對請求資料data進行格式轉換

我們用以下範例說明,詳細說明請參閱 Python 文件: urllib.parse 用於解析 URL

範例 pythonUrllib-06.py : urllib.parse模組,中文的不同編碼方式 : URL編碼 和 UTF-8 。

# pythonUrllib-06.py
from urllib import parse

s = '台灣積體電路製造'
url_code = parse.quote(s)
print('URL編碼  : ', url_code)
code = parse.unquote(url_code)
print('中文編碼 : ', code)

執行結果

URL編碼 : %E5%8F%B0%E7%81%A3%E7%A9%8D%E9%AB%94%E9%9B%BB%E8%B7%AF%E8%A3%BD%E9%80%A0
中文編碼 : 台灣積體電路製造

範例 pythonUrllib-07.py : urllib.parse模組的6大組件 : scheme : URL協議, netloc : 網絡位置, path : 分層路徑, params : 最後路徑元素的參數, query : 查詢組件, fragment : 片段識別 。

# pythonUrllib-07.py
from urllib import parse

url = 'https://docs.python.org/3/search.html?q=parse&check_keywords=yes&area=default'
parse.urlparse = parse.urlparse(url)
print(type(parse.urlparse))
print(parse.urlparse)
print('scheme   = ', parse.urlparse.scheme)
print('netloc   = ', parse.urlparse.netloc)
print('path     = ', parse.urlparse.path)
print('params   = ', parse.urlparse.params)
print('query    = ', parse.urlparse.query)
print('fragment = ', parse.urlparse.fragment)

執行結果

<class 'urllib.parse.ParseResult'>
ParseResult(scheme='https', netloc='docs.python.org', path='/3/search.html', params='', query='q=parse&check_keywords=yes&area=default', fragment='')
scheme   =  https
netloc   =  docs.python.org
path     =  /3/search.html
params   =
query    =  q=parse&check_keywords=yes&area=default
fragment =.....

範例 pythonUrllib-08.py : 另外相似的模組 parse.urlsplit(url) 和 parse.urlparse(url) 最大不同是回傳部分沒有 params 元素。

# pythonUrllib-08.py
from urllib import parse

url = 'https://docs.python.org/3/search.html?q=parse&check_keywords=yes&area=default'
urp = parse.urlsplit(url)
print(type(urp))
print(urp)
print('scheme   = ', urp.scheme)
print('netloc   = ', urp.netloc)
print('path     = ', urp.path)
print('query    = ', urp.query)
print('fragment = ', urp.fragment)

執行結果

<class 'urllib.parse.SplitResult'>
SplitResult(scheme='https', netloc='docs.python.org', path='/3/search.html', query='q=parse&check_keywords=yes&area=default', fragment='')
scheme   =  https
netloc   =  docs.python.org
path     =  /3/search.html
query    =  q=parse&check_keywords=yes&area=default
fragment =...

範例 pythonUrllib-09.py : parse.urlsplit(url) 合成URL方法為 parse.urlunsplit(url),parse.urlparse(url) 合成URL方法為 parse.urlunparse(url)。

# pythonUrllib-09.py
from urllib import parse

scheme = 'https'
netloc  = 'docs.python.org'
path = '/3/search.html'
params = ''
query = 'q=parse&check_keywords=yes&area=default'
frament = ''
url_unparse = parse.urlunparse((scheme,netloc,path,params,query,frament))
print(url_unparse)
url_unsplit = parse.urlunsplit([scheme,netloc,path,query,frament])
print(url_unsplit)

執行結果

https://docs.python.org/3/search.html?q=parse&check_keywords=yes&area=default
https://docs.python.org/3/search.html?q=parse&check_keywords=yes&area=default

範例 pythonUrllib-10.py : 使用 parse.urlencode() 方法,將字典格式的資料轉化為網頁網址。

# pythonUrllib-10.py
from urllib import parse

url_python = 'https://docs.python.org/3/search.html?'
query = {
         'q':'parse',
         'check_keywords':'yes',
         'area':'default'}
url = url_python + parse.urlencode(query)
print(url)

執行結果

https://docs.python.org/3/search.html?q=parse&check_keywords=yes&area=default

範例 pythonUrllib-11.py : 使用 parse.parse_qs() 方法,將網頁網址轉化為字典格式的資料。

# pythonUrllib-11.py
from urllib import parse

query_str = 'q=parse&check_keywords=yes&area=default'
print('parse.parse_qs  = ', parse.parse_qs(query_str))
print('parse.parse_qsl = ', parse.parse_qsl(query_str))

執行結果

parse.parse_qs = {'q': ['parse'], 'check_keywords': ['yes'], 'area': ['default']}
parse.parse_qsl = [('q', 'parse'), ('check_keywords', 'yes'), ('area', 'default')]

urllib.error 異常處理

爬取網頁異常時,可以使用 urllib.error 模組處理 urllib.request() 模組產生的異常, URLError是OSError的一個子類,HTTPError是URLError的一個子類,服務器上HTTP的響應會返回一個狀態碼,根據這個HTTP狀態碼,我們可以知道我們的訪問是否成功。以下範例來說明使用的方式:

URLError 類別

範例 pythonUrllib-12.py : 使用錯誤網址與正確網址來觀察 URLError 類別的回應。

# pythonUrllib-12.py
from urllib import request, error

headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64)\
            AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101\
            Safari/537.36', }
# 錯誤網址
url_error = 'http://aaa.24t.com.tw/'            # 錯誤網址
try:
    htmlfile = request.urlopen(url_error)
except error.URLError as e:
    print('錯誤原因 : ', e.reason)
else:
    print("擷取網路資料成功")
# 正確網址
url = 'http://aaa.24ht.com.tw/'                 # 網址正確
try:
    req = request.Request(url, headers=headers)
    htmlfile = request.urlopen(req)
except error.URLError as e:
    print('錯誤原因 : ', e.reason)
else:
    print("擷取網路資料成功")

執行結果

錯誤原因 : [Errno 11001] getaddrinfo failed
擷取網路資料成功

HTTPError 類別

範例 pythonUrllib-13.py : 延續 範例 pythonUrllib-12.py 觀察 URLError 和 HTTPError 類別的回應。

# pythonUrllib-13.py
from urllib import request, error

headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64)\
            AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101\
            Safari/537.36', }
# 錯誤1
url_error = 'http://aaa.24t.com.tw/'            # 錯誤網址
try:
    htmlfile = request.urlopen(url_error)
except error.HTTPError as e:
    print('錯誤代碼 : ', e.code)
    print('錯誤原因 : ', e.reason)
    print('回應表頭 : ', e.headers)
except error.URLError as e:
    print('錯誤原因 : ', e.reason)
else:
    print("擷取網路資料成功")
print('-'*70)
# 錯誤2
url = 'http://aaa.24ht.com.tw/'                 # 網址正確
try:
    htmlfile = request.urlopen(url)
except error.HTTPError as e:
    print('錯誤代碼 : ', e.code)
    print('錯誤原因 : ', e.reason)
    print('回應表頭 : ', e.headers)    
except error.URLError as e:
    print('錯誤原因 : ', e.reason)    
else:
    print("擷取網路資料成功")
print('-'*70)
# 正確
url = 'http://aaa.24ht.com.tw/'                 # 網址正確
try:
    req = request.Request(url, headers=headers)
    htmlfile = request.urlopen(req)
except error.HTTPError as e:
    print('錯誤代碼 : ', e.code)
    print('錯誤原因 : ', e.reason)
    print('回應表頭 : ', e.headers)    
except error.URLError as e:
    print('錯誤原因 : ', e.reason)
else:
    print("擷取網路資料成功")

執行結果

錯誤原因 :  [Errno 11001] getaddrinfo failed
----------------------------------------------------------------------
錯誤代碼 :  406
錯誤原因 :  Not Acceptable
回應表頭 :  Date: Mon, 04 Oct 2021 04:47:50 GMT
Server: Apache
Accept-Ranges: bytes
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html
----------------------------------------------------------------------
擷取網路資料成功

urllib.robotparser 用於解析 robots.txt 文件

urllib.robotparser 主要就是讀取網站的 robots.txt,詳細說明,請參考 : 認識 robots.txt

範例 pythonUrllib-14.py : urllib.robotparser 使用範例。

# pythonUrllib-14.py
import urllib.robotparser
rp = urllib.robotparser.RobotFileParser()
rp.set_url("http://www.musi-cal.com/robots.txt")
rp.read()
print(rp.can_fetch("*", "http://www.musi-cal.com/cgi-bin/search?city=San+Francisco"))
print(rp.can_fetch("*", "http://www.musi-cal.com/"))

執行結果

True
True

參考資料

特色、摘要,Feature、Summary:

關鍵字、標籤,Keyword、Tag:

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

留言

這個網誌中的熱門文章

Ubuntu 常用指令、分類與簡介

iptables的觀念與使用

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

了解、分析登錄檔 - log

Python 與SQLite 資料庫

Blogger文章排版範本

Pandas 模組

如何撰寫Shell Script

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

下載網頁使用 requests 模組