• 使用python


    思路

    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()
    

    初始化相关参数

    	# 初始化相关参数
        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()
    
  • 相关阅读:
    Server Develop (三) 多进程实现C/S
    Server Develop (二) 多线程实现C/S
    Server Develop (一) 简单的TCP/IP C/S
    Server Develop (四) select实现非阻塞sever
    js添加、修改、删除xml节点例子
    网站弹出“位于Google Code SubversionRepository 的服务器……”的解决办法
    iframe嵌入网页
    asp空间判断jmail组件是否安装或支持的代码
    IE8的margintop兼容问题
    不错的CMS,值得借鉴!
  • 原文地址:https://www.cnblogs.com/NFii/p/11707159.html
Copyright © 2020-2023  润新知