[譯] 30 分鐘 Python 爬蟲教程

kezhenxu94發表於2018-05-15

一直想用 Python 和 Selenium 寫一個網頁爬蟲,但一直都沒去實現。直到幾天前我才決定動手實現它。寫程式碼從 Unsplash 網站上抓取一些漂亮的圖片,這看起來好像是非常艱鉅的事情,但實際上卻是極其簡單。

[譯] 30 分鐘 Python 爬蟲教程

圖片來源:Blake Connally 釋出於 Unsplash.com

簡單圖片爬蟲的原料

簡單圖片爬蟲的菜譜

以上的所有都安裝好了?棒!在我們繼續開始寫程式碼前,我先來解釋一下以上這些原料都是用來幹什麼的。

我們首先要做的是利用 Selenium webdrivergeckodriver 來為我們開啟一個瀏覽器視窗。首先,在 Pycharm 中新建一個專案,根據你的作業系統下載最新版的 geckodriver,將其解壓並把 geckodriver 檔案拖到專案資料夾中。Geckodriver 本質上就是一個能讓 Selenium 控制 Firefox 的工具,因此我們的專案需要它來讓瀏覽器幫我們做一些事。

接下來我們要做的事就是從 Selenium 中匯入 webdriver 到我們的程式碼中,然後連線到我們想爬取的 URL 地址。說做就做:

from selenium import webdriver
# 我們想要瀏覽的 URL 連結
url = "https://unsplash.com"
# 使用 Selenium 的 webdriver 來開啟這個頁面
driver = webdriver.Firefox(executable_path=r'geckodriver.exe')
driver.get(url)
複製程式碼

開啟瀏覽器視窗到指定的 URL。

[譯] 30 分鐘 Python 爬蟲教程

一個遠端控制的 Firefox 視窗。

相當容易對吧?如果以上所說你都正確完成了,你已經攻克了最難的那部分了,此時你應該看到一個類似於以上圖片所示的瀏覽器視窗。

接下來我們就應該向下滾動以便更多的圖片可以載入出來,然後我們才能夠將它們下載下來。我們還想再等幾秒鐘,以便萬一網路連線太慢了導致圖片沒有完全載入出來。由於 Unsplash 網站是使用 React 構建的,等個 5 秒鐘似乎已經足夠”慷慨”了,那就使用 Python 的 time 包等個 5 秒吧,我們還要使用一些 Javascript 程式碼來滾動網頁——我們將會用到 [window.scrollTo()](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo) 函式來實現這個功能。將以上所說的合併起來,最終你的程式碼應該像這樣:

import time
from selenium import webdriver

url = "https://unsplash.com"

driver = webdriver.Firefox(executable_path=r'geckodriver.exe')
driver.get(url)
# 向下滾動頁面並且等待 5 秒鐘
driver.execute_script("window.scrollTo(0,1000);")
time.sleep(5)
複製程式碼

滾動頁面並等待 5 秒鐘。

測試完以上程式碼後,你應該會看到瀏覽器的頁面稍微往下滾動了一些。下一步我們要做的就是找到我們要下載的那些圖片。在探索了一番 React 生成的程式碼之後,我發現了我們可以使用一個 CSS 選擇器來定位到網頁上畫廊的圖片。網頁上的佈局和程式碼在以後可能會發生改變,但目前我們可以使用 #gridMulti img 選擇器來獲得螢幕上可見的所有 <img> 元素。

我們可以通過 [find_elements_by_css_selector()](http://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.find_element_by_css_selector) 得到這些元素的一個列表,但我們想要的是這些元素的 src 屬性。我們可以遍歷這個列表並一一抽取出 src 來:

import time
from selenium import webdriver

url = "https://unsplash.com"

driver = webdriver.Firefox(executable_path=r'geckodriver.exe')
driver.get(url)

driver.execute_script("window.scrollTo(0,1000);")
time.sleep(5)
# 選擇圖片元素並列印出他們的 URL
image_elements = driver.find_elements_by_css_selector("#gridMulti img")
for image_element in image_elements:
    image_url = image_element.get_attribute("src")
    print(image_url)
複製程式碼

選擇圖片元素並獲得圖片 URL。

現在為了真正獲得我們找到的圖片,我們會使用 requests 庫和 PIL 的部分功能,也就是 Image。我們還會用到 io 庫裡面的 BytesIO 來將圖片寫到資料夾 ./images/ 中(在專案資料夾中建立)。現在把這些都一起做了,我們要先往每張圖片的 URL 連結傳送一個 HTTP GET 請求,然後使用 ImageBytesIO 來將返回的圖片儲存起來。以下是實現這個功能的其中一種方式:

import requests
import time
from selenium import webdriver
from PIL import Image
from io import BytesIO

url = "https://unsplash.com"

driver = webdriver.Firefox(executable_path=r'geckodriver.exe')
driver.get(url)

driver.execute_script("window.scrollTo(0,1000);")
time.sleep(5)
image_elements = driver.find_elements_by_css_selector("#gridMulti img")
i = 0

for image_element in image_elements:
    image_url = image_element.get_attribute("src")
    # 傳送一個 HTTP GET 請求,從響應內容中獲得圖片並將其儲存
    image_object = requests.get(image_url)
    image = Image.open(BytesIO(image_object.content))
    image.save("./images/image" + str(i) + "." + image.format, image.format)
    i += 1
複製程式碼

下載圖片。

這就是爬取一堆圖片所需要做的所有了。很顯然的是,除非你想隨便找些圖片素材來做個設計原型,否則這個小小的爬蟲用處可能不是很大。所以我花了點時間來優化它,加了些功能:

  • 允許使用者通過指定一個命令列引數來指定搜尋查詢,還有一個數值引數指定向下滾動次數,這使得頁面可以顯示更多的圖片可供我們下載。
  • 可以自定義的 CSS 選擇器。
  • 基於搜尋查詢關鍵字的自定義結果資料夾
  • 通過截斷圖片的預覽圖連結來獲得全高清圖片
  • 基於圖片的 URL 給圖片檔案命名。
  • 爬取最終結束後關閉瀏覽器。

你可以(你也應該)嘗試自己實現這些功能。全功能版本的爬蟲可以在這裡下載。記得要先按照文章開頭所說的,下載 geckodriver 然後連線到你的專案中。


不足之處,注意事項和未來優化項

整個專案是一個簡單的“驗證概念”,以弄清楚網頁爬蟲是如何做的,這也就意味著有很多東西可以做,來優化這個小工具:

  • 沒有致謝圖片最開始的上傳者是個很不好的做法。Selenium 肯定是有能力處理這種情況的,那麼每個圖片都帶有作者的名字。
  • Geckodriver 不應該被放在專案資料夾中,而是安裝在全域性環境下,並被放到 PATH 系統變數中。
  • 搜尋功能可以輕易地擴充套件到多個查詢關鍵字,那麼下載很多型別圖片地過程就可以被簡化了。
  • 預設瀏覽器可以用 Chrome 替代 Firefox,甚至可以用 PhantomJS 替代,這對這種型別的專案來說是更好的。

掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章