滑塊驗證碼(騰訊)——基於selenium,pillow實現

  與網上的其餘滑塊驗證碼不一樣,騰訊的驗證碼能夠直接經過url請求獲得,只須要對url進一步分析,提取出驗證碼原圖的地址,並將圖片下載便可。web

但據我觀察,該url彷佛是有兩種不一樣類型的地址格式,須要具體分析。這裏,選擇其中一種進行實驗,源碼在文章末尾。chrome

    def get_img(self):
        """
        獲取驗證碼陰影圖和原圖
        :return:
        """
        self.driver.switch_to.frame('tcaptcha_iframe')
        time.sleep(3)
        # 獲取有陰影的圖片
        src = self.driver.find_element_by_id('slideBg').get_attribute('src')
        #  分析圖片地址,發現原圖地址能夠經過陰影圖地址改動獲取
        src_bg = re.sub('_1_', '_0_', src)
        urlretrieve(src, 'img1.png')
        urlretrieve(src_bg, 'img2.png')
獲取圖片,保存到本地

  有一個須要注意的問題,下載到本地的圖片對原圖進行了放大,因此要對圖片尺寸進行調整還原,保證後面計算出的偏移值的正確性瀏覽器

def resize_img(self, img):
        """
        下載的圖片把網頁中的圖片進行了放大,因此將圖片還原成原尺寸
        :param img: 圖片
        :return: 返回還原後的圖片
        """
        a = 2.428  # 經過本地圖片與原網頁圖片的比較,計算出的縮放比例
        (x, y) = img.size
        x_resize = int(x // a)
        y_resize = int(y // a)
        img = img.resize((x_resize, y_resize), Image.ANTIALIAS)
還原圖片尺寸

  此時,已經拿到原圖和陰影圖,只須要進行像素比較便可,網上有其餘相關教程,可參考dom

    def is_pixel_equal(self, img1, img2, x, y):
        """
        比較兩張圖片同一點上的像數值,差距大於設置標準返回False
        :param img1: 陰影圖
        :param img2: 原圖
        :param x: 橫座標
        :param y: 縱座標
        :return: 是否相等
        """
        pixel1, pixel2 = img1.load()[x, y], img2.load()[x, y]
        sub_index = 100
        if abs(pixel1[0] - pixel2[0]) < sub_index and abs(pixel1[1] - pixel2[1]) < sub_index and abs(
                pixel1[2] - pixel2[2]) < sub_index:
            return True
        else:
            return False

    def get_gap_offset(self, img1, img2):
        '''
            獲取缺口的偏移量
        '''
        offset = None
        distance = 70
        for i in range(distance, img1.size[0]):
            for j in range(img1.size[1]):
                # 兩張圖片對比,(i,j)像素點的RGB差距,過大則該x爲偏移值
                if not self.is_pixel_equal(img1, img2, i, j):
                    offset = i
                    return offset
        return offset
計算偏移值

  最後,經過selenuim的動做鏈,模擬滑塊拖動ide

    def operate_slider(self, track):
        """
        拖動滑塊
        :param track: 運動軌跡
        :return:
        """
        #  定位到拖動按鈕
        slider_bt = self.driver.find_element_by_xpath('//div[@class="tc-drag-thumb"]')
        # 點擊拖動按鈕不放
        ActionChains(self.driver).click_and_hold(slider_bt).perform()
        # 按正向軌跡移動
        for i in track:
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
            time.sleep(random.random() / 100)  # 每移動一次隨機停頓0-1/100秒之間騙過了極驗,經過率很高
        time.sleep(random.random())
        # 按逆向軌跡移動
        back_tracks = [-1, -0.5, -1]
        for i in back_tracks:
            time.sleep(random.random() / 100)
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
        # 模擬人手抖動
        self.shake_mouse()
        time.sleep(random.random())
        # 鬆開滑塊按鈕
        ActionChains(self.driver).release().perform()
模擬滑塊拖動

  下面附上完整的代碼,僅供參考,不足之處,歡迎你們指正ui

import time
import re
import random
from selenium import webdriver
from urllib.request import urlretrieve
from PIL import Image
from selenium.webdriver.common.action_chains import ActionChains


class Tencent():
    def __init__(self):
        """
        初始化屬性,傳入url地址,驅動路徑,瀏覽器窗口最大化,僞造ua
        """
        self.url = 'https://qzone.qq.com/'
        driver_path = r'C:\Users\xiaodengtang\AppData\Local\Google\Chrome\Application\chromedriver.exe'
        self.driver = webdriver.Chrome(executable_path=driver_path)
        self.driver.maximize_window()
        self.headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'}

    def input_username_password(self, account, password):
        """
        打開瀏覽器,傳入帳號、密碼,定位到登陸窗口,切換登錄方式
        :param account:
        :param password:
        :return:
        """
        self.driver.get(self.url)
        time.sleep(1)
        self.driver.switch_to.frame('login_frame')
        self.driver.find_element_by_id('switcher_plogin').click()
        time.sleep(1)
        self.driver.find_element_by_id('u').send_keys(account)
        time.sleep(0.5)
        self.driver.find_element_by_id('p').send_keys(password)
        time.sleep(0.5)
        self.driver.find_element_by_class_name('login_button').click()

    def get_img(self):
        """
        獲取驗證碼陰影圖和原圖
        :return:
        """
        self.driver.switch_to.frame('tcaptcha_iframe')
        time.sleep(3)
        # 獲取有陰影的圖片
        src = self.driver.find_element_by_id('slideBg').get_attribute('src')
        #  分析圖片地址,發現原圖地址能夠經過陰影圖地址改動獲取
        src_bg = re.sub('_1_', '_0_', src)
        urlretrieve(src, 'img1.png')
        urlretrieve(src_bg, 'img2.png')
        captcha1 = Image.open('img1.png')
        captcha2 = Image.open('img2.png')
        return captcha1, captcha2

    def resize_img(self, img):
        """
        下載的圖片把網頁中的圖片進行了放大,因此將圖片還原成原尺寸
        :param img: 圖片
        :return: 返回還原後的圖片
        """
        a = 2.428  # 經過本地圖片與原網頁圖片的比較,計算出的縮放比例
        (x, y) = img.size
        x_resize = int(x // a)
        y_resize = int(y // a)
        img = img.resize((x_resize, y_resize), Image.ANTIALIAS)
        return img

    def is_pixel_equal(self, img1, img2, x, y):
        """
        比較兩張圖片同一點上的像數值,差距大於設置標準返回False
        :param img1: 陰影圖
        :param img2: 原圖
        :param x: 橫座標
        :param y: 縱座標
        :return: 是否相等
        """
        pixel1, pixel2 = img1.load()[x, y], img2.load()[x, y]
        sub_index = 100
        if abs(pixel1[0] - pixel2[0]) < sub_index and abs(pixel1[1] - pixel2[1]) < sub_index and abs(
                pixel1[2] - pixel2[2]) < sub_index:
            return True
        else:
            return False

    def get_gap_offset(self, img1, img2):
        '''
            獲取缺口的偏移量
        '''
        offset = None
        distance = 70
        for i in range(distance, img1.size[0]):
            for j in range(img1.size[1]):
                # 兩張圖片對比,(i,j)像素點的RGB差距,過大則該x爲偏移值
                if not self.is_pixel_equal(img1, img2, i, j):
                    offset = i
                    return offset
        return offset

    def get_track(self, offset):
        '''
        計算滑塊的移動軌跡
        '''
        offset -= 30  # 滑塊並非從0開始移動,有一個初始值
        a = offset / 4
        track = [a, a, a, a]
        return track

    def shake_mouse(self):
        """
        模擬人手釋放鼠標抖動
        :return: None
        """
        ActionChains(self.driver).move_by_offset(xoffset=-2, yoffset=0).perform()
        ActionChains(self.driver).move_by_offset(xoffset=2, yoffset=0).perform()

    def operate_slider(self, track):
        """
        拖動滑塊
        :param track: 運動軌跡
        :return:
        """
        #  定位到拖動按鈕
        slider_bt = self.driver.find_element_by_xpath('//div[@class="tc-drag-thumb"]')
        # 點擊拖動按鈕不放
        ActionChains(self.driver).click_and_hold(slider_bt).perform()
        # 按正向軌跡移動
        for i in track:
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
            time.sleep(random.random() / 100)  # 每移動一次隨機停頓0-1/100秒之間騙過了極驗,經過率很高
        time.sleep(random.random())
        # 按逆向軌跡移動
        back_tracks = [-1, -0.5, -1]
        for i in back_tracks:
            time.sleep(random.random() / 100)
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
        # 模擬人手抖動
        self.shake_mouse()
        time.sleep(random.random())
        # 鬆開滑塊按鈕
        ActionChains(self.driver).release().perform()

    def login(self, account, password):
        '''
        實現主要的登錄邏輯
        '''
        self.input_username_password(account, password)
        time.sleep(2)
        a, b = self.get_img()
        a = self.resize_img(a)
        b = self.resize_img(b)
        distance = self.get_gap_offset(a, b)
        track = self.get_track(distance)
        self.operate_slider(track)


if __name__ == '__main__':
    qq = Tencent()
    account = '123548658'
    password = 'yanzhengma'
    qq.login(account, password)
完整代碼
相關文章
相關標籤/搜索