動態網頁與靜態網頁最大的不同是資料是在什麼時間點取得的,動態網頁是在瀏覽器已經取得 HTML 後,才透過 JavaScript 在需要時動態地取得資料。因此,爬蟲程式也必須要考慮動態取得資料這件事情,才有辦法正確地找到想要的資料。「滑動驗證碼(Slider Captcha)」是驗證碼機制當中常見的典型,也是防範爬蟲程式中一種難纏的對手。這一篇文章將會利用 Python 、opencv 與 Selenium 三個工具,示範如何拆解和模擬滑動驗證碼。

jQueryScript.net

常見的網頁驗證碼類型與原理

CAPTCHAs

你在瀏覽網頁的時候,有看過這些驗證機制嗎?網頁驗證碼的專業術語稱為「CAPTCHA 」(全名是 Completely Automated Public Turing test to tell Computers and Humans Apart 自動判別電腦與人類的公開圖靈測試),是目前在網頁當中常見的一種驗證機制,用來判斷惡意的使用者干擾與攻擊。目前常見的 CAPTCHA 方法有以下幾種:

  • 信件驗證碼/簡訊驗證碼
  • 圖形驗證碼
  • 問題驗證碼
  • 行為驗證碼

reCAPTCHA 計畫目前是由 Google 主要發展的驗證機制,最早是由 CMU 發起的 。reCAPTCHA 透過不同的情境讓人類回答,並藉此來幫助幫助文件數位化的進行。這個計畫將紙本掃描後無法被辨識文字顯示在問題中,讓人類在回答問題也能加以利用。

利用 Python 處理「滑動程式碼」的思路

而在「圖形驗證碼」當中,有一種常見的變形稱為「滑動驗證碼」。滑動驗證碼會動態的更新圖片與缺塊,並且要求使用者將缺塊移動到圖片中的特定位置才能通過判斷,如圖所示:

對於爬蟲的開發者而言,滑動驗證碼的確是一個蠻大的門檻。如何爬蟲程式可以讀得懂驗證碼,並且進一步模擬其行為都需要對網頁運作有一定的熟悉程度才行。接下來,就讓我們透過實作範例一起來體驗滑動程式碼的解決思路,我們可以分成兩個大區塊:

  • ① 利用 Python + opencv 拆解缺塊位置
  • ② 利用 Python + Selenium 模擬滑動行為

① 利用 Python + opencv 拆解缺塊位置

先手動把「背景圖」下載到本地端電腦,再試著利用圖像識別的方法試著找出位置。

(1)利用 cv2 將圖片讀取到程式中

OpenCV(Open Source Computer Vision Library)是用於電腦視覺的處理套件,在 Python 可以使用 opencv-python 與 cv2 進行安裝/載入。第一步,我們先利用 cv2.imread(…) 把圖片讀到程式中,並且利用 cv2.cvtColor 進行顏色轉換(原始圖片有誤差):

1
2
3
4
5
6
7
8
from matplotlib import pyplot as plt
import cv2

path = '~/Downloads/filename.png'
image = cv2.imread(path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Converting BGR to RGB
plt.imshow(image)
plt.show()

(2)判斷圖片中的物體邊緣輪廓

Canny 是圖形識別中用來作邊緣偵測(Edge Detection)的方法,細節的參數細節可以看官方文件。Canny 方法能將原始圖片轉成灰階之後,輸出包含邊緣範圍的黑白影像:

1
2
3
canny = cv2.Canny(image, 300, 300)
plt.imshow(canny)
plt.show()

可以從結果中看到除了區塊之後,也包含很多小的零散的區塊部分:

(3)取出缺塊所在的位置

接下來利用 cv2.findContours() 找出圖片中所有偵測到的區塊,把他標記成藍色的部分畫出來。從長度(w)跟寬度(h)可以判斷出哪一個區塊是圖片中真正的區塊,但圖片顏色太接近的情況下會增加判斷的難度。

1
2
3
4
5
6
7
8
9
10
11
contours, hierarchy = cv2.findContours(canny, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)  
dx, dy = 0, 0
for i, contour in enumerate(contours):
x, y, w, h = cv2.boundingRect(contour)
if (w > 50) and (h > 50):
dx = x
dy = y
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)

