# 使用到的庫 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()