Python爬虫:Bilibili模拟登陆(滑动验证码)项目

Posted 黑马程序员官方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python爬虫:Bilibili模拟登陆(滑动验证码)项目相关的知识,希望对你有一定的参考价值。

文章目录


1. 为什么要处理滑动验证码

​ 在很多时候我们在做模拟登陆的时候会遇到滑动验证码,这个时候就必须要处理

2. 目标网站

​ bilibili视频网站的滑动验证码

经过分析我们知道bilibili使用的是极验的拖动验证码

3. 项目的开发环境

本项目需要用到 selenium,io,PIL,time,random,请提前安装

请安装对应版本的库如下,其他库均为标准库,无需安装​ pip3 install Pillow4.2.1​ pip3 install selenium3.12.0

4. 项目流程介绍

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

5. bilibili模拟登陆-初始化

#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random

class Bilibili(object):

    def __init__(self):
        # 设置登录页面url
        self.url = 'https://passport.bilibili.com/login'

        # 创建浏览器对象
        self.driver = webdriver.Chrome()

        # 设置页面的隐式等待
        self.driver.implicitly_wait(3)

        # 设置账号密码
        self.user = '18668928175'
        self.pwd = '461324morganna'


    def login(self):
        """
            实现主要的登录逻辑
        """
        pass


    def run(self):
        self.login()

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

6. bilibili模拟登陆-请求bilibili的登录页面&模拟输入账号密码

#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random

class Bilibili(object):

    #......

    def input_user_pwd(self):
        """
            请求登录页面输入账号密码
        """
        # 请求登录页面
        self.driver.get(self.url)

        # 输入账号
        el_user = self.driver.find_element_by_id('login-username')
        el_user.send_keys(self.user)

        # 输入密码
        el_user = self.driver.find_element_by_id('login-passwd')
        el_user.send_keys(self.pwd)

    def login(self):
        """
            实现主要的登录逻辑
        """
        # 调用输入账号密码的方法
        self.input_user_pwd()


    def run(self):
        self.login()

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

7. bilibili模拟登陆-获取验证码图片

#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random

class Bilibili(object):

    #......

    def get_screenshot(self):
        """
            获取屏幕截图,截图结果使用Image打开,打开之后的对象可以用来从中截取出验证码图片
        """
        # 截屏
        screenshot = self.driver.get_screenshot_as_png()

        # 打开图片
        screenshot = Image.open(BytesIO(screenshot))

        return screenshot

    def get_position(self):
        """
            获取图片验证码在屏幕截图上的位置
        """
        # 点击锁按钮才能查看没有阴影的验证码
        el_lock = self.driver.find_element_by_xpath('//div[@class="gt_ajax_tip gt_ready"]')
        el_lock.click()

        # 获取图片验证码元素
        img = self.driver.find_element_by_xpath('//div[@class="gt_cut_fullbg gt_show"]')
        time.sleep(3)

        # 获取验证码在屏幕截图上的坐标
        location = img.location

        # 获取验证码图片的的尺寸
        size = img.size

        # 使用图片坐标和图片尺寸计算出整个图片的区域,这里所有的计算都乘以2倍,需要注意的是,在我机器上因为分辨率的问题才这样做的,请根据自身机器的分辨率决定是否
        left, top, right, button = 2 * location['x'], 2 * location['y'], 2 * (location['x'] + size['width']), 2 * (location['y'] + size['height'])

        return left, top, right, button

    def get_image(self):

        # 获取截图用的坐标元组
        position = self.get_position()

        # 屏幕截图
        screenshot1 = self.get_screenshot()
        # 将不带阴影的验证码图片抠出
        captcha1 = screenshot1.crop(position)
        with open('captcha1.png', 'wb')as f:
            captcha1.save(f)

        # 点击拖动按钮,出现验证码图片用于截图
        button = self.get_button()
        button.click()
        # 点击之后会出现错误提示信息,等待一定时间之后提示信息消失再截图
        time.sleep(4)

        # 屏幕截图
        screenshot2 = self.get_screenshot()
        # 将验证码图片抠出
        captcha2 = screenshot2.crop(position)
        with open('captcha2.png', 'wb')as f:
            captcha2.save(f)

        return captcha1, captcha2

    def login(self):
        """
            实现主要的登录逻辑
        """
        # 调用输入账号密码的方法
        self.input_user_pwd()

        # 获取两个图片对象
        img1, img2 = self.get_image()


    def run(self):
        self.login()

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

8. bilibili模拟登陆-比较两个验证码图片获取验证码滑块的偏移量

#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random

