思路
- 輸入用戶名密碼點擊登陸
- 獲取驗證碼的原始圖片與有缺口的圖片
- 找出兩張圖片的缺口起始處
- 拖動碎片
功能代碼段
# 使用到的庫
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()
初始化相關參數
# 初始化相關參數
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: 圖片對象
"""
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
獲取需要移動的距離
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 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的值。
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()