[Python網頁爬蟲]網站圖片擷取-1

Sean Yeh
Python Everywhere -from Beginner to Advanced
11 min readAug 9, 2022

--

Mount Hehuan, Taiwan , photo by Sean Yeh

我們為什麼需要爬取網路上的資料?如果你需要分析資料或者是訓練機器學習(Machine Learning)模型時,常常需要提供大量的資料來進行資料的分析與模型之建構。就以進行有關圖像辨識技術的建模時,要讓電腦可以辨識出圖片中的內容,就需要輸入大量的圖片,讓電腦進行機器學習。當本身沒有這麼多資料時,唯有搜集其他資料才能快速的累積上述工作所需的資料量。在這樣的背景下,如何快速又自動地幫助我們搜集大量的資料就變得很重要了。

以前述的圖像辨識命題來說,就可以透過Python爬蟲技術來自動的蒐集網路上的圖片,進而輸入機器中學習。

想在網路上進行爬蟲活動並不限於使用Python,但Python這語言中已經有許多好用的爬蟲工具,舉例來說,使用requests或urllib等套件去請求(request)網站,並得到網站HTML的回應(response)後,再使用BeautifulSoup解析網頁的結構,把資料抽取出來。requests和urllib模組也可以用來下載圖片檔案,在此我們要示範使用這兩個模組下載網路上的圖檔。

requests模組

requests模組可以用Python程式發出HTTP請求,取得指定網站的內容,我們可透過對特定網站發出請求,並且取回該網站的內容,非常的方便。在使用requests模組前需要先進行安裝。

安裝

我們可以透過使用pip(pip install)在Python的虛擬環境中安裝requests模組。

$ pip install requests

匯入模組

透過上述簡單的pip安裝方式,安裝完requests模組後,就可以在Python程式中匯入模組。匯入的指令如下:

import requests

透過requests模組送出請求的方法有二:GET請求與POST請求。以下分別說明之:

GET請求

平時當我們打開瀏覽器後,會輸入網址(例如:www.google.com)送出並期待指定的網站頁面呈現在眼前。當該網站的伺服器接收到您瀏覽器的要求後,會回應相應的內容給瀏覽器。前述的請求方式即被稱作GET請求。

requests模組可以不透過瀏覽器做到前述的GET請求。要做到GET請求,需要透過get()方法送出請求。就如下面的程式碼一樣,get()方法的參數是url網址,而res變數則是回應的Response物件。

import requests
url='http://www.abc.com'
res = requests.get(url)

因此,簡單的說GET請求的語法為:

import requests
Response物件 = requests.get(網址)

#Response物件屬性

get()方法取得Response物件後,可以使用下面的屬性來取得傳回的不同回應內容。

  • text :網頁原始碼的HTML標籤字串
  • contents:網站二進位檔案資料
  • encoding:HTML標籤字串的編碼
  • status_code :取得HTTP狀態碼。狀態碼又可分為:200,表示請求成功;開頭為4,表示使用者端發生錯誤(例如鼎鼎大名的404找不到網頁);開頭為5表示伺服器端發生錯誤。

既然有了status_code狀態碼,我們可以利用Response物件返回的狀態碼來判斷GET請求的成敗。實務上的寫法:

if res.status_code == 200:
# HTTP請求成功
else:
# HTTP請求失敗

POST請求

POST請求也是HTTP請求中的一種常見的請求方式。當網頁中有讓使用者填寫表單資料的地方,通常會以POST請求的方式進行傳送。

在requests模組中,也可以進行POST請求。要進行POST請求,可以使用post()函式。使用方式如下:

import requests
url='http://www.abc.com'
data = {'key1':'value1','key2':'value2'}
res = requests.post(url, data=data)

其傳遞的參數要定義在字典資料型態(如上例中的data)。不過,在這裡我們應該不會使用到POST請求。

使用requests下載圖檔

若要使用requests下載圖檔,可以使用get()的方式透過requests送出HTTP請求。

requests可以開啟串流來下載圖檔。 requests.get() 中有兩個參數,第一個參數為圖檔的url路徑;第二個參數則表示回應的方式為串流( stream=True )。

res = requests.get(url, stream=True)

為確認HTTP請求是否成功,可以使用 if statement 來進行判斷。若判斷的結果為HTTP請求成功,就可以開始下載檔案。

if res.status_code == 200:

在此使用with open的語法來開啟檔案,並以二進位的方式寫入檔案(這裡open函式使用的是 wb 而非w )。接著透過for迴圈讀取res返回的串流資料chunck,且呼叫write函式來寫入檔案:

with open(path,'wb') as file_path:
for chunck in res:
file_path.write(chunck)

再加上一些print作為提示後,完整程式碼如下:

