最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

opencv+playwright滑動(dòng)驗(yàn)證碼的實(shí)現(xiàn)

 更新時(shí)間:2023年11月03日 14:51:12   作者:la_vie_est_belle  
滑動(dòng)驗(yàn)證碼是常見(jiàn)的驗(yàn)證碼之一,本文主要介紹了opencv+playwright滑動(dòng)驗(yàn)證碼的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下

在本節(jié),我們將使用opencv和playwright這兩個(gè)庫(kù)通過(guò)QQ空間的滑動(dòng)驗(yàn)證碼。 

梳理思路

1. 使用playwright打開瀏覽器,訪問(wèn)qq空間登錄頁(yè)面

2. 點(diǎn)擊密碼登錄。

3. 輸入賬號(hào)密碼并點(diǎn)擊登錄。

4. 出現(xiàn)滑動(dòng)驗(yàn)證碼圖片后,我們就可以獲取到驗(yàn)證碼背景圖以及滑塊圖片。驗(yàn)證碼背景圖片通過(guò)元素style中的url鏈接就可以獲取到,由于下載保存的是原圖,所以我們要將寬度調(diào)整為280px,280這個(gè)值同樣也可以在style中看到。

注:從style中也可以看到height值為200px,但其實(shí)這個(gè)包含了下方滑軌的高度,因此圖片的真實(shí)高度要小于200px。所以我們?cè)谡{(diào)整原圖大小時(shí),高度不要設(shè)為200px,而是通過(guò)以下公式進(jìn)行等比縮放。

調(diào)整后的高度 = 原圖高/(原圖寬/280)

5. 我們同樣可以找到滑塊圖片的鏈接,但打開后卻是這樣的。

由于不知道滑塊在這張大圖上的位置,所以無(wú)法有效截取。另一種方案是直接通過(guò)屏幕截取獲得滑塊圖片。首先,對(duì)全屏幕進(jìn)行截圖,然后用playwright獲取到滑塊元素,獲取到該元素的位置和大小后,就可以截取了?;瑝K的起始位置就是style中的left值。

6. 驗(yàn)證碼背景圖有了,滑塊有了,滑塊初始位置也有了,接下來(lái)就是判斷背景圖的缺口位置,再求出滑動(dòng)距離。我們可以使用opencv-python的matchTemplate()和minMaxLoc()方法獲取缺口的x坐標(biāo)。拿到缺口x坐標(biāo)后減去滑塊的x坐標(biāo)就可以求出滑動(dòng)距離了。

為了讓matchTemplate()的結(jié)果更加準(zhǔn)確,我們可以對(duì)滑塊圖片做下處理,調(diào)整下對(duì)比度,讓它暗一些,跟缺口差不多。

注:minMaxLoc()這個(gè)函數(shù)返回一個(gè)最大值和最小值,因?yàn)闊o(wú)法知道哪一個(gè)是正確的,所以我們兩個(gè)值都應(yīng)該拿來(lái)驗(yàn)證。也就是說(shuō),我們會(huì)拿到兩個(gè)距離值,并且可能要滑動(dòng)兩次。當(dāng)然,這個(gè)其實(shí)沒(méi)啥關(guān)系,因?yàn)榛瑒?dòng)驗(yàn)證碼驗(yàn)證失敗了的話,是可以再次滑動(dòng)的。

7. 位置拿到之后,就是用鼠標(biāo)控制滑軌上的按鈕并進(jìn)行滑動(dòng)操作?;瑒?dòng)操作要真實(shí),不能勻速,滑動(dòng)時(shí)鼠標(biāo)肯定也會(huì)有上下抖動(dòng),總之要盡量模擬人的滑動(dòng)操作。

8. 滑動(dòng)成功的話下方的滑軌元素就會(huì)消失,我們可以通過(guò)這點(diǎn)來(lái)判斷是否通過(guò)了滑動(dòng)驗(yàn)證碼。如果滑了兩次都沒(méi)有通過(guò)(小概率),則刷新驗(yàn)證碼并再次執(zhí)行步驟4,5,6,7,8。

編寫代碼

根據(jù)以上思路,我們可以編寫出如下代碼。

