您好,登錄后才能下訂單哦!
前言
上篇文章記錄了2種分割驗證碼的方法,此外還有一種叫做”滴水算法”(Drop Fall Algorithm)的方法,但本人智商原因看這個算法看的云里霧里的,所以今天記錄滑動驗證碼的處理吧。網上據說有大神已經破解了滑動驗證碼的算法,可以不使用selenium來破解,但本人能力不足還是使用笨方法吧。
基礎原理很簡單,首先點擊驗證碼按鈕后的圖片是滑動后的完整結果,點擊一下滑塊后會出現拼圖,對這2個分別截圖后比較像素值來找出滑動距離,并結合selenium來實現拖拽效果。
至于selenium怎么安裝就不說了,滑動驗證碼的一個難點就是要模擬人的拖拽行為,移動快了不行,慢了也不行。
這里以國家企業公示網站為例:
# -*- coding: utf-8 -*- import time import random from io import BytesIO from PIL import Image from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class Slide(object): """滑動驗證碼破解""" def __init__(self, target): self.target = target # 要搜索的公司名稱 self.driver = webdriver.Chrome() self.wait = WebDriverWait(self.driver, 10) def crop(self, left, top, right, bottom, pic_name): """截屏并裁剪""" ss = Image.open(BytesIO(self.driver.get_screenshot_as_png())) cp = ss.crop((left, top, right, bottom)) # 注意這里順序 cp.save(pic_name) return cp def calc_move(self, pic1, pic2): """根據閾值計算移動距離""" pix1 = pic1.load() pix2 = pic2.load() threshold = 200 move = 0 # 因為滑塊都從左向右滑動,而碎片本身寬度為60所以從60開始遍歷 for i in range(60, pic1.size[0]): flag = False for j in range(pic1.size[1]): r = abs(pix1[i, j][0] - pix2[i, j][0]) g = abs(pix1[i, j][1] - pix2[i, j][1]) b = abs(pix1[i, j][2] - pix2[i, j][2]) # if r > threshold and g > threshold and b > threshold: # 方法1:分別判斷rgb大于閾值 # flag = True # break if r + g + b > threshold: # 方法2:判斷rgb總和跟閾值比較,效果比1好 為什么呢?? flag = True break if flag: move = i break return move def path2(self, distance): """繪制移動路徑方法1,構造一個等比數列""" q = 0.4 # 測試后發現0.4效果最佳 n = 10 # 最多移動幾次 a1 = ((1 - q) * distance) / (1 - q**n) result = [] for o in range(1, n + 1): an = a1 * q**(o - 1) if an < 0.1: # 小于移動閾值的就不要了 break t = random.uniform(0, 0.5) # 測試后0.5秒的間隔成功率最高 result.append([an, 0, t]) return result def path3(self, distance): """繪制移動路徑方法2,模擬物理加速、減速運動,效果比1好""" result = [] current = 0 # 減速閾值 mid = distance * 4 / 5 # 計算間隔 t = 0.2 # 初速度 v = 0 while current < (distance - 10): if current < mid: # 加速度為正2 a = 2 else: # 加速度為負3 a = -3 # 初速度v0 v0 = v # 當前速度v = v0 + at v = v0 + a * t # 移動距離x = v0t + 1/2 * a * t^2 move = v0 * t + 0.5 * a * t * t # 當前位移 current += move # 加入軌跡 result.append([round(move), 0, random.uniform(0, 0.5)]) return result def run(self): self.driver.get("http://www.gsxt.gov.cn/index") input_box = self.driver.find_element_by_id('keyword') input_box.send_keys(self.target) search_btn = self.driver.find_element_by_id('btn_query') time.sleep(3) # 注意這里等一下再點,否則會出現卡死現象 search_btn.click() # 等待驗證碼彈出 bg_pic = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "gt_cut_fullbg"))) # html中坐標原點是左上角,右為x軸正方向,下為y軸正方向 # 輸出的x為正就是此元素距離屏幕左側距離 # 輸出的y為正就是此元素距離屏幕上側距離 # 所以我們需要截圖的四個距離如下: top, bottom, left, right = ( bg_pic.location['y'], bg_pic.location['y'] + bg_pic.size['height'], bg_pic.location['x'], bg_pic.location['x'] + bg_pic.size['width']) time.sleep(1) cp1 = self.crop(left, top, right, bottom, '1.png') # 獲取滑塊按鈕并點擊一下 slide = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "gt_slider_knob"))) slide.click() time.sleep(3) # 等3秒報錯信息消失 TODO 這里應該可以改進 cp2 = self.crop(left, top, right, bottom, '2.png') move = self.calc_move(cp1, cp2) result = self.path2(move) # result = self.path3(move) # 拖動滑塊 ActionChains(self.driver).click_and_hold(slide).perform() for x in result: ActionChains(self.driver).move_by_offset(xoffset=x[0],yoffset=x[1]).perform() # ActionChains(driver).move_to_element_with_offset(to_element=slide,xoffset=x[0],yoffset=x[1]).perform() time.sleep(x[-1]) # 如果使用方法1則需要sleep time.sleep(0.5) ActionChains(self.driver).release(slide).perform() # 釋放按鈕 time.sleep(0.8) element = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "gt_info_text"))) ans = element.text if u"通過" in ans: # 這里也需要等一下才能獲取到具體的鏈接 element = self.wait.until(EC.presence_of_all_elements_located((By.CLASS_NAME, "search_list_item"))) for o in self.driver.find_elements_by_xpath(u"http://a[@target='_blank']"): print(o.get_attribute("href")) self.driver.quit() else: print("識別失敗") self.driver.quit() if __name__ == '__main__': s = Slide('中國平安') s.run()
代碼中注釋很詳細就不多說了,如果運行時候提示
selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home
則需要到 https://sites.google.com/a/chromium.org/chromedriver/home 下載驅動后解壓到/usr/local/bin目錄即可。
使用服務器運行時使用phantomjs替換chrome,另外失敗的時候可以進行判斷自動重試,有興趣的小伙伴可以自己補充完善。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。