python爬蟲21 | 對於b站這樣的滑動驗證碼,很差意思,照樣自動識別

今天javascript

 

要來講說滑動驗證碼了php

 

你們應該都很熟悉css

 

點擊滑塊而後移動到圖片缺口進行驗證html

 

 

如今愈來愈多的網站使用這樣的驗證方式java

 

爲的是增長驗證碼識別的難度python

 

 

那麼ios

 

對於這種驗證碼web

 

應該怎麼破呢apache

 

接下來就是ruby

 

學習 python 的正確姿式

 

 

打開 b 站的登陸頁面

 

https://passport.bilibili.com/login

 

 

能夠看到登陸的時候須要進行滑塊驗證

 

按下 F12

 

進入 Network

 

看下咱們將滑塊移到缺口鬆開以後作了什麼提交

 

 

能夠看到是一個 GET 請求

 

可是

 

這請求連接也太特麼長了吧

 

就是比小帥b短了一點點

 

 

咱們來看看請求的參數是怎麼樣的

 

 

哇靠

 

gt?

 

challenge?

 

w?

 

這些都是什麼鬼參數

 

還加密了

 

徹底下不了手啊

 

 

那麼

 

本篇完

 

再見

 

peace

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你是否是迷戀我??

 

 

好吧

 

你竟然滑到這裏來了

 

說明你仍是有點小帥b的

 

小帥b是那種遇到一點困難就放棄的人嗎

 

顯然不是

 

那麼接下來纔是真的

 

學習 python 的正確姿式

 

 

既然以請求的方式很差弄

 

咱們從它們的源代碼入手

 

看看有什麼突破口

 

 

回到 b 站的登陸頁

 

按下 F12

 

進入 Element

 

而後點擊滑塊出現了圖片

 

定位一下

 

 

發現有兩個 a 標籤

 

一個 class 是 gt_bg gt_show

 

一個 class 是 gt_fullbg gt_show

 

和小帥b想的同樣

 

這個驗證碼應該是有兩張圖片

 

一張是徹底的背景圖片

 

一張是缺口的圖片

 

那把這兩張圖片下載下來對比一下不就好了

 

打開 a 標籤一看

 

 

哇靠

 

一張圖片被切割成不少小塊

 

原來這張圖片是拼出來的

 

咱們看看原始圖片是怎麼樣的

 

 

什麼亂七八糟的

 

再仔細看下源代碼

 

原來是在同一張圖片經過偏移量合成了一張完整的圖片

 

background-position: -277px -58px;

 

厲害厲害

 

小帥b看了一下缺口的圖片也是如此

 

 

 

到這裏

 

咱們的第一個思路就是

 

下載這兩張原始圖片

 

而後經過偏移量合成兩張真正的圖片

 

背景圖

 

 

↓變身

 

 

 

缺口圖

 

 

↓變身

 

 

那麼怎麼作呢?

 

由於咱們還要模擬滑動滑塊

 

因此呢

 

咱們要用到 selenium

 

打開b站的登陸頁

 

而後等到那個滑塊顯示出來

 

 # 獲取滑塊按鈕 driver.get(url) slider = WAIT.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))

 

接下來咱們就獲取頁面的源碼

 

driver.page_source

 

而後使用 bs 獲取兩張原始背景圖片的 url 

 

 bs = BeautifulSoup(driver.page_source,'lxml') # 找到背景圖片和缺口圖片的div bg_div = bs.find_all(class_='gt_cut_bg_slice') fullbg_div = bs.find_all(class_='gt_cut_fullbg_slice')
# 獲取缺口背景圖片url bg_url = re.findall('background-image:\surl\("(.*?)"\)',bg_div[0].get('style'))    # 獲取背景圖片url fullbg_url = re.findall('background-image:\surl\("(.*?)"\)',fullbg_div[0].get('style'))

 

拿到了圖片地址以後

 

將圖片下載下來

 

 # 將圖片格式存爲 jpg 格式 bg_url = bg_url[0].replace('webp', 'jpg') fullbg_url = fullbg_url[0].replace('webp', 'jpg') # print(bg_url) # print(fullbg_url)