class Bilibili(object):

    #......

    def is_pixel_equal(self, image1, image2, x, y):
        """
            比较两张图片相同位置的像素的是否相似
        """
        # 获取两个验证码图片在x,y这个点的rgb的颜色
        pixel1 = image1.load()[x, y]
        pixel2 = image2.load()[x, y]
        # 设置比较值
        threshold = 60

        print(pixel1, pixel2)

        # 分别获取两个验证码同一像素差值的绝对值,然后再和比较值
        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):
        # 该值用于跳过验证码滑块,因为验证码滑块有阴影会影响结果,所以直接设置跳过验证码滑块
        left = 120

        # 遍历验证码的x轴
        for i in range(left, image1.size[0]):
            # 确定x之后遍历y,获取一个坐标
            for j in range(image1.size[1]):
                # 判断相似度,超过设定的相似度则表示找到拼图阴影
                if not self.is_pixel_equal(image1, image2, i, j):
                    left = i
                    # left为滑块移动的像素,因为分辨率的问题,我的机器上需要除2才能得到真实拖动距离,-6是阴影区域的修正值,也可根据自身环境决定是否使用
                    return round(left/2 - 6)
        return left

    def login(self):
        """
            实现主要的登录逻辑
        """
        # 调用输入账号密码的方法
        self.input_user_pwd()

        # 获取两个图片对象
        img1, img2 = self.get_image()

        # 获取拼图阴影位置
        offset = self.get_gap(img1, img2)


    def run(self):
        self.login()

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

9. bilibili模拟登陆-使用偏移值计算移动操作

#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random

class Bilibili(object):

    #......


    def get_track(self, offset):
        """
            直接将滑块瞬间拖动到阴影会被极验的后台识别为机器操作,所以这里仿照人类的拖动验证码的情形模拟操作,一般来说人类拖动验证码的时候是先加速后减速,最后可能做微调
        """

        track = []
        # 当前距离
        current = 0
        # 设置加减速的中间值
        mid = offset * 9/10
        # 设置时间渐渐个
        t = 0.3
        # 设置速度
        v = 0

        # 当滑动到偏移值的时候退出生成
        while current < offset:
            根据是否到达临界值决定使用加速的加速度还是减速的加速度
            if current < round(mid):
                a = 2
            else:
                a = -4

            v0 = v
            v = v0 + a*t

            move = v0 * t + 1/2 * a * t * t
            current += move

            track.append(round(move))
        # print(track)
        # 设置混淆位移,成对编写,添加到track的结尾,模拟微调
        data = [1, 5, 3,  -5, -3, -1]
        random.shuffle(data)
        track.extend(data)

        return track

    def login(self):
        """
            实现主要的登录逻辑
        """
        # 调用输入账号密码的方法
        self.input_user_pwd()

        # 获取两个图片对象
        img1, img2 = self.get_image()

        # 获取拼图阴影位置
        offset = self.get_gap(img1, img2)

        # 计算移动轨迹
        track = self.get_track(offset)


    def run(self):
        self.login()

if __name__ == '__main__':
    bili = Bilibili()
    bili.run()
11.bilibili模拟登陆-拖动滑块
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random

class Bilibili(object):

    # ......

    def operate_button(self, track):
        print("***")
        # 点击拖动验证码的按钮
        ActionChains(self.driver).click_and_hold(self.get_button()).perform()

        # 拖动验证码
        for i in track:
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()

        # 松开按钮验证
        ActionChains(self.driver).release().perform()

    def login(self):
        """
            实现主要的登录逻辑
        """
        # 调用输入账号密码的方法
        self.input_user_pwd()

        # 获取两个图片对象
        img1, img2 = self.get_image()

        # 获取拼图阴影位置
        offset = self.get_gap(img1, img2)

        # 计算移动轨迹
        track = self.get_track(offset)

        # 操作拖动条完成拼图
        self.operate_button(track)

    def run(self):
        self.login()

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

10.bilibili模拟登陆-拖动滑块

#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random

class Bilibili(object):

    # ......

    def operate_button(self, track):
        print("***")
        # 点击拖动验证码的按钮
        ActionChains(self.driver).click_and_hold(self.get_button()).perform()

        # 拖动验证码
        for i in track:
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()

        # 松开按钮验证
        ActionChains(self.driver).release().perform()

    def login(self):
        """
            实现主要的登录逻辑
        """
        # 调用输入账号密码的方法
        self.input_user_pwd()

        # 获取两个图片对象
        img1, img2 = self.get_image()

        # 获取拼图阴影位置
        offset = self.get_gap(img1, img2)

        # 计算移动轨迹
        track = self.get_track(offset)

        # 操作拖动条完成拼图
        self.operate_button(track)

    def run(self):
        self.login()

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

以上是关于Python爬虫:Bilibili模拟登陆(滑动验证码)项目的主要内容,如果未能解决你的问题,请参考以下文章

Python3网络爬虫实战-43极验滑动验证码的识别

Python之极验滑动验证码的识别(教程+案例)

python爬虫 模拟登陆校园网-初级

Python爬虫实例网站模拟登陆

python模拟网站登陆-滑动验证码

Python爬虫(二十二)_selenium案例:模拟登陆豆瓣