plt.imshow(image)
plt.show(

最終就可以得到缺塊所在的位置是 dx 和 dy 兩個變數:

1
dx, dy # (202, 15)

以這個例子來說,我們就可以得知缺塊距離最左邊距離 202 px 的偏移量。

② 利用 Python + Selenium 模擬滑動行為

第二段,我們會利用 Selenium 瀏覽器測試工具幫助我們「模擬使用者移動方塊」的行為。關於 Selenium 的動態網頁模擬,之前也有寫過這一篇分享文。

(1)打開瀏覽器前往網頁,下載原始圖片

先觀察一下網頁的組成,發現給定的範例網站是利用 Canvas 動態載入圖片實現滑動驗證碼的效果的:

接下來利用 Selenium 打開瀏覽器跳轉到網頁中,利用 JavaScript 的先將 Canvas 轉成圖片後再進行下載 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome('./chromedriver')

js_download_code = '''
var link = document.createElement('a');
link.download = 'filename.png';
link.href = document.getElementById('captcha').getElementsByTagName("canvas")[0].toDataURL()
link.click();
'''

browser.execute_script(js_download_code)

這麼做就可以把原始圖片下載下來,搭配「① 利用 Python + opencv 拆解缺塊位置」可以得知需要移動的偏移量。

找出按鈕元素

透過觀察網頁的結構,可以明確的找出需要被移動的方塊是位在 class = jigsaw__slider–ihcNg 的 div 元素(具體的找法,可以參考這一篇文章):

使用 Selenium 輕鬆選取:

1
btn = browser.find_element_by_class_name("jigsaw__slider--ihcNg")

(2)模擬使用者拖拉方塊行為

最後一步,就可以利用剛剛找出來缺塊的位置及瀏覽器模擬的工具,把方塊移動到指定的偏移位置:

1
2
3
4
move = ActionChains(browser)
move.click_and_hold(btn)
move.move_by_offset(dx, 0)
move.perform()

實戰!讓 Python 爬蟲也能讀得懂「滑動驗證碼」

在這個例子當中,除了實作滑動驗證碼的爬蟲之外,也弄了一個簡單的滑動驗證碼作為標的。最後的成果大概像這樣,從「打開網頁」 → 「下載圖片」 → 「解析位置」 → 「模擬滑動」的整個過程:

網頁爬蟲是資料收集的一種手法,不過面對於網頁技術的變化,爬蟲程式也有不同的應對策略。如果你在爬蟲實作上還遇到什麼問題,也歡迎留言分享你的觀察與解法 😃😃😃


Reference

[1] 驗證碼
[2] Captcha验证码有哪些分类?有什么作用?
[3] 使用 Python + Selenium 破解滑块验证码



嗨,你好,我是維元,持續在不同的平台發表對 #資料科學、 #網頁開發 或 #軟體職涯 相關的文章。如果對於內文有疑問都歡迎與我們進一步的交流,都可以追蹤我的 Facebook 粉專:資料科學家的工作日常 ,也會不定時的舉辦分享活動,一起來玩玩吧 ヽ(●´∀`●)ノ

在大數據時代下,資料收集與程式爬蟲你已經是基本的數位技能!最近正在計畫【超新手也能用 Python 爬蟲打造貨比千家的比價網站】的線上課程,實現一鍵極速收集海量資料,手把手帶你打造比價網站。​從資料收集、資料整理到最終的部屬展示,將海量數據轉化為可視化的圖表,並結合網站實作,實現真正的落地運用​ 🚀🚀🚀


📍 課程募資優惠中: https://pse.is/3hq9sp
📍 課程募資優惠中: https://pse.is/3hq9sp
📍 課程募資優惠中: https://pse.is/3hq9sp


License


本著作由Chang Wei-Yaun (v123582)製作,
創用CC 姓名標示-相同方式分享 3.0 Unported授權條款釋出。