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