使用python - selenium模擬登錄b站

思路

  1. 輸入用戶名密碼點擊登錄
  2. 獲取驗證碼的原始圖片與有缺口的圖片
  3. 找出兩張圖片的缺口起始處
  4. 拖動碎片

功能代碼段

# 使用到的庫
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image
import time
import base64
username = '用戶名'
password = '密碼'
# 放在外面的緣由是若是再類的內部初始化,則程序結束後瀏覽器會自動退出
driver = webdriver.Chrome()

初始化相關參數python

# 初始化相關參數
    def __init__(self):
        self.url = 'https://passport.bilibili.com/login'
        self.browser = driver
        self.wait = WebDriverWait(self.browser, 20)
        self.name = username
        self.pw = password

獲取按鈕、輸入框、碎片拖動按鈕對象web

def get_login_button(self):
        """
        獲取初始登陸按鈕
        :return: 按鈕對象
        """
        button = self.wait.until(
            EC.presence_of_element_located((By.XPATH, "//a[contains(@class,'btn') and contains(@class, 'btn-login')]")))
        return button

    def get_slider_button(self):
        """
        獲取拖動碎片的地方
        :return: 拖動對象
        """
        sliderbutton = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='geetest_slider_button']")))
        return sliderbutton

    def get_login_input(self):
        """
        獲取登錄輸入框(用戶名/密碼)
        :return: 輸入框對象
        """
        user_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-username']")))
        pw_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-passwd']")))
        return user_login, pw_login

獲取帶有碎片的圖片和完整圖片canvas

def save_pic(self, data, filename):
        """
        解碼獲取到的base64再寫入到文件中,保存圖片
        :return:
        """
        data = data.split(',')[1]
        data = base64.b64decode(data)
        with open(filename, 'wb') as f:
            f.write(data)

    def get_pic(self):
        """
        獲取完好口圖片和有缺口圖片
        :return: 圖片對象
        """
        picName = ['full.png', 'slice.png']
        # 圖片對象的class
        className = ['geetest_canvas_fullbg', 'geetest_canvas_bg']
        # canvas標籤中的圖片經過js代碼獲取base64編碼,而後再經過解碼,將其寫入文件才能獲取到
        for i in range(len(className)):
            js = "var change = document.getElementsByClassName('"+className[i]\
                 + "'); return change[0].toDataURL('image/png');"
            im_info = self.browser.execute_script(js)
            self.save_pic(im_info, picName[i])

判斷像素點是否相同瀏覽器

def is_pixel_equal(self, image1, image2, x, y):
        """
        判斷兩個像素點是不是相同
        :param image1: 不帶缺口圖片
        :param image2: 帶缺口圖片
        :param x: 像素點的x座標
        :param y: 像素點的y座標
        :return:
        """
        pixel1 = image1.load()[x, y]
        pixel2 = image2.load()[x, y]
        threshold = 40
        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

獲取須要移動的距離app

def get_gap(self, image1, image2):
        """
        獲取缺口偏移量
        :param image1: 不帶缺口圖片
        :param image2: 帶缺口圖片
        :return:
        """
        # 這個能夠自行操做一下,若是發現碎片對不許,能夠調整
        left = 10
        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):
                    left = i
                    return left
        return left

變速運動拖動碎片,不然容易被看出來是機器執行ide

def get_track(self, distance):
        """
        根據偏移量獲取移動軌跡
        :param self:
        :param distance: 偏移量
        :return: 移動軌跡
        """
        # 移動軌跡
        track = []
        # 當前位移
        current = 0
        # 對的不必定很準確,因此自行調整一下distance
        distance = distance - 9
        # 減速閾值 -> 也就是加速到什麼位置的時候開始減速
        mid = distance * 4 / 5
        # 計算間隔
        t = 0.2
        # 初速度
        v = 0

        while current < distance:
            if current < mid:
                # 加速度爲正2
                a = 2
            else:
                # 加速度爲負3
                a = -3
            v0 = v
            v = v0 + a * t
            move = v0 * t + 1 / 2 * a * t * t
            current += move
            track.append(round(move))
        return track

模擬拖動碎片ui

def move_to_gap(self, slider, tracks, browser):
        """
        拖動滑塊到缺口處
        :param self:
        :param slider: 滑塊
        :param tracks: 軌跡
        :return:
        """
        # click_and_hold()點擊鼠標左鍵,不鬆開
        ActionChains(self.browser).click_and_hold(slider).perform()
        for x in tracks:
            # move_by_offset()鼠標從當前位置移動到某個座標
            ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
        time.sleep(0.5)
        # release()在某個元素位置鬆開鼠標左鍵
        ActionChains(self.browser).release().perform()

配置執行編碼

def test(self):
        # 輸入用戶名和密碼
        self.browser.get(self.url)
        user_login, pw_login = self.get_login_input()
        user_login.send_keys(self.name)
        pw_login.send_keys(self.pw)
        # 點擊按鈕對象
        button = self.get_login_button()
        button.click()
        # 這裏設置等待是爲了使得滑動驗證碼能出現,以後才能經過toDataURL獲取
        time.sleep(3)
        self.get_pic()
        image1 = Image.open('full.png')
        image2 = Image.open('slice.png')
        left = self.get_gap(image1, image2)
        track = self.get_track(left)
        slider = self.get_slider_button()
        self.move_to_gap(slider, track, self.browser)

