解决哔哩哔哩网站滑块验证登陆思路
  • 对于滑块,selenium可以很简单的模拟鼠标操作,但是,获取滑动的距离是这种验证破解的难点,首先,研究了B站的代码之后,发现验证是由一个叫极验的公司做的,他们传输过来的图片我发现是坐标被打乱了的一个图片,而且还有一组数据,我估计是计算图片坐标然后拼成一个完整的图片,很多人就会去研究这个坐标怎么计算的,但是我在看了他的HTML发现,最后他将计算后的完整图片加载到了canvas画布中,而且是三个画布(一个完整的,一个有缺口的,一个缺口图片),所以我们不用那么麻烦直接通过js将画布中的图片转为base64数据:
    $('.geetest_canvas_bg')[0].toDataURL('image/png')
    $('.geetest_canvas_fullbg')[0].toDataURL('image/png')
    $('.geetest_canvas_slice')[0].toDataURL('image/png')
    
  • 拿到数据后就简单了,我们可以通过base64模块以及OpenCV提供的imdecode方法将数据转化为OpenCV图片对象。
  • 然后就是最关键的时候了,我们先将图片转为灰度图片,然后通过scikit-image(计算图片差异模块)模块计算完整图片和有缺口的图片之间的差异,然后将差异数组通过threshold设置阈值,然后将设置完阈值的图像通过findContours函数识别轮廓,然后通过boundingRect就能得到缺口的简单轮廓数据(起始坐标x,y以及宽和高w,h),我们拿到轮廓1的x1,y1数据,然后将缺口图片做灰度处理,然后将通过numpy转为图像数组,然后通过threshold和findContours以及boundingRect函数得到轮廓2的x2,y2数据,最后x1-x2的值就是我们需要滑动的长度,最后将这个值通过selenium滑动模块就可以了,详细的设计在下面的代码中体现。

