滑動驗證碼 轉載:https://mp.weixin.qq.com/s/4cvr3mqKD0jkZNVDky4y7w
今天的主角是滑動驗證碼,現在有很多網站使用了極驗驗證碼來智慧反爬蟲,其中有一種是滑動驗證碼,具體來說就是拖動滑塊來拼合影象,若影象完全拼合,則驗證成功。下圖是B站的登入驗證碼,便是採用了極驗的滑動驗證碼,一起來看看如何破解吧!
先開啟B站的登入頁面,https://passport.bilibili.com/login,輸入賬號密碼之後點選登入便會彈出上述的滑動驗證碼。
將任務拆分有助於我們解決問題,解決這個滑動驗證碼我們可以分為這麼兩個步驟:
1)識別圖片缺口
2)模擬拖動滑塊
那麼就一步一步來吧~
圖片缺口識別
可以看到的是缺口圖的顏色與周圍有顯著不同,我們只需要拿到不含缺口的原圖進行對比就能夠找到這個缺口的座標。
開啟開發者工具,看原始碼上是否包含兩張圖片的url連結,這樣我們可以直接下載下來分析對比。不巧的是,我們沒有發現它的蹤跡。
但當我們將上圖原始碼中的類別為"geetest_canvas_fullbg geetest_fade geetest_absolute"的style設定為空,即可顯示沒有缺口的原圖。
知道如何獲得這兩張圖片之後,我們可以通過get_geetest_image函式來獲取滑動驗證碼的圖片,具體是用了 Selenium 工具選取圖片元素,然後得到其所在位置以及大小,隨後獲取整個網頁的截圖,再將這個滑動驗證碼從截圖中裁切出來。
def get_geetest_image(self,name,flag):
"""
獲得驗證碼圖片
"""
bottom,top,left,right=self.get_position(flag)
print("驗證圖片位置",bottom,top,left,right)
screenshot=self.get_screenshot()
captcha=screenshot.crop((left,bottom,right,top))
captcha.save(name)
return captcha
def get_position(self, flag):
"""
獲取驗證碼圖片位置
"""
img=self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "canvas.geetest_canvas_slice")))
time.sleep(2)
if flag:
# 執行js獲取不帶缺口的原圖
self.browser.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].setAttribute("style", "")')
else:
# 執行js,把缺口復原
self.browser.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].setAttribute("style", "opacity: 1; display: none;")')
location=img.location
print("圖片座標為:{}".format(location))
size=img.size
print("圖片大小為:{}".format(size))
bottom,top,left,right=location["y"],location["y"]+size["height"],location["x"],location["x"]+size["width"]
return (bottom,top,left,right)
我們通過selenium執行js程式碼,獲取不帶缺口的原圖和我們最先見到的有缺口的圖。
圖片獲取之後,來對比圖片各個畫素通道的差異來獲取缺口的位置就行。我們寬泛的認為,畫素相差在一定範圍內視為相同,畫素相差大於閾值視為發現缺口,便由此得到了缺口的座標資訊。
def get_gap(self, image1, image2):
"""
獲取缺口位置,通過比較畫素值
"""
for i in range(LEFT, image1.size[0]):
for j in range(image1.size[1]):
if not self.is_pixel_equal(image1,image2,i,j):
return i
return LEFT
def is_pixel_equal(self,image1,image2,x,y):
"""
畫素值比較,若三個通道均為出現超過閾值的變化,返回True
"""
pixel1=image1.load()[x, y]
pixel2=image2.load()[x, y]
if abs(pixel1[0]-pixel2[0])<THRESHOLD and abs(pixel1[1]-pixel2[1])<THRESHOLD and abs(pixel1[2]-pixel2[2])<THRESHOLD:
return True
else:
return False
加上LEFT這個偏移量,是因為帶缺口的圖還附帶了滑塊,我們需要將滑塊的長度範圍捨棄,即在滑塊的右側開始畫素的比較,這樣我們就可以得到缺口的位置了。
模擬拖動滑塊
要拖動滑塊我們需要先得到滑塊,通過簡單的selenium操作即可。
def get_slider(self):
"""
獲取滑塊
"""
return self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "geetest_slider_button")))
這裡需要用到簡單的高中物理知識,為了讓selenium模擬人的操作,我們需要將滑塊先加速運動,再減速運動,這樣會比較符合人的操作。我們採用勻加速和勻減速的方法,方便套用物理公式。模擬前4/5路程為勻加速路程,後1/5路程是勻減速路程,t是計算的時間間隔。
def get_track(self,distance):
"""
獲取滑塊移動軌跡的列表,distance是缺口的左側橫座標值
"""
track=[]
current=0
mid=distance*0.8
t=0.2
v=0
while current<distance:
if current<mid:
a=2.5
else:
a=-3.5
v0=v
v=v0+a*t
move=v0*t+ 0.5*a*t*t
current+=move
track.append(round(move))
return track
然後,我們只需要按照計算得到的運動軌跡操控小滑塊運動即可。下面是破解過程展示。