b站这样的滑动验证码,用Python照样自动识别

作者: 一墨编程学习 | 来源:发表于2019-04-25 17:16 被阅读13次

    大家应该都很熟悉

    点击滑块然后移动到图片缺口进行验证

    现在越来越多的网站使用这样的验证方式

    为的是增加验证码识别的难度

    那么

    对于这种验证码

    应该怎么破呢

    接下来就是

    打开 b 站的登录页面

    https://passport.bilibili.com/login

    image

    可以看到登录的时候需要进行滑块验证

    按下 F12

    进入 Network

    看下我们将滑块移到缺口松开之后做了什么提交

    可以看到是一个 GET 请求

    但是

    这请求链接也太特么长了吧

    我们来看看请求的参数是怎么样的

    哇靠

    gt?

    challenge?

    w?

    这些都是什么鬼参数

    还加密了

    完全下不了手啊

    既然以请求的方式不好弄

    我们从它们的源代码入手

    看看有什么突破口

    回到 b 站的登录页

    按下 F12

    进入 Element

    然后点击滑块出现了图片

    定位一下

    发现有两个 a 标签

    一个 class 是 gt_bg gt_show

    一个 class 是 gt_fullbg gt_show

    和小帅b想的一样

    这个验证码应该是有两张图片

    一张是完全的背景图片

    一张是缺口的图片

    那把这两张图片下载下来对比一下不就行了

    打开 a 标签一看

    一张图片被切割成很多小块

    原来这张图片是拼出来的

    我们看看原始图片是怎么样的

    什么乱七八糟的

    再仔细看下源代码

    原来是在同一张图片通过偏移量合成了一张完整的图片

    background-position: -277px -58px;
    
    

    看了一下缺口的图片也是如此

    到这里

    我们的第一个思路就是

    下载这两张原始图片

    然后通过偏移量合成两张真正的图片

    背景图

    image

    缺口图

    ↓变身

    那么怎么做呢?

    因为我们还要模拟滑动滑块

    所以呢

    我们要用到 selenium

    打开b站的登录页

    然后等到那个滑块显示出来

     # 获取滑块按钮
    

    接下来我们就获取页面的源码

    driver.page_source

    然后使用 bs 获取两张原始背景图片的 url

        bs = BeautifulSoup(driver.page_source,'lxml')
    

    拿到了图片地址之后

    将图片下载下来

     # 将图片格式存为 jpg 格式
    

    ok

    我们已经把两张原始图片下载下来了

    那么接下来就是要合成图片了

    我们要根据图片的位置来合成

    也就是源码中的 background-position

    获取每一个小图片的位置

    我们可以通过字典的形式来表示这些位置

    然后将数据放到列表中

        # 存放每个合成缺口背景图片的位置
    

    那么

    现在我们已经有了原始图片

    还知道了每个位置应该显示原始图片的什么部分

    接下来我们就写一个方法

    用来合成图片

        # 写入图片
    

    那么问题又来了

    怎么合成啊

    我们再看看一开始分析的图片

    这里图片被分割成的每一个小图片的尺寸是

    10 * 58

    所以我们也要将我们刚刚下载的原始图片切割成相应的尺寸大小

    而且

    这张图片是由上半部分的小图片和下半部分的小图片合成的

    所以我们定义两个 list 来装这些小图片

      # 存放上下部分的各个小块
    

    然后将原始的图片切割好放进去

    image = Image.open(image_file)
    

    至此

    我们这两个 list 就分别放好了各个切割的图片了

    那么接下来就创建一张空白的图片

    然后将小图片一张一张(间距为10)的粘贴到空白图片里

    这样我们就可以得到一张合成好的图片了

     # 创建一张大小一样的图片
    

    那么到现在

    我们可以得到网页上显示的那两张图片了

    一张完全的图片

    一张带缺口的图片

    接下来我们就要通过对比这两张图

    看看我们要滑动的距离是多远

        # 合成图片
    

    可以通过图片的 RGB 来计算

    我们设定一个阈值

    如果 r、g、b 大于这个阈值

    我们就返回距离

    def get_distance(bg_Image, fullbg_Image):
    

    现在

    我们知道了关键的滑动距离了

    激动人心的时刻到了

    我们使用 selenium

    拿到滑块的元素

    然后根据这个距离拖动到缺口位置不就好了么

    马上打开 selenium 的文档

    看到了这个函数

    它可以使用左键点击元素

    然后拖动到指定距离

    最后释放鼠标左键

    knob =  WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))
    

    运行一下试试看吧

    妖怪吃了拼图了

    看来直接拖拽是不行的

    试着拖完滑块让它睡一下再释放

        ActionChains(driver).click_and_hold(knob).perform()
    

    发现拼图还是特么的被妖怪吃了

    有个叫匀速直线运动的东西

    什么 加速度

    什么 v = v0 + at

    什么 s = ½at²

    什么鬼

    回到正题

    我们可以使用它来构造一个运动路径

    该加速时加速

    该减速的时候减速

    这样的话就更像人类在滑动滑块了

    
    

    这次

    我们使用这个轨迹来滑动

        knob = WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))
    

    好了好了

    我们再来运行一下吧

    哈哈哈

    成功识别了

    当然了

    成功率不是 100%

    可以多调戏它几次

    ok

    以上就是识别滑动验证码的具体过程了

    对于其它大部分的滑动验证码

    也是可以使用这招搞定的

    如果大家想找一个Python学习环境,可以加入我们的Python学习圈,自己是一名高级python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、人工智能、机器学习等。送给正在学习python的小伙伴!每天会准时的讲一些项目实战案例,分享一些学习的方法和需要注意的小细节,我们的python学习交流q–u--n【 784758214 】,这里是python学习者聚集地,欢迎初学和进阶中的小伙伴!

    python专业技术分享

    完整代码:

    进口时间
    导入请求
    来自 PIL  导入图片
    来自 selenium import webdriver
    来自 selenium.webdriver 导入 ActionChains
    从 selenium.webdriver.common.by 进口通过
    来自 selenium.webdriver.support.ui 导入 WebDriverWait
    从 selenium.webdriver.support 导入 expected_conditions 作为 EC
    来自 bs4 进口 BeautifulSoup
    进口重新
    来自 io import BytesIO
    
    driver = webdriver.Chrome(' / usr / lib / chromium-browser / chromedriver ')
    WAIT  = WebDriverWait(驱动程序,10)
    url =  ' https://passport.bilibili.com/login '
    
    
    def  mergy_Image(image_file,location_list):
        “””
        将原始图片进行合成
        :param image_file:图片文件
        :param location_list:图片位置
        :return:合成新的图片
        “””
    
        #存放上下部分的各个小块
        upper_half_list = []
        down_half_list = []
    
        image = Image.open(image_file)
    
        #通过y的位置来判断是上半部分还是下半部分,然后切割
        在 location_list 中的位置:
            如果 location [ ' y ' ] ==  - 58:
                #间距为10,Y:58-116
                IM = image.crop((ABS(位置[ ' X ' ]),58,ABS(位置[ ' X ' ])+ 10,116))
                upper_half_list.append(IM)
            如果 location [ ' y ' ] ==  0:
                #间距为10,Y:0-58
                IM = image.crop((ABS(位置[ ' X ' ]),0,ABS(位置[ ' X ' ])+  10,58))
                down_half_list.append(IM)
    
        #创建一张大小一样的图片
        new_image = Image.new(' RGB ',(260,116))
    
        #粘贴好上半部分y坐标是从上到下(0-116)
        offset =  0
        对于 IM 在 upper_half_list:
            new_image.paste(im,(offset,0))
            偏移+ =  10
    
        #粘贴好下半部分
        offset =  0
        对于 IM 在 down_half_list:
            new_image.paste(im,(offset,58))
            偏移+ =  10
    
        返回 new_image
    
    
    def  get_distance(bg_Image,fullbg_Image):
    
        #阈值
        阈值=  200
    
        print(bg_Image.size [ 0 ])
        print(bg_Image.size [ 1 ])
    
    
        对于我在 范围(60,bg_Image.size [ 0 ]):
            为 Ĵ 在 范围(bg_Image.size [ 1 ]):
                bg_pix = bg_Image.getpixel((i,j))
                fullbg_pix = fullbg_Image.getpixel((i,j))
                r =  abs(bg_pix [ 0 ] - fullbg_pix [ 0 ])
                g =  abs(bg_pix [ 1 ] - fullbg_pix [ 1 ])
                b =  abs(bg_pix [ 2 ] - fullbg_pix [ 2 ])
    
                如果 r + g + b >阈值:
                   回来我
    
    
    
    
    def  get_path(距离):
            result = []
            current =  0
            中期=距离*  4  /  5
            t =  0.2
            v =  0
            而当前<(距离-  10):
                如果当前< mid:
                    a =  2
                否则:
                    a =  - 3
                v0 = v
                v = v0 + a * t
                s = v0 * t +  0.5  * a * t * t
                当前+ = s
                result.append(round(s))
            返回结果
    
    
    def  start_drag(driver,distance):
    
        #被妖怪吃掉了
        #旋钮= WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, “#GC-箱> DIV> div.gt_slider> div.gt_slider_knob.gt_show”)))
        # ActionChains(司机).click_and_hold(旋钮).perform()
        # ActionChains(驱动程序).move_by_offset(X偏移=距离,Y偏移= 0.1).perform()
        # time.sleep(0.5)
        # ActionChains(司机).release(旋钮).perform()
    
        #被妖怪吃掉了
        # ActionChains(驱动程序).drag_and_drop_by_offset(旋钮,距离10,0).perform()
    
        knob =  WAIT .until(EC .presence_of_element_located((。CSS_SELECTOR,“#gc-box> div> div.gt_slider> div.gt_slider_knob.gt_show ”)))
        result = get_path(距离)
        ActionChains(司机).click_and_hold(旋钮).perform()
    
        for x in result:
            ActionChains(驱动程序).move_by_offset(xoffset = x,yoffset = 0).perform()
    
        time.sleep(0.5)
        ActionChains(司机).release(旋钮).perform()
    
    
    高清 recognize_code(司机):
        “””
        识别滑动验证码
        :param driver:selenium驱动
        :返回:
        “””
    
        bs = BeautifulSoup(driver.page_source,' lxml ')
        #找到背景图片和缺口图片的DIV
        bg_div = bs.find_all(class_ = ' gt_cut_bg_slice ')
        fullbg_div = bs.find_all(class_ = ' gt_cut_fullbg_slice ')
    
        #获取缺口背景图片网址
        bg_url = re.findall(' background-image:\ surl \(“(。*?)”\)',bg_div [ 0 ] .get(' style '))
        #获取背景图片的URL
        fullbg_url = re.findall(' background-image:\ surl \(“(。*?)”\)',fullbg_div [ 0 ] .get(' style '))
    
        #存放每个合成缺口背景图片的位置
        bg_location_list = []
        #存放每个合成背景图片的位置
        fullbg_location_list = []
    
        为 BG 在 bg_div:
            location = {}
            location [ ' x ' ] =  int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',bg.get(' style '))[ 0 ] [ 0 ])
            location [ ' y ' ] =  int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',bg.get(' style '))[ 0 ] [ 1 ])
            bg_location_list.append(位置)
    
        for fullbg in fullbg_div:
            location = {}
            location [ ' x ' ] =  int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',fullbg.get(' style '))[ 0 ] [ 0 ])
            location [ ' y ' ] =  int(re.findall(' background-position:\ s(。*?)px \ s(。*?)px; ',fullbg.get(' style '))[ 0 ] [ 1 ])
            fullbg_location_list.append(位置)
    
        print(bg_location_list)
        print(fullbg_location_list)
    
        #将图片格式存为jpg格式
        bg_url = bg_url [ 0 ] .replace(' webp ',' jpg ')
        fullbg_url = fullbg_url [ 0 ] .replace(' webp ',' jpg ')
        #打印(bg_url)
        #打印(fullbg_url)
    
        #下载图片
        bg_image = requests.get(bg_url).content
        fullbg_image = requests.get(fullbg_url).content
        print('完成图片下载')
    
        #写入图片
        bg_image_file = BytesIO(bg_image)
        fullbg_image_file = BytesIO(fullbg_image)
    
        #合成图片
        bg_Image = mergy_Image(bg_image_file,bg_location_list)
        fullbg_Image = mergy_Image(fullbg_image_file,fullbg_location_list)
        # bg_Image.show()
        # fullbg_Image.show()
    
        #计算缺口偏移距离
        distance = get_distance(bg_Image,fullbg_Image)
        print('得到 距离:%s '% str(距离))
    
        start_drag(司机,距离)
    
    
    
    
    如果 __name__  ==  ' __main__ ':
    
        #获取滑块按钮
        driver.get(URL)
        slider =  WAIT .until(EC .element_to_be_clickable(
            (通过.CSS_SELECTOR,“#gc-box> div> div.gt_slider> div.gt_slider_knob.gt_show ”)))
    
        recognize_code(驱动器)
    
    
        # driver.close()
    
    
    

    相关文章

      网友评论

        本文标题:b站这样的滑动验证码,用Python照样自动识别

        本文链接:https://www.haomeiwen.com/subject/tfvzgqtx.html