import requests
url = 'http://圖片路徑'
path = '圖檔'
res = requests.get(url, stream=True)
if res.status_code == 200:
with open(path,'wb) as file_path:
for chunck in res:
file_path.write(chunck)
print("The Image has been downloaded")
else:
print("Error!! HTTP Request failed")

使用urllib下載圖檔

除了使用requests模組外,也可以使用urllib模組下載檔案。urllib模組也會送出HTTP請求並下載圖片檔案。此外,為增加圖檔的下載效率,在Python可以使用緩衝區的方式進行圖檔下載。

匯入模組

使用urllib模組前需要匯入urllib中的request模組。

import urllib.request

匯入模組後,再呼叫urlopen函式來送出HTTP請求。並以二進位的方式寫入檔案(這裡open函式使用的是 wb 而非w )。

import urllib.requesturl = 'http://圖片路徑'
path = '圖檔'

res = urllib.request.urlopen(url)
file_path = open(path,'wb')
info = res.read()
file_path.write(info)
file_path.close()
res.close()

逐步下載

若擔心圖檔太大無法一次成功下載時,可以分批一次次部分下載資料。要分批下載,我們需要修改上面的程式碼如下:

import urllib.requesturl = 'http://圖片路徑'
path = '圖檔'
res = urllib.request.urlopen(url)
file_path = open(path,'wb')
size = 0
while True:
info = res.read(10000)
if len(info) < 1:
break
size = size + len(info)
file_path.write(info)**
print('已下載',size)
file_path.close()
res.close()

由上面的程式可以看到,這裡透過了while迴圈,每次下載檔案的一部分(這裡設定的是10000個字元)。

實作:下載圖檔

以下分別採用上述兩種方式下載檔案。

1. 使用requests模組下載

以使用requests模組下載為例,我們要到免費的圖片網站unsplash 試著下載一張圖片。

首先,打開您的瀏覽器,進入unsplash 網站。並且輸入關鍵字,我們在這裡輸入mountains關鍵字查詢圖片。

接著,你會看到所有關於mountains的圖片,可以從中間選定一張來作為下載測試。我們在這裡要選擇畫面中間的那一張圖。

我們需要找到該圖片的url路徑。

找路徑的方法很簡單,只要按下滑鼠的右鍵,選擇「複製圖片位址」,就可以把該圖片的路徑複製起來。

接著將複製的圖片路徑,貼在下面程式中url的地方,並且在path 的地方取一個名稱,作為待會兒下載圖片要儲存的名稱。

import requestsurl = 'https://images.unsplash.com/photo-1610555356070-d0efb6505f81?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bW91bmF0aW5zfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=800&q=60'path = 'unsplash.jpg'res = requests.get(url, stream=True)if res.status_code == 200:
with open(path,'wb') as file_path:
for chunck in res:
file_path.write(chunck)
print("The Image has been downloaded")
else:
print("Error!! HTTP Request failed")

設定完畢之後,就可以執行上面的程式碼了。程式執行完畢,您的電腦中應該會出現一個unsplash.jpg的檔案。打開檔案,看看是不是與上面的圖片一致?

2.使用urllib模組下載

第二種方式是使用urllib模組來下載圖片,為免圖檔太大,在此使用上述的第二種urllib下載方式(即一次下載10000)。

跟第一種方式一樣,我們需要將複製的圖片路徑,貼在下面程式中url的地方,並且在path 的地方指定一個名稱,作為下載之圖片所儲存的檔名。

import urllib.requesturl = 'https://images.unsplash.com/photo-1610555356070-d0efb6505f81?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bW91bmF0aW5zfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=800&q=60'path = 'unsplash.jpg'res = urllib.request.urlopen(url)file_path = open(path,'wb')size = 0
while True:
info = res.read(10000)
if len(info) < 1:
break
size = size + len(info)
file_path.write(info)
print('已下載',size)
file_path.close()
res.close()

設定完path與url之後,就可以執行上面的程式碼。程式執行完畢後,在您的電腦中,應該也會出現一個unsplash.jpg的檔案。

結語

以上的示範,實際上只是為了告訴大家,透過程式碼可以把檔案下載下來。上述的程式碼,僅只是一個簡化過範例的罷了。

因為在現實世界中,為了達成上述目的而寫這個程式實屬無必要。實際上,我們可以在前面「按下滑鼠的右鍵」的步驟時,直接選擇「另存圖片」就好了。

若真的要讓我們寫的程式在現實世界發揮功用的話,就需要再這之上加上 selenium、BeautifulSoup 等套件,並且改寫程式。讓程式可以自行打開瀏覽器,或者輸入我們希望的關鍵字,或者把所有滿足關鍵字查詢條件的圖片,逐一的下載下來。如此修正後,與前面那看似「跛腳」的程式樣本比起來,會讓你感覺到稍具實用性。在下一篇我們將進行上述的修正。

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。