# 下載圖片 bg_image = requests.get(bg_url).content fullbg_image = requests.get(fullbg_url).content print('完成圖片下載')

 

ok 

 

 



 

咱們已經把兩張原始圖片下載下來了

 

 

那麼接下來就是要合成圖片了

 

咱們要根據圖片的位置來合成

 

也就是源碼中的 background-position

 

 

獲取每個小圖片的位置

 

咱們能夠經過字典的形式來表示這些位置

 

而後將數據放到列表中

 

 # 存放每一個合成缺口背景圖片的位置 bg_location_list = []    # 存放每一個合成背景圖片的位置 fullbg_location_list = []
for bg in bg_div: location = {} location['x'] = int(re.findall('background-position:\s(.*?)px\s(.*?)px;', bg.get('style'))[0][0]) location['y'] = int(re.findall('background-position:\s(.*?)px\s(.*?)px;', bg.get('style'))[0][1]) bg_location_list.append(location)
for fullbg in fullbg_div: location = {} location['x'] = int(re.findall('background-position:\s(.*?)px\s(.*?)px;', fullbg.get('style'))[0][0]) location['y'] = int(re.findall('background-position:\s(.*?)px\s(.*?)px;', fullbg.get('style'))[0][1]) fullbg_location_list.append(location)

 

那麼

 

如今咱們已經有了原始圖片

 

還知道了每一個位置應該顯示原始圖片的什麼部分

 

接下來咱們就寫一個方法

 

用來合成圖片

 

 # 寫入圖片 bg_image_file = BytesIO(bg_image) fullbg_image_file = BytesIO(fullbg_image)
# 合成圖片 bg_Image = mergy_Image(bg_image_file, bg_location_list) fullbg_Image = mergy_Image(fullbg_image_file, fullbg_location_list)

 

那麼問題又來了

 

怎麼合成啊

 

咱們再看看一開始分析的圖片

 

 

這裏圖片被分割成的每個小圖片的尺寸是

 

10 * 58

 

因此咱們也要將咱們剛剛下載的原始圖片切割成相應的尺寸大小

 

並且

 

這張圖片是由上半部分的小圖片和下半部分的小圖片合成的

 

因此咱們定義兩個 list 來裝這些小圖片

 

 # 存放上下部分的各個小塊 upper_half_list = [] down_half_list = []

 

而後將原始的圖片切割好放進去

 

image = Image.open(image_file)
# 經過 y 的位置來判斷是上半部分仍是下半部分,而後切割 for location in location_list: if location['y'] == -58: # 間距爲10,y:58-116 im = image.crop((abs(location['x']), 58, abs(location['x'])+10, 116)) upper_half_list.append(im) if location['y'] == 0: # 間距爲10,y:0-58 im = image.crop((abs(location['x']), 0, abs(location['x']) + 10, 58)) down_half_list.append(im)

 

至此

 

咱們這兩個 list 就分別放好了各個切割的圖片了

 

那麼接下來就建立一張空白的圖片

 

而後將小圖片一張一張(間距爲10)的粘貼到空白圖片裏

 

這樣咱們就能夠獲得一張合成好的圖片了

 

 

我真是個天才

 

 

 # 建立一張大小同樣的圖片 new_image = Image.new('RGB', (260, 116))
# 粘貼好上半部分 y座標是從上到下(0-116) offset = 0 for im in upper_half_list: new_image.paste(im, (offset, 0)) offset += 10
# 粘貼好下半部分 offset = 0 for im in down_half_list: new_image.paste(im, (offset, 58)) offset += 10

 

那麼到如今

 

咱們能夠獲得網頁上顯示的那兩張圖片了

 

一張徹底的圖片

 

 

一張帶缺口的圖片

 

 

接下來咱們就要經過對比這兩張圖

 

看看咱們要滑動的距離是多遠

 

 # 合成圖片 bg_Image = mergy_Image(bg_image_file, bg_location_list) fullbg_Image = mergy_Image(fullbg_image_file, fullbg_location_list) # bg_Image.show() # fullbg_Image.show()
