极验验证识别

验证原理 java实列讲解 java 验证 阅读详情

你可能曾经遇到过需要在网站登录时校验验证码的情况,这就是一种叫做“极验”的安全技术服务。极验是行为式验证安全技术服务的领先者,他们提供验证码服务,让用户在登录网站时需要输入验证码来确保安全性。如果你曾经在某些网站登录时需要校验验证码,那很有可能这个验证码就是由极验提供的。

PS:动手能力弱得朋友可以直奔www.ttocr.com进行使用。

例如某“XX”官网,它使用了极验验证码来保护用户账户的安全。一种常见的破解极验验证码的方法是计算缺口位置,使用Selenium自动化测试工具模拟人类手动拖动滑块的过程。虽然这种方法实现简单,但存在两个缺点:首先,模拟滑动容易被极验检测到自动化软件,导致滑动操作失败;其次,每次登录任务都需要驱动浏览器,登录耗时较长,不适合实时性要求较高的数据采集任务。

因此,一些人选择更艰难的方法,他们死磕JavaScript代码,破解每个请求中的加密参数,然后在程序中发送请求以获取正确响应。这种方法最直接且高效,无需等待浏览器对页面进行渲染,只要解出加密的请求参数,发送请求获取响应即可。需要注意的是,用户在浏览器中的操作最终都会转化为请求发送到服务端,服务端对请求参数进行合法性校验,并响应结果。

请求参数分析

我使用登录方法作为分析入口。我输入错误的账号密码,然后点击滑动验证码直到正确的位置。此后,页面会提示“账户名或密码错误!”。我查看浏览器的Network栏,发现登录动作实际上是发送了一个名为“accLoginPC.do”的请求。

该请求的URL为“https://xxx.xxx/webapi/accLoginPC.do”(已隐去敏感信息)。

该请求参数Form Data中包含多个参数,如“appId”、“loginName”、“loginPwd”、“geetest_challenge”等。可以看到,密码已被加密为一长串字符“CN-SA95...”,另外还有三个以“geetest_”开头的加密参数,分别为:

• “geetest_challenge: 102f7d723ad76e387ad6000f87ff91f8j3”

• “geetest_validate: 651ecdf62cb1e940e5ea999b6af7fc10”

• “geetest_seccode: 651ecdf62cb1e940e5ea999b6af7fc10|jordan”

从参数命名上,我们可以清楚地看到这是极验滑动验证码的加密参数。也就是说,我们的点击和拖动操作最终会转换为这三个加密参数。因此,我们的主要工作就是破解这三个参数。注意到,其中的“geetest_validate”和“geetest_seccode”参数基本相同,只是后者多了一个“|jordan”的字符串后缀。因此,我们的主要工作是解密“challenge”和“validate”两个参数。

对于其他非极验加密参数,如“loginPwd”、“jtSafeKey”、“token”等,这些都是XX官网自身的加密逻辑,破解难度不大。因此,在本文中,我们将重点关注极验参数的破解。

我们进一步查看,发现了一个名为“ajax.php?”的请求,其中响应信息类似于JSON格式,其中包含“validate”参数。惊讶的是,这不就是我们之前提到的“geetest_validate”参数吗?

仔细观察其请求参数,我们发现它包含“gt”、“challenge”、“lang”、“w”和“callback”等参数。其中,“w”被加密为一长串字符。

通过分析,我们逐渐理清了思路,确定了每个请求需要解密哪些参数。接下来,我们需要查看 JavaScript 代码中这些参数的加密方式,逐步解决这个问题。

然而,极验为了增加破解的难度,对代码进行了大量的混淆操作,使得代码几乎无法被理解。它采用了 Unicode 编码和大量混淆代码,这导致我们无法直接搜索关键参数名如“challenge”来找到相关代码。

因此,我们需要将完整的 JavaScript 代码复制出来,然后使用一个 Unicode 编码还原工具进行解码。解码后,我们可以将代码复制到编辑器中,搜索“challenge”参数,终于找到了几个关键的参数。

然而,这些代码仍然难以阅读,因为极验对每个关键参数都进行了混淆。它使用类似于“UtTS”这样的代码进行替换,具体的混淆逻辑藏在 JavaScript 代码中。我们需要进一步还原代码。

