python爬虫热点项目—滑块验证码项目(以Bilili为例)

时间:2024-04-17 16:41:15

1.模拟登录的网站:

bilibili视频网:https://passport.bilibili.com/login

2. 开发环境

本项目需要用到

io

time

random

selenium

PIL

请安装对应版本的库如下,其他库均为标准库,无需安装 ​ pip install pillow ​ pip install selenium

 

3.项目流程介绍

  1. 初始化
  2. 请求bilibili的登录页面&模拟输入账号密码
  3. 有阴影拼图的验证码图片&获取验证码图片
  4. 比较两个验证码图片获取验证码滑块的偏移量
  5. 使用偏移值计算移动操作
  6. 操作滑块按钮,模拟拖动滑块做验证登录

5.bilibili模拟登陆-初始化和模拟输入账号密码

class Bilibili(object):

	def __init__(self):
		#创建浏览器对象
		self.driver = webdriver.Chrome()
		#隐式等待
		self.driver.implicitly_wait(3)
		self.url = \'https://passport.bilibili.com/login\'
		#用户名
		self.user = \'\'
		#密码
		self.pwd = \'\'

	def close(self):
		\'\'\'
		关闭浏览器
		\'\'\'
		self.driver.quit()

	def input_user_pwd(self):
		\'\'\'
		   输入用户名和密码
		\'\'\'
		#进入登陆页面
		self.driver.get(self.url)

		#文本框输入用户名
		tb_user = self.driver.find_element_by_id(\'login-username\')
		tb_user.send_keys(self.user)
		#文本框输入密码
		tb_pwd = self.driver.find_element_by_id(\'login-passwd\')
		tb_pwd.send_keys(self.pwd)

6.有阴影拼图的验证码图片&获取验证码图片

    def get_screenshot(self):
        \'\'\'
        获取屏幕截图
        \'\'\'
        screenshot = self.driver.get_screenshot_as_png()
        screenshot =Image.open(BytesIO(screenshot))

        return screenshot
    def update_style(self):
        \'\'\'
            修改图片的style属性,显示无缺口的图片
        \'\'\'
        js = \'document.querySelectorAll("canvas")[3].style="display:block"\'
        self.driver.execute_script(js)
        time.sleep(2)

    def get_position(self):
        \'\'\'
            获取截取验证码时的四条边
        \'\'\'
        #定位到登陆按钮
        bt_login = self.driver.find_element_by_xpath(\'//a[@class="btn btn-login"]\')
        #模拟点击
        bt_login.click()
        time.sleep(2)
        #获取验证码图片对象
        code_img = self.driver.find_element_by_xpath(\'//canvas[@class="geetest_canvas_slice geetest_absolute"]\')
        time.sleep(2)

        location = code_img.location
        size = code_img.size

        #screenshot = self.get_screenshot()
        #print(screenshot.size)

        #计算图片截取区域(左,上,右,下,的坐标值)
        left,top,right,buttom = location[\'x\'],location[\'y\'],location[\'x\']+size[\'width\'],location[\'y\']+size[\'height\']
        return left,top,right,buttom


    def get_image(self):
        \'\'\'
            截取验证码图片
        \'\'\'
        #获取验证码位置
        position = self.get_position()
        #从屏幕截图中抠出有缺口的验证码图片
        captcha1 = self.get_screenshot().crop(position)
        #修改style属性,显示无缺口的验证码图片
        self.update_style()
        #从屏幕截图中抠出无缺口的验证码图片
        captcha2 = self.get_screenshot().crop(position)

        with open(\'captcha1.png\',\'wb\') as f1 ,open(\'captcha2.png\',\'wb\') as f2:
            captcha1.save(f1)
            captcha2.save(f2)

        return captcha1,captcha2

7. 比较两个验证码图片获取验证码滑块的偏移量

    def is_pixel_equal(self,img1,img2,x,y):
        \'\'\'
            判断两张图片的同一像素点的RGB值是否相等
        \'\'\'
        pixel1,pixel2= img1.load()[x,y],img2.load()[x,y]
        #print(pixel1,pixel2)
        #设定一个比较基准
        sub_index = 60

        #比较
        if abs(pixel1[0]-pixel2[0])< sub_index and abs(pixel1[1]-pixel2[1])< sub_index and abs(pixel1[2]-pixel2[2])< sub_index:
            return True
        else:
            return False

    def get_gap_offset(self,img1,img2):
        \'\'\'
            获取缺口的偏移量
        \'\'\'
        x = int(img1.size[0]/4.2)
        for i in range(x,img1.size[0]):
            for j in range(img1.size[1]):
                #两张图片对比,(i,j)像素点的RGB差距,过大则该x为偏移值
                if not self.is_pixel_equal(img1,img2,i,j):
                    x = i
                    return x
        return x


8.使用偏移值计算移动操作(轨迹)

    def get_track(self,offset):
        \'\'\'
           模拟人为拖动验证码滑块 
        \'\'\'
        track = []
        #滑块起始x坐标
        current = 5
        #变速临界值

        border_point = int(offset*3/5)
        #设置时间间隔
        t = 0.2
        #设置初速度
        offset +=4
        v = 0
        #循环直到滑动到偏移值时退出
        while current < offset:
            #根据是否临界点改变运动状态
            if current < border_point:
                #加速度
                a = 1
            else:
                a =-0.5
            v0 = v
            v = v0 + a*t

            move = v0*t +0.5*a*t*t

            current += move

            track.append(round(move))

        return track

9.操作滑块按钮,模拟拖动滑块做验证登录

    def shake_mouse(self):
        """
        模拟人手释放鼠标抖动
        :return: None
        """
        ActionChains(self.driver).move_by_offset(xoffset=-2,yoffset=0).perform()
        ActionChains(self.driver).move_by_offset(xoffset=2,yoffset=0).perform()

    def operate_slider(self,track):
        \'\'\'
           拖动滑块
        \'\'\'
        #获取拖动按钮
        back_tracks = [-1,-1,-2,-1]
        slider_bt = self.driver.find_element_by_xpath(\'//div[@class="geetest_slider_button"]\')

        #点击拖动验证码的按钮不放
        ActionChains(self.driver).click_and_hold(slider_bt).perform()

        #按正向轨迹移动
        for i in track:
            ActionChains(self.driver).move_by_offset(xoffset=i,yoffset=0).perform()
           #先加速后减速效果也不是很好。
           #每移动一次随机停顿0-1/100秒之间骗过了极验,通过率很高
            time.sleep(random.random()/100)
        time.sleep(random.random())
        #按逆向轨迹移动
        for i in back_tracks:
            time.sleep(random.random()/100)
            ActionChains(self.driver).move_by_offset(xoffset=i,yoffset=0).perform()
        #模拟人手抖动
        self.shake_mouse()
        time.sleep(random.random())
        #松开滑块按钮
        ActionChains(self.driver).release().perform()


    def do_captcha(self):
        \'\'\'
            实现处理验证码
        \'\'\'
        #有缺口,无缺口图片
        img1,img2 = self.get_image()
        #比较两个验证码图片获取验证码滑块的偏移量
        offset = self.get_gap_offset(img1,img2)
        print(offset)

        #使用偏移值计算移动操作
        track = self.get_track(offset)

        #操作滑块按钮,模拟拖动滑块做验证登录
        self.operate_slider(track)

    def login(self):
        \'\'\'
        实现主要的登陆逻辑
        \'\'\'
        #来到登陆界面并输入账号密码
        self.input_user_pwd()
        #处理验证码
        self.do_captcha()


        #关闭浏览器
        self.close()

    def run(self):
        self.login()

if __name__ == \'__main__\':
    bili =Bilibili()
    bili.run()