今天javascript
要來講說滑動驗證碼了php
你們應該都很熟悉css
點擊滑塊而後移動到圖片缺口進行驗證html
如今愈來愈多的網站使用這樣的驗證方式java
爲的是增長驗證碼識別的難度python
那麼ios
對於這種驗證碼web
應該怎麼破呢apache
接下來就是ruby
打開 b 站的登陸頁面
https://passport.bilibili.com/login
能夠看到登陸的時候須要進行滑塊驗證
按下 F12
進入 Network
看下咱們將滑塊移到缺口鬆開以後作了什麼提交
能夠看到是一個 GET 請求
可是
這請求連接也太特麼長了吧
就是比小帥b短了一點點
咱們來看看請求的參數是怎麼樣的
哇靠
gt?
challenge?
w?
這些都是什麼鬼參數
還加密了
徹底下不了手啊
那麼
本篇完
再見
peace
說
你是否是迷戀我??
好吧
你竟然滑到這裏來了
說明你仍是有點愛小帥b的
小帥b是那種遇到一點困難就放棄的人嗎
顯然不是
那麼接下來纔是真的
既然以請求的方式很差弄
咱們從它們的源代碼入手
看看有什麼突破口
回到 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') bg_div = bs.find_all(class_='gt_cut_bg_slice') fullbg_div = bs.find_all(class_='gt_cut_fullbg_slice')
bg_url = re.findall('background-image:\surl\("(.*?)"\)',bg_div[0].get('style')) fullbg_url = re.findall('background-image:\surl\("(.*?)"\)',fullbg_div[0].get('style'))
拿到了圖片地址以後
將圖片下載下來
bg_url = bg_url[0].replace('webp', 'jpg') fullbg_url = fullbg_url[0].replace('webp', 'jpg')
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: (offset, 0)) offset += 10
# 粘貼好下半部分 offset = 0 for im in down_half_list: (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) % 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(knob, distance, 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
相關文章
點個在看啊~~(破音)