此外,我们还会发现一些看起来毫无意义的代码,例如“var xow_list = uklgT.xow”。经过研究,我们发现这实际上是极验加入的冗余代码,主要目的是为了混淆视听。

比如,对于这样的冗余代码:

function tPcX(e) {
    var SkB = uklgT.yaA()[0][22];

    for (; SkB !== uklgT.yaA()[16][19]; ) {
        switch (SkB) {
        case uklgT.yaA()[0][22]:

            var t = this;

            var r = e["DxJq"];

            SkB = uklgT.yaA()[0][21];

            break;

        case uklgT.yaA()[16][21]:

            r["height"] = r["width"] = 0;

            t["vjyG"] = r["getContext"]("2d");

            SkB = uklgT.yaA()[4][20];

            break;

        case uklgT.yaA()[4][20]:

            t["wOTb"] = t["xmDd"] = t["yZRm"] = t["AZ_O"] = 0;

            t["BnKG"] = r;

            SkB = uklgT.yaA()[4][19];

            break;

        }

    }

}

去冗余之后,就变成了以下精简且易读的代码:

function tPcX(e) {
    var t = this;

    var r = e["DxJq"];

    r["height"] = r["width"] = 0;

    t["vjyG"] = r["getContext"]("2d");

    t["wOTb"] = t["xmDd"] = t["yZRm"] = t["AZ_O"] = 0;

    t["BnKG"] = r;

}

我们对多个 JavaScript 文件进行反混淆和去冗余的处理后,JavaScript的调用逻辑变得更加清晰了。此外,还需要从JavaScript代码中提取出关键的代码,即用于加密请求参数的代码。这个过程称为“代码解绑定”。 这个过程并不需要太高深的技巧,需要的是耐心和细心。通过在Chrome浏览器中打断点,分析请求的入口和出口,逐步剥离出关键代码。例如,如果需要解密参数a,就需要抽离出加密参数a的代码,并封装为一个名为“get_a()”的函数。将所需的JavaScript函数封装后,在Python程序中,我们可以使用PyExecJS库方便地执行JavaScript代码,以获取加密参数。以下是一些示例代码:slide.js/fullpage.js 等。

def get_js_object(js_file_path):

    """获取js可执行对象"""

    with open(os.path.dirname(__file__) + js_file_path, encoding='GBK') as f:

        js_file = f.read()

        return execjs.compile(js_file)

pwd_encrypt_js = get_js_object(pwd_encrypt_js_path)

full_page_t1_js = get_js_object(full_page_t1_js_path)

full_page_w1_js = get_js_object(full_page_w1_js_path)

full_page_w2_js = get_js_object(full_page_w2_js_path)

u_js = get_js_object(u_js_path)

slide_u_js = get_js_object(slide_u_js_path)

slide_a_js = get_js_object(slide_a_js_path)

def get_encrypt_pwd(pwd):

    """获取加密后的密码"""

    return pwd_encrypt_js.call('pwdEncrypt', pwd)

def get_full_page_t1(s):

    """获取fullpage的t1参数"""

    return full_page_t1_js.call('get_t', s)

def get_full_page_w1(gt, challenge, s):

    """获取fullpage的w1参数"""

    t = get_full_page_t1(s)

    return full_page_w1_js.call('get_w', gt, challenge, s, t)

def get_full_page_w2(gt, challenge, s):

    """获取fullpage的w2参数"""

    return full_page_w2_js.call('get_w', gt, challenge, s)

def get_slide_w(gt, challenge, s, offset, track):

    """获取slide的w参数"""

    u = {
        'lang': 'zh-cn',

        'userresponse': u_js.call('getUserResponse', offset - 1, challenge),

        'passtime': track[-1][-1],

        'imgload': random.randint(110, 180),

        'a': u_js.call("mouse_encrypt", track),

        'ep': {"v": "1.2", "f": u_js.call("lmWn", gt + challenge)},

        'rp': u_js.call("lmWn", gt + challenge[0:32] + str(track[-1][-1]))

    }

    u = slide_u_js.call('_encrypt', u, s)

    a = slide_a_js.call('get_a', s)

    return u + a

滑动轨迹采集