完整代碼

TIP
若是出現碎片移動存在必定對不許的狀況,能夠自行調整一下left和distance的值。url

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image
import time
import base64
username = '用戶名'
password = '密碼'
driver = webdriver.Chrome()


class Start:
    def __init__(self):
        self.url = 'https://passport.bilibili.com/login'
        self.browser = driver
        self.wait = WebDriverWait(self.browser, 20)
        self.name = username
        self.pw = password

    def get_login_button(self):
        """
        獲取初始登陸按鈕
        :return: 按鈕對象
        """
        button = self.wait.until(
            EC.presence_of_element_located((By.XPATH, "//a[contains(@class,'btn') and contains(@class, 'btn-login')]")))
        return button

    def get_slider_button(self):
        """
        獲取拖動碎片的地方
        :return: 拖動對象
        """
        sliderbutton = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='geetest_slider_button']")))
        return sliderbutton

    def get_login_input(self):
        """
        獲取登錄輸入框(用戶名/密碼)
        :return: 輸入框對象
        """
        user_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-username']")))
        pw_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-passwd']")))
        return user_login, pw_login

    def save_pic(self, data, filename):
        """
        解碼獲取到的base64再寫入到文件中,保存圖片
        :return:
        """
        data = data.split(',')[1]
        data = base64.b64decode(data)
        with open(filename, 'wb') as f:
            f.write(data)

    def get_pic(self):
        """
        獲取完好口圖片和有缺口圖片
        :return: 圖片對象
        """
        # 圖片對象的類名
        # 首先須要這個東西已經出現了,咱們才能去執行相關的js代碼
        picName = ['full.png', 'slice.png']
        className = ['geetest_canvas_fullbg', 'geetest_canvas_bg']
        # canvas標籤中的圖片經過js代碼獲取base64編碼
        for i in range(len(className)):
            js = "var change = document.getElementsByClassName('"+className[i]\
                 + "'); return change[0].toDataURL('image/png');"
            im_info = self.browser.execute_script(js)
            self.save_pic(im_info, picName[i])

    def is_pixel_equal(self, image1, image2, x, y):
        """
        判斷兩個像素點是不是相同
        :param image1: 不帶缺口圖片
        :param image2: 帶缺口圖片
        :param x: 像素點的x座標
        :param y: 像素點的y座標
        :return:
        """
        pixel1 = image1.load()[x, y]
        pixel2 = image2.load()[x, y]
        threshold = 40
        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

    def get_gap(self, image1, image2):
        """
        獲取缺口偏移量
        :param image1: 不帶缺口圖片
        :param image2: 帶缺口圖片
        :return:
        """
        # 這個能夠自行操做一下,若是發現碎片對不許,能夠調整
        left = 10
        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):
                    left = i
                    return left
        return left

    def get_track(self, distance):
        """
        根據偏移量獲取移動軌跡
        :param self:
        :param distance: 偏移量
        :return: 移動軌跡
        """
        # 移動軌跡
        track = []
        # 當前位移
        current = 0
        # 由於老對不的不許確,因此自行調整一下distance
        distance = distance - 9
        # 減速閾值 -> 也就是加速到什麼位置的時候開始減速
        mid = distance * 4 / 5
        # 計算間隔
        t = 0.2
        # 初速度
        v = 0

        while current < distance:
            if current < mid:
                # 加速度爲正2
                a = 2
            else:
                # 加速度爲負3
                a = -3
            v0 = v
            v = v0 + a * t
            move = v0 * t + 1 / 2 * a * t * t
            current += move
            track.append(round(move))
        return track

    def test(self):
        # 輸入用戶名和密碼
        self.browser.get(self.url)
        user_login, pw_login = self.get_login_input()
        user_login.send_keys(self.name)
        pw_login.send_keys(self.pw)
        # 點擊按鈕對象
        button = self.get_login_button()
        button.click()
        # 保存圖片
        time.sleep(3)
        self.get_pic()
        image1 = Image.open('full.png')
        image2 = Image.open('slice.png')
        left = self.get_gap(image1, image2)
        track = self.get_track(left)
        slider = self.get_slider_button()
        self.move_to_gap(slider, track, self.browser)

    def move_to_gap(self, slider, tracks, browser):
        """
        拖動滑塊到缺口處
        :param self:
        :param slider: 滑塊
        :param tracks: 軌跡
        :return:
        """
        # click_and_hold()點擊鼠標左鍵,不鬆開
        ActionChains(self.browser).click_and_hold(slider).perform()
        for x in tracks:
            # move_by_offset()鼠標從當前位置移動到某個座標
            ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
        time.sleep(0.5)
        # release()在某個元素位置鬆開鼠標左鍵
        ActionChains(self.browser).release().perform()


Start().test()
相關文章
相關標籤/搜索