from playwright.sync_api import sync_playwright
from PIL import Image
import numpy as np
import cv2 as cv
import requests
import random
import re
 
 
class QQZonSlide:
    def __init__(self):
        self.login_url = "https://i.qq.com/"
        self.username = "你的賬號(hào)"
        self.password = "你的密碼"
        self.page = None
 
    def start(self):
        with sync_playwright() as p:
            self.init_page(p)
            self.login()
 
            while True:
                self.get_slide_bg_img()
                start_x = self.get_slide_block_img_and_start_x()
                distance1, distance2 = self.get_slide_distance(start_x)
                slide_result = self.move_to_notch(distance1, distance2)
                if not slide_result:
                    self.refresh_captcha()
                else:
                    break
 
    def init_page(self, p):
        """初始化瀏覽器,獲取page對(duì)象"""
        browser = p.chromium.launch(headless=False)
        self.page = browser.new_page()
 
    def login(self):
        """通過(guò)賬號(hào)密碼登錄"""
        print("開始登錄")
 
        # 訪問(wèn)頁(yè)面
        self.page.goto(self.login_url)
 
        # 定位到登錄框元素并點(diǎn)擊密碼登錄
        login_frame = self.page.frame_locator("#login_frame")
        login_frame.get_by_role("link", name="密碼登錄").click()
 
        # 清空賬號(hào)框然后輸入賬號(hào)
        login_frame.locator("#u").clear()
        login_frame.locator("#u").fill(self.username)
 
        # 清空密碼框然后輸入密碼
        login_frame.locator("#p").clear()
        login_frame.locator("#p").fill(self.password)
 
        # 點(diǎn)擊登錄按鈕
        self.page.wait_for_timeout(1000)
        login_frame.locator("#login_button").click()
 
    def get_slide_bg_img(self):
        """截取滑動(dòng)驗(yàn)證碼背景圖片"""
        self.page.wait_for_timeout(2000)
        print("正在獲取滑動(dòng)驗(yàn)證碼背景圖片")
 
        # 獲取滑動(dòng)驗(yàn)證碼所在的iframe
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")
 
        # 獲取滑動(dòng)驗(yàn)證碼的背景圖
        slide_bg_style = captcha_iframe.locator("#slideBg").get_attribute("style")
        slide_bg_url = re.search(r'url\("(.+)"\)', slide_bg_style).groups()[0]
        r = requests.get(slide_bg_url)
        with open("./slide_bg.png", "wb") as f:
            f.write(r.content)
 
        # 調(diào)整圖片大小,根據(jù)style內(nèi)容將寬度調(diào)整為280,高度等比例調(diào)整
        img = Image.open("./slide_bg.png")
        ratio = img.width / 280
        img = img.resize(size=(280, int(img.height/ratio)))
        img.save("./slide_bg.png")
 
    def get_slide_block_img_and_start_x(self):
        """獲取滑塊圖片以及初始x坐標(biāo)"""
        print("正在獲取滑塊圖片")
 
        # 首先保存整個(gè)登錄背景截圖
        self.page.screenshot(path="bg.png")
 
        # 獲取滑動(dòng)驗(yàn)證碼所在的iframe
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")
 
        # 獲取滑塊圖片
        # .tc-fg-item對(duì)應(yīng)的有三個(gè)元素,一個(gè)是目標(biāo)滑塊,一個(gè)是滑軌,還有一個(gè)是滑軌上的按鈕
        for i in range(3):
            slide_block_ele = captcha_iframe.locator(".tc-fg-item").nth(i)
            slide_block_style = slide_block_ele.get_attribute("style")
 
            # 滑軌按鈕元素的style值中不包含url字符串
            if "url" not in slide_block_style:
                continue
 
            # 從元素的style值中分析得出只有目標(biāo)滑塊的top值小于150
            top_value = re.search(r'top: (.+)px;', slide_block_style).groups()[0]
            if float(top_value) > 150:
                continue
 
            # 獲取x坐標(biāo)
            slide_block_x = float(re.search(r'left: (.+)px; top: ', slide_block_style).groups()[0])
 
            # 通過(guò)滑塊位置,從背景圖中截取滑塊圖片
            slide_block_rect = slide_block_ele.bounding_box()
            bg = Image.open("./bg.png")
            offset = slide_block_rect["width"] // 4  # 從背景圖上截取會(huì)混入滑塊周圍的一些像素點(diǎn),所以加一個(gè)偏移值,截取到滑塊內(nèi)部的圖片。
            slide_block_img = bg.crop((slide_block_rect["x"] + offset, slide_block_rect["y"] + offset,
                                       slide_block_rect["x"] + slide_block_rect["width"] - offset,
                                       slide_block_rect["y"] + slide_block_rect["height"] - offset))
            slide_block_img.save("slide_block.png")
 
            return slide_block_x + slide_block_rect["width"] // 4
 
    @staticmethod
    def set_contrast_brightness(frame, contrast_value, brightness_value):
        if not contrast_value:
            contrast_value = 0.0
 
        if not brightness_value:
            brightness_value = 0
 
        blank = np.zeros(frame.shape, frame.dtype)
        frame = cv.addWeighted(frame, contrast_value, blank, 1 - contrast_value, brightness_value)
 
        return frame
 
    def get_slide_distance(self, start_x):
        """獲取滑動(dòng)距離"""
        print("正在獲取滑動(dòng)距離")
        # 通過(guò)opencv比較圖片,獲取缺口位置
        slide_bg_img = cv.imread("./slide_bg.png")
        slide_block_img = cv.imread("./slide_block.png")
        slide_block_img = self.set_contrast_brightness(slide_block_img, 0.4, 0)
        result = cv.matchTemplate(slide_block_img, slide_bg_img, cv.TM_CCOEFF_NORMED)
        minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(result)
 
        # 缺口的x坐標(biāo)
        notch_x1 = minLoc[0]
        notch_x2 = maxLoc[0]
 
        # 距離
        distance1 = notch_x1 - start_x
        distance2 = notch_x2 - start_x
        return distance1, distance2
 
    @staticmethod
    def get_tracks(distance):
        """獲取移動(dòng)軌跡"""
        tracks = []                             # 移動(dòng)軌跡
        current = 0                             # 當(dāng)前位移
        mid = distance * 4 / 5                  # 減速閾值
        t = 0.2                                 # 計(jì)算間隔
        v = 0                                   # 初始速度
 
        while current < distance:
            if current < mid:
                a = random.randint(3, 5)        # 加速度為正5
            else:
                a = random.randint(-5, -3)      # 加速度為負(fù)3
 
            v0 = v                              # 初速度 v0
            v = v0 + a * t                      # 當(dāng)前速度
            move = v0 * t + 1 / 2 * a * t * t   # 移動(dòng)距離
            current += move
            tracks.append(round(current))
 
        return tracks
 
    def move_to_notch(self, distance1, distance2):
        """移動(dòng)滑軌按鈕到缺口處"""
        # 獲取滑動(dòng)驗(yàn)證碼所在的iframe
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")
 
        for i in range(2):
            # 獲取按鈕位置,將鼠標(biāo)移到上方并按下
            slider_btn_rect = captcha_iframe.get_by_alt_text("slider").bounding_box()
            self.page.mouse.move(slider_btn_rect['x'], slider_btn_rect['y'])
            self.page.mouse.down()
 
            distance = [distance1, distance2][i]
            if distance <= 0:   # 距離不可能小于等于0
                continue
 
            print(f"正在進(jìn)行第{i+1}次滑動(dòng)")
            tracks = self.get_tracks(distance)
            for x in tracks:
                self.page.mouse.move(slider_btn_rect['x']+x, random.randint(-5, 5)+slider_btn_rect['y'])
            self.page.mouse.move(slider_btn_rect['x'] + tracks[-1] + 5, random.randint(-5, 5) + slider_btn_rect['y'])
            self.page.mouse.move(slider_btn_rect['x'] + tracks[-1] - 5, random.randint(-5, 5) + slider_btn_rect['y'])
            self.page.mouse.up()
 
            # 滑動(dòng)結(jié)束后等待一段時(shí)間
            self.page.wait_for_timeout(2000)
 
            # 尋找按鈕是否還存在,不存在的話表明已通過(guò)滑動(dòng)驗(yàn)證碼,存在的話嘗試下一個(gè)距離
            try:
                captcha_iframe.get_by_alt_text("slider").wait_for(timeout=2000)
            except Exception as e:
                print("已通過(guò)滑動(dòng)驗(yàn)證碼")
                return True
            else:
                print(f"第{i+1}次滑動(dòng)失敗")
 
        return False
 
    def refresh_captcha(self):
        """刷新驗(yàn)證碼"""
        # 獲取滑動(dòng)驗(yàn)證碼所在的iframe
        print("刷新驗(yàn)證碼")
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")
        captcha_iframe.locator("#e_reload").click()
        self.page.wait_for_timeout(2000)
 
 