在完成代码解绑定之后,还需要做一件重要的工作,就是采集滑动轨迹。在之前的代码中,函数get_slide_w()中包含有偏移量offset和轨迹track。对于偏移量offset,我们需要获取两张验证码图片,分别有和没有缺口的版本,并通过对比像素计算出偏移量。而验证码图片还需要还原,因为我们通过URL获取到的图片是经过打乱的。我们需要根据极验的JavaScript代码对这两张打乱的图片进行还原,具体还原逻辑不再赘述。对于滑动轨迹track,若使用自动化工具模拟人类拖动滑块,容易被识别为机器行为而导致滑动失败。为了解决这个问题,我采用人工手动拖动验证码的方式收集一批轨迹,构成轨迹库。之后,只要算出滑动验证码缺口的偏移量,从轨迹库中找到与偏移量相近的轨迹即可。虽然有一定的错误率,比如,轨迹库不够丰富而导致取不到偏移量对应的轨迹,但是我们可以在接下来的程序里采用一种特殊的优化方式对其进行优化,可以保证每次登录100%成功。

以上便是整个极验滑动验证码破解的过程,再简单总结一下:

请求参数分析

代码反混淆

代码解绑定

滑动轨迹采集

其中,还包括验证码图片的还原,图片缺口的计算等等...

在文章中还提到了一种特殊的优化方式,即极验Session验证码池的设计。我们可以预先滑动好验证码(实际上就是发请求),然后把得到成功响应的Session存起来,放在一个池子中。这样做的好处主要有两点:提高登录效率,缩减耗时;解耦合,提高数据采集程序的健壮性。当有登录任务到来时,我们只需要从池子中取出一个极验Session,加上登录任务中的用户名/密码(按照XX官网,密码还需做加密)直接去登录,是不是既简单又高效呢?

本文主要介绍了如何设计一个自动更新和过期删除的验证码池子。作者使用 Redis Sorted Sets(zset)数据类型来存储极验 Session,将当前时间戳作为元素的 score,zset 会根据 score 对插入元素进行排序。这样,可以使用 zrevrange 命令方便地获取最新的一个验证码 session,并使用 zremrangebyscore 移除某个时间范围内的验证码,从而达到自动过期删除的目的。

作者提到,生产极验 Session 并放入池子的示例代码如下。

import json

import pickle

import re

import time

from io import BytesIO

from threading import Thread

from PIL import Image

from requests import session

from config import gt_register_url, common_login_headers, get_php_url, ajax_php_url, prefix_url, geetest_session_key

from param import get_full_page_w1, get_full_page_w2, get_track, get_slide_w, get_s

from utils.captcha import calculate_offset

from utils.fetch import fetch

from utils.logger import logger

from utils.response import Resp

from utils.times import now_str

from utils.zk import client