详细代码

  • image_diff.py图片处理文件
    from skimage.measure import compare_ssim
    import argparse
    import imutils
    import cv2
    import numpy as np
    from base64 import b64decode
    
    class Diff:
        def __init__(self, b1, b2, b3):
            self.imageA = cv2.imdecode(np.frombuffer(b64decode(b1), np.uint8), cv2.COLOR_RGB2BGR)
            self.imageB = cv2.imdecode(np.frombuffer(b64decode(b2), np.uint8), cv2.COLOR_RGB2BGR)
            self.imageC = cv2.imdecode(np.frombuffer(b64decode(b3), np.uint8), cv2.COLOR_RGB2BGR)
    
        def show(self):
            cv2.imshow('imga', self.imageA)
            cv2.imshow('imgb', self.imageB)
            cv2.imshow('imgc', self.imageC)
            if chr(cv2.waitKey(0)) == 'q':
                cv2.destroyAllWindows()
    
        def hOption(self):
            grayA = cv2.cvtColor(self.imageA, cv2.COLOR_BGR2GRAY)
            grayB = cv2.cvtColor(self.imageB, cv2.COLOR_BGR2GRAY)
            grayC = cv2.cvtColor(self.imageC, cv2.COLOR_BGR2GRAY)
            npArr_3 = np.array(grayC)
            return self.diff(grayA, grayB, npArr_3)
    
        def diff(self, grayA, grayB, npArr_3):
            (score, diff) = compare_ssim(grayA, grayB, full=True)
            diff = (diff * 255).astype("uint8")
            print('图片相似度为:%s' % score)
            return self.tf(diff, npArr_3)
    
        def tf(self, diff, npArr_3):
            thresh = cv2.threshold(diff, 0, 255,
                                   cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
            cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
                                    cv2.CHAIN_APPROX_SIMPLE)
            cnts = cnts[1] if imutils.is_cv3() else cnts[0]
    
            img3_t = cv2.threshold(npArr_3, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    
            cnts_3 = cv2.findContours(img3_t.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            cnts_3 = cnts_3[1] if imutils.is_cv3() else cnts_3[0]
            return self.getBr(cnts, cnts_3)
    
        def getBr(self, cnts, cnts_3):
            x1, y1, w1, h1 = cv2.boundingRect(cnts[-1])
            cv2.rectangle(self.imageA, (x1, y1), (x1 + w1, y1 + h1), (0, 0, 255), 1)
            cv2.rectangle(self.imageB, (x1, y1), (x1 + w1, y1 + h1), (0, 0, 255), 1)
            x2, y2, w2, h2 = cv2.boundingRect(cnts_3[-1])
            cv2.rectangle(self.imageC, (x2, y2), (x2 + w2, y2 + h2), (0, 0, 255), 1)
            cv2.rectangle(self.imageC, (x2, y2), (x2 + w2, y2 + h2), (0, 0, 255), 1)
            return x1 - x2
    
  • seleclass.py主入口文件
    # -*- coding: utf-8 -*-
    import json
    import time
    from selenium import webdriver
    from selenium.webdriver.common.action_chains import ActionChains
    from image_diff import Diff
    
    class SeleClass:
        def __init__(self, url, username, password):
            self.url = url
            self.wdc_option = webdriver.ChromeOptions()
            self.wdc_option.add_argument('--headless')
            self.wdc_option.add_argument('--disable-gpu')
            self.wdc = webdriver.Chrome(options=self.wdc_option)
            self.wdc.implicitly_wait(10)
            self.wdc.get(self.url)
            self.wdc.maximize_window()
            self.input(username, password)
    
        def input(self, username, password):
            self.wdc.find_element_by_id('login-username').send_keys(username)
            self.wdc.find_element_by_id('login-passwd').send_keys(password)
            self.mouseLogin()
    
        def mouseLogin(self):
            ActionChains(self.wdc).move_to_element(self.wdc.find_elements_by_class_name('btn-login')[0]).perform()
            ActionChains(self.wdc).click().perform()
            self.mouseMove(Diff(**self.getBase64Data()).hOption())
            self.isLogin()
    
        def doScript(self, script):
            for _ in range(10):
                try:
                    if len(self.wdc.execute_script(script)) < 3000:
                        print('错误数据大小:%s' % len(self.wdc.execute_script(script)), end='\\t')
                        raise BaseException()
                    return self.wdc.execute_script(script)
                except:
                    time.sleep(1)
            raise BaseException('拉取数据超时')
    
        def mouseMove(self, long):
            print('鼠标移动位移为:%s' % long)
            ActionChains(self.wdc).click_and_hold(
                self.wdc.find_elements_by_class_name('geetest_slider_button')[0]).perform()
            ActionChains(self.wdc).move_by_offset(long, 0).perform()
            time.sleep(1)
            ActionChains(self.wdc).release().perform()
    
        def getBase64Data(self):
            bg = self.doScript("return $('.geetest_canvas_bg')[0].toDataURL('image/png')")[22::]
            full = self.doScript("return $('.geetest_canvas_fullbg')[0].toDataURL('image/png')")[22::]
            slice = self.doScript("return $('.geetest_canvas_slice')[0].toDataURL('image/png')")[22::]
            print('真实数据大小:', len(bg), len(full), len(slice))
            return {
                'b1': bg,
                'b2': full,
                'b3': slice
            }
    
        def isLogin(self):
            for _ in range(10):
                if self.wdc.get_cookie('DedeUserID'):
                    self.wdc.get('<https://space.bilibili.com/>' + self.wdc.get_cookie('DedeUserID')['value'])
                    print('登陆成功:%s' % self.wdc.find_element_by_id('h-name').text)
                    return
                else:
                    time.sleep(1)
            raise BaseException('页面跳转超时')
    
        def __del__(self):
            time.sleep(2)
            self.wdc.close()
    
    if __name__ == '__main__':
        s = SeleClass('<https://passport.bilibili.com/login>', 'username', 'password')
    

效果展示

202408091130991

202408091130684

202408091130373

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