if __name__ == "__main__":
    slide = QQZonSlide()
    slide.start()

總結(jié)與提高

有時(shí)候就算通過(guò)了滑動(dòng)驗(yàn)證碼,QQ空間也會(huì)提示當(dāng)前網(wǎng)絡(luò)異?;蛘卟话踩瑢?dǎo)致這種情況出現(xiàn)的原因很可能是軌跡出了問(wèn)題,我們可以使用更好的算法生成更真實(shí)的滑動(dòng)軌跡來(lái)避免這種情況出現(xiàn)。

到此這篇關(guān)于opencv+playwright滑動(dòng)驗(yàn)證碼的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)opencv playwright滑動(dòng)驗(yàn)證碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • python實(shí)現(xiàn)控制COM口的示例

    python實(shí)現(xiàn)控制COM口的示例

    今天小編就為大家分享一篇python實(shí)現(xiàn)控制COM口的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-07-07
  • 解決python 讀取npy文件太大不能完全顯示的問(wèn)題

    解決python 讀取npy文件太大不能完全顯示的問(wèn)題

    這篇文章主要介紹了解決python 讀取npy文件太大不能完全顯示的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-03-03
  • django的使用步驟入門教程(很詳細(xì))

    django的使用步驟入門教程(很詳細(xì))

    隨著IT行業(yè)的不斷發(fā)展,編程學(xué)習(xí)也越來(lái)越重要,很多人都開啟了很多計(jì)算機(jī)語(yǔ)言的學(xué)習(xí),下面這篇文章主要給大家介紹了關(guān)于django的使用步驟入門教程,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • 關(guān)于Python Error標(biāo)準(zhǔn)異常的總結(jié)

    關(guān)于Python Error標(biāo)準(zhǔn)異常的總結(jié)

    這篇文章主要介紹了關(guān)于Python Error標(biāo)準(zhǔn)異常的總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Python分布式異步任務(wù)框架Celery使用教程

    Python分布式異步任務(wù)框架Celery使用教程

    Celery 是由Python 編寫的簡(jiǎn)單,靈活,可靠的用來(lái)處理大量信息的分布式系統(tǒng),它同時(shí)提供操作和維護(hù)分布式系統(tǒng)所需的工具
    2023-05-05
  • django實(shí)現(xiàn)類似觸發(fā)器的功能

    django實(shí)現(xiàn)類似觸發(fā)器的功能

    今天小編就為大家分享一篇django實(shí)現(xiàn)類似觸發(fā)器的功能,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-11-11
  • python在不同條件下的輸入與輸出

    python在不同條件下的輸入與輸出

    在本篇文章里小編給大家整理的是關(guān)于python在不同條件下的輸入與輸出,有需要的朋友們可以學(xué)習(xí)下。
    2020-02-02
  • python獲取一組數(shù)據(jù)里最大值max函數(shù)用法實(shí)例

    python獲取一組數(shù)據(jù)里最大值max函數(shù)用法實(shí)例

    這篇文章主要介紹了python獲取一組數(shù)據(jù)里最大值max函數(shù)用法,實(shí)例分析了max函數(shù)的使用技巧,需要的朋友可以參考下
    2015-05-05
  • Python將多個(gè)圖像合并輸出的實(shí)現(xiàn)方法

    Python將多個(gè)圖像合并輸出的實(shí)現(xiàn)方法

    這篇文章主要介紹了Python將多個(gè)圖像合并輸出的實(shí)現(xiàn)方法,本文介紹了兩種將多個(gè)圖像合并為一個(gè)輸出的方法:使用PIL庫(kù)或使用OpenCV和NumPy,這些庫(kù)都可以使用Python中的簡(jiǎn)單語(yǔ)法和少量的代碼來(lái)完成此任務(wù),需要的朋友可以參考下
    2023-06-06
  • 教你30分鐘使用Python制作自己的記事本

    教你30分鐘使用Python制作自己的記事本

    這篇文章主要為大家詳細(xì)介紹了30分鐘如何使用Python制作自己的記事本,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2026-02-02

最新評(píng)論

保康县| 祁东县| 松潘县| 东港市| 漯河市| 虎林市| 靖远县| 威宁| 漯河市| 台北县| 承德县| 洛隆县| 黔西县| 和龙市| 蓬溪县| 墨脱县| 博湖县| 巴楚县| 喀喇沁旗| 丰台区| 延庆县| 霍城县| 呈贡县| 镶黄旗| 苏州市| 清流县| 全南县| 阿坝| 黔西| 得荣县| 株洲市| 高邮市| 冀州市| 甘肃省| 金沙县| 南开区| 遂溪县| 基隆市| 仁化县| 临潭县| 准格尔旗|