# 計算缺口偏移距離 distance = get_distance(bg_Image, fullbg_Image) print('獲得距離:%s' % str(distance))

 

能夠經過圖片的 RGB 來計算

 

咱們設定一個閾值

 

若是 r、g、b 大於這個閾值

 

咱們就返回距離

 

def get_distance(bg_Image, fullbg_Image):
#閾值 threshold = 200
print(bg_Image.size[0]) print(bg_Image.size[1])

for i in range(60, bg_Image.size[0]): for j in range(bg_Image.size[1]): bg_pix = bg_Image.getpixel((i, j)) fullbg_pix = fullbg_Image.getpixel((i, j)) r = abs(bg_pix[0] - fullbg_pix[0]) g = abs(bg_pix[1] - fullbg_pix[1]) b = abs(bg_pix[2] - fullbg_pix[2])
if r + g + b > threshold: return i

 

如今

 

咱們知道了關鍵的滑動距離

 

激動人心的時刻到了

 

咱們使用 selenium

 

拿到滑塊的元素

 

而後根據這個距離拖動到缺口位置不就行了麼

 

立刻打開 selenium 的文檔

 

看到了這個函數

 

 

它可使用左鍵點擊元素

 

而後拖動到指定距離

 

最後釋放鼠標左鍵

 

挖槽

 

正合我意

 

趕忙試一下

 

knob = WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))ActionChains(driver).drag_and_drop_by_offset(knobdistance, 0).perform()

 

運行一下試試看吧

 

 

哇哦你妹哦~

 

妖怪吃了拼圖了

 

 

看來直接拖拽是不行的

 

容易遇到妖怪

 

畢竟這太快了

 

就算加藤鷹也沒那麼快吧

 

小帥b試着拖完滑塊讓它睡一下再釋放

 

 ActionChains(driver).click_and_hold(knob).perform()    ActionChains(driver).move_by_offset(xoffset=distance, yoffset=0.1).perform()    time.sleep(0.5)    ActionChains(driver).release(knob).perform()

 

發現拼圖仍是特麼的被妖怪吃了

 

 

後來小帥b發現原來別人也遇到了這樣的問題

 

而後又發現了

 

有個叫勻速直線運動的東西

 

什麼 加速度

 

什麼 v = v0 + at

 

什麼 s = ½at²

 

 

這不是高中的知識點麼

 

瞬間想起小帥b高中的時候在最角落的課桌

 

此刻往右上方擡起頭

 

 45 度角

 

讓個人眼淚劃出一條美麗的弧線

 

 

什麼鬼

 

回到正題

 

咱們可使用它來構造一個運動路徑

 

該加速時加速

 

該減速的時候減速

 

這樣的話就更像人類在滑動滑塊了

 


def get_path(distance): result = [] current = 0 mid = distance * 4 / 5 t = 0.2 v = 0 while current < (distance - 10): if current < mid: a = 2 else: a = -3 v0 = v v = v0 + a * t s = v0 * t + 0.5 * a * t * t current += s result.append(round(s)) return result

 

此次

 

咱們使用這個軌跡來滑動

 

 knob = WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show"))) result = get_path(distance) ActionChains(driver).click_and_hold(knob).perform()
for x in result: ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5) ActionChains(driver).release(knob).perform()

 

好了好了

 

咱們再來運行一下吧

 

 

哈哈哈

 

cool

 

成功識別了哇

 

我無論

 

 

固然了

 

成功率不是 100%

 

能夠多調戲它幾回

 

ok

 

以上就是識別滑動驗證碼的具體過程了

 

對於其它大部分的滑動驗證碼

 

也是可使用這招搞定的

 

因爲篇幅有限

 

源代碼我放在了這個公衆號後臺了

 

你發送〔滑動〕兩個字

 

就能夠獲取啦

 

 

此次本篇就真的完啦

 

據說你想約我?

 

peace

 

 

相關文章

 

(小帥b教你三招搞定模擬登陸)

 

(小帥b教你輕鬆識別圖片驗證碼)

 

 

 

      點個在看啊~~(破音)

相關文章
相關標籤/搜索