class GSession:

    """获取极验Session"""

    def __init__(self):

        self.session = session()

        self.res = Resp.SUCCESS

        self.gt = str()

        self.challenge = str()

        self.s = get_s()

        self.validate = str()

        self.sec_code = str()

        self.bg_url = str()

        self.full_bg_url = str()

        self.offset = 0

        self.track = list()

    def set_gt_challenge(self) -> bool:

        """发送网络请求,拿到gt和challenge"""

        params = dict(t=now_str())

        resp = fetch(self.session, url=gt_register_url, headers=common_login_headers, params=params)

        if resp is None:

            logger.warning('无法获取gt/challenge...')

            self.res = Resp.TIMEOUT

            return False

        res = resp.json()

        logger.info('gt/challenge请求结果:{}'.format(res))

        self.gt, self.challenge = res['gt'], res['challenge']

        return True

    def get_php(self):

        """注册参数s:s经过多层加密拼接成w"""

        params = {
            'gt': self.gt,

            'challenge': self.challenge,

            'lang': 'zh-cn',

            'w': get_full_page_w1(self.gt, self.challenge, self.s),

            'callback': 'geetest_' + now_str()

        }

        resp = fetch(self.session, url=get_php_url, headers=common_login_headers, params=params)

        if resp is None:

            logger.warning('无法注册参数s...')

            self.res = Resp.TIMEOUT

        return resp is not None

    def ajax_php(self, step=1, params=None):

        """

        step=1:发送请求,校验参数w

        step=2:滑动滑块

        """

        if step == 1:

            params = {
                'gt': self.gt,

                'challenge': self.challenge,

                'lang': 'zh-cn',

                'w': get_full_page_w2(self.gt, self.challenge, self.s) + '1',

                'callback': 'geetest_' + now_str()

            }

        resp = fetch(self.session, url=ajax_php_url, headers=common_login_headers, params=params)

        if resp is None:

            self.res = Resp.TIMEOUT

            return False

        if step != 1:

            res = json.loads(re.search(r'(.∗?)
(
.

?
)
', resp.text, re.S).group(1))

            if res['data']['result'] != 'success':

                self.res = Resp.SLIDE_ERR

                return False

            self.validate = res['data']['validate']

            self.sec_code = self.validate + '|jordan'

        return True

    def get_slide_images(self):

        """获取验证码图片的地址"""

        params = {
            'is_next': 'true',

            'type': 'slide3',

            'gt': self.gt,

            'challenge': self.challenge,

            'lang': 'zh-cn',

            'https': 'true',

            'protocol': 'https://',

            'offline': 'false',

            'product': 'popup',

            'api_server': 'captcha-api.com',

            'width': '100%',

            'callback': 'geetest_' + now_str()

        }

        resp = fetch(self.session, url=get_php_url, headers=common_login_headers, params=params)

        if resp is None:

            self.res = Resp.TIMEOUT

            return False

        res = json.loads(re.search(r'(.∗?)
(
.

?
)
', resp.text, re.S).group(1))

        # 获得滑动验证码图片的URL(带缺口+不带缺口)

        self.bg_url = prefix_url + res['data']['bg']

        self.full_bg_url = prefix_url + res['data']['fullbg']

        logger.info('滑动验证码图片,bg_url:{}, full_bg_url:{}'.format(self.bg_url, self.full_bg_url))

        # 更新gt/challenge

        self.gt = res['data']['gt']

        self.challenge = res['data']['challenge']

        return True

    def get_track(self):

        """获取滑动轨迹"""

        resp1 = fetch(self.session, url=self.bg_url, headers=common_login_headers)

        resp2 = fetch(self.session, url=self.full_bg_url, headers=common_login_headers)

        if not (resp1 and resp2):

            self.res = Resp.TIMEOUT

            return False

        img1 = Image.open(BytesIO(resp1.content))

        img2 = Image.open(BytesIO(resp2.content))

        # 计算偏移量

        self.offset = calculate_offset(img1, img2)

        # 根据偏移量获取轨迹

        self.track = get_track(self.offset)

        if self.track is None:

            self.res = Resp.TRACK_ERR

        return self.track is not None

    def slide(self):

        """滑动滑块"""

        params = {
            'gt': self.gt,

            'challenge': self.challenge,

            'lang': 'zh-cn',

            'w': get_slide_w(self.gt, self.challenge, get_s(), self.offset, self.track),

            'callback': 'geetest_' + now_str()

        }

        return self.ajax_php(step=2, params=params)

    def run(self):

        self.set_gt_challenge() and self.get_php() and self.ajax_php() and self.get_slide_images() and self.get_track() and self.slide()

        logger.info('获取极验session结果:{}'.format(self.res))

        return self.res == Resp.SUCCESS

def produce_session():

    """生产极验session"""

    while True:

        gs = GSession()

        try:

            if gs.run():

                res = pickle.dumps(gs.session)

                client.zadd(geetest_session_key, {res: time.time()})

            else:

                logger.error('获取极验session请求出错')

        except Exception as e:

            logger.error('获取极验session失败,错误信息:{}'.format(e))

        time.sleep(4)

def pop_session():

    """从池子中弹出极验session"""

    res = client.zrevrange(geetest_session_key, 0, 0, withscores=True)

    if res:

        g_session, score = res[0]

        g_session = pickle.loads(g_session)

        client.zremrangebyscore(geetest_session_key, min=score, max=score)

        return g_session

    return None

if __name__ == '__main__':

    thread_list = []

    for i in range(4):

        thread = Thread(target=produce_session)

        thread.start()

        thread_list.append(thread)

    for thread in thread_list:

        thread.join()

验证码定期删除的代码如下:

登录后复制

def expire_schedule():

    """定期删除"""

    client.zremrangebyscore(geetest_session_key, 0, time.time() - 3600)

    timer = threading.Timer(2, expire_schedule)

    timer.start()

if __name__ == '__main__':

    expire_schedule()

以上,全篇完!

如果上述代码遇到问题或已更新无法使用等情况可以联系Q:1436423940或直接访问www.ttocr.com测试对接(免费得哈)
 

爬虫漫游指南:聊聊(Geetest) 上至政务类网站国家企业信用信息公示系统,下至娱乐类网站斗鱼战旗龙珠,都可以看到的身影。于是,如何绕过自然成为了许多爬虫小伙伴不得不面对的问题。本文就来聊一聊,爬虫遇到用的网站,该如何是好。 阅读详情

相关推荐

爬虫之滑动验证码的识别

一 目标 用程序来识别并通过验证码的验证,包括分析识别思路、识别缺口位置、生成滑块拖动路径、模拟实现滑块拼合通过验证等步骤。 二 了解验证码 1 官网 https://www.geetest.com/ 2 原理 主要验证方法是拖动滑块拼合图像。若图像完全拼合,则验证成功,即表单成功提交,否则需要重新验证。 举例:魅族登录页面采用了验证码。https://login.fly...

实践求真知 3740

易语言-bilibili验证识别

bilibili验证识别源码

验证识别例子

很多网站的登陆都有验证码一项,而的方案就是应用的非常普遍。更多的场景是在反爬虫的对抗中,验证码更是首选。本次目标则是用程序来识别并通过验证码的验证。本次使用的是Python库是selenium库,Chrome浏览器,并配置好ChromeDriver。验证码官网为:https://auth.geetest.com/login/。它是一个专注于提供验证安全的系统,主要验证方式是拖动滑块拼合图像。若图像完全拼合,则验证成功,即表单成功提交,否则需要重新验证,如图所示: 1.先明确识别步骤:首先,

易云云博客 991

[639]验证码破解分析&滑动验证码的识别

本节我们分析并实现了验证码的识别,其关键在于识别的思路,如怎样识别缺口位置,怎样生成运动轨迹等,学会了这些思路后以后我们再遇到类似原理的验证码同样可以完成识别过程。最后,放上代码,有需要的小伙伴可以自取,在使用时请注意,需要更改自己的账号密码,如果没有,则需要注册。'''验证码特点:首先点击按钮进行智能验证,如果验证不通过,则会弹出滑动验证的窗口,拖动滑块拼合图像进行验证,之后生成三个加密参数,通过表单提交到后台,后台还会进行一次验证识别验证需要三步:1.模拟点击验证按钮。

周小董 7516

验证(滑动验证)的使用

验证目录一、样例二、注册账号三、获取ID四、官方文档(参考)五、SpringBoot集成5.1、maven依赖(可能有些需要自己去导,个人的包依赖太多不好全部放上来,核心就这两个)5.2、yml配置文件5.3、util类(读本地ip)5.4、读取配置类5.5、获取的第一次数据包5.5.1、在资源服务器里面放行路径5.5.2、添加控制器 GeetestController5.5.3、在gateway网关里面添加 CorsConfig 一、样例 二、注册账号 验证官网 在表单里面填写真

zhouhengzhe的博客 1万+

静态网页版验证测试项目 - 简单、高效、开源的验证码解决方案

静态网页版验证测试项目 - 简单、高效、开源的验证码解决方案 去发现同类优质开源项目:https://gitcode.com/ 该项目,由在GitCode上开源,是一个静态网页版本的验证测试实现。验证是一种新型的验证码技术,旨在提供更加智能和用户友好的验证,而这个项目则将这种验证机制融入到纯静态网页环境中。 项目简介 项目的核心在于通过JavaScript模拟验证的客户端行为,...

gitblog_00015的博客 585

Python爬虫系列之滑动验证码的识别

1. 本节目标 本节我们的目标是用程序来识别并通过验证码的验证,其步骤有分析识别思路、识别缺口位置、生成滑块拖动路径,最后模拟实现滑块拼合通过验证。 2. 准备工作 本次我们使用的 Python 库是 Selenium,使用的浏览器为 Chrome,在此之前请确保已经正确安装好了 Selenium 库、Chrome浏览器并配置好了 ChromeDriver,相关流程可以参考第一章的说明。 3....

qq_42992919的博客 2409

验证码的识别】—— 验证码的识别

目前,许多网站采取各种各样的措施来反爬虫,其中一个措施便是使用验证码。随着技术的发展,验证码的花样越来越多。验证码最初是几个数字组合的简单的图形验证码,后来加入了英文字母和混淆曲线。有的网站还可能看到中文字符的验证码,这使得识别愈发困难。后来 12306 验证码的出现使得行为验证码开始发展起来,用过 12306 的用户肯定多少为它的验证码头疼过。我们需要识别文字,点击与文字描述相符的图片,验证码完全正确,验证才能通过。

爱吃饼干的小白鼠的博客 1196

Python 爬虫实战 滑动验证码的识别

上节我们了解了图形验证码的识别,简单的图形验证码我们可以直接利用 Tesserocr 来识别,但是近几年又出现了一些新型验证码,如滑动验证码,比较有代表性的就是验证码,它需要拖动拼合滑块才可以完成验证,相对图形验证码来说识别难度上升了几个等级,本节来讲解下验证码的识别过程。 1. 本节目标 本节我们的目标是用程序来识别并通过验证码的验证,其步骤有分析识别思路、识别缺口位置、生成滑块拖动路径,最后模拟实现滑块拼合通过验证。 2. 准备工作 本次我们使用的 Python 库是 Seleniu

易云云博客 1757

第四代滑块验证码破解(二):滑块缺口识别

第四代滑块验证码破解(二):滑块缺口识别声明 声明 原创文章,请勿转载! 本文内容仅限于安全研究,不公开具体源码。维护网络安全,人人有责。 本文关联文章超链接:

帯泪的鱼的博客 1万+

使用F#实现滑动验证识别

wait.Until(fun driver -> driver.FindElement(By.ClassName("geetest_success_radar_tip_content")).Text.Contains("验证成功")) |> ignore。在这篇文章中,我们使用F#实现了滑动验证码的识别。代码中使用了Selenium库来操作浏览器,通过模拟点击、获取验证码图片、计算滑动距离,并最终完成滑块拖动验证。从模拟点击到识别滑动缺口、计算位移并模拟拖动滑块。识别滑动缺口的位置,计算位移。

asfdsgdf的博客 780

滑动验证识别教程

EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '验证成功')首先等待验证码加载完成(wait_pic),获取网页截图(get_screenshot),然后获取验证背景图所在的位置及大小参数(get_position)和滑块对象(get_slider)。get_gap()方法遍历两张图片的每个像素,再利用is_pixel_equal()方法判断两张图片同一位置的像素。

asfdsgdf的博客 745

Python_滑动验证识别

滑动验证码 1、准备工作 安装好Selenium库,Chrome浏览器,配置ChromeDriver 目标:用程序来识别并通过点触验证码的验证 点触验证码服务站点TouClick:https://www.touclick.com ...

十一姐的博客 1256

验证识别

验证码是一种常见的人机验证机制,通过交互式操作的方式,防止机器人自动化攻击网站。验证识别是指对验证码进行分析和处理,以实现对验证码中的图形、轨迹、滑动轨迹等的自动识别和破解。验证码的识别相对比较复杂,包括对验证码的滑动轨迹、图形、缺口、干扰线等进行处理和分析。

hdhddhdjxjc的博客 1089

【Python爬虫教程】进阶篇-06 滑动验证码的识别

摘要:了解滑动验证码、特点、识别思路、初始化、模拟点击、识别缺口、模拟拖动。

仲君Johnny的博客 1642

[Python3网络爬虫开发实战] 滑动验证码的识别

上节我们了解了图形验证码的识别,简单的图形验证码我们可以直接利用 Tesserocr 来识别,但是近几年又出现了一些新型验证码,如滑动验证码,比较有代表性的就是验证码,它需要拖动拼合滑块才可以完成验证,相对图形验证码来说识别难度上升了几个等级,本节来讲解下验证码的识别过程。 本节目标 本节我们的目标是用程序来识别并通过验证码的验证,其步骤有分析识别思路、识别缺口位置、生成滑块拖动路...

python学习教程 632

滑动验证识别

let success = self.client.find(Locator::Css(".geetest_success_radar_tip_content")).unwrap().text().unwrap() == "验证成功";首先等待验证码加载完成(wait_pic),获取网页截图(get_screenshot),然后获取验证背景图所在的位置及大小参数(get_position)和滑块对象(get_slider)。执行主体流程,若验证失败,则再次调用crack()进行识别,直至成功。

asfdsgdf的博客 1009
上一篇: 极验验证码识别
下一篇: 极验验证识别
gdf;lkgk;algl'dh
博客等级 码龄3年 0粉丝 33原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值