搞定验证码和滑块

Frida可视化工具Dwarf2已经开源,大家有什么问题,可以一起交流。

本文内容仅用于学习,严禁用作非法目的。

验证码

作用

验证码作为一种人机识别手段,其终极目的,就是区分正常人和机器的操作。 区分人机行为的作用不言而喻。互联行为的注册、登录、发帖、领优惠券、投票等等应用场景,都有被机器刷造成各类损失的风险,如果不对各类机器垃圾的行为加以防范,灌水内容、垃圾注册、恶意登录、刷票、撞库、活动作弊、垃圾广告、爬虫、羊毛党等用户行为一旦发生,将对产品自身发展、用户体验造成极大的影响。

搞定验证码

很多网站都使用了验证码进行人机识别,给爬虫带来了一定的困扰。常见的验证码如下:
[图片上传失败...(image-851c8f-1648449832834)]
[图片上传失败...(image-c2de3e-1648449832834)]

trwebocr

一个开源的ocr工具,非常强大。官方介绍是: 开源易用的中文离线OCR,识别率媲美大厂,并且提供了易用的web页面及web的接口,方便人类日常工作使用或者其他程序来调用~ 。笔者在github上有关于它的使用。

通过一个例子来感受一下这个工具。以下是我2022虎年快乐这个公众号里面部分内容的截图:
[图片上传失败...(image-b52873-1648449832834)]
trwebocr的识别结果:
[图片上传失败...(image-2c49e7-1648449832834)]所有的文字都被识别出来了!还是非常nice的。

过验证码

有这么强大的工具,过验证码岂不是轻而易举。验证码的图片如下:
[图片上传失败...(image-43a215-1648449832834)]
直接上代码:

import requests;

def main():
    url = 'http://127.0.0.1:8089/api/tr-run/'
    img1_file = {
        'file': open("./imgs/yzm.jpg", 'rb')
    }
    res = requests.post(url=url, data={'compress': 0}, files=img1_file)
    jsonObj = res.json()
    if jsonObj['code'] != 200:
        return
    raw_out = jsonObj['data']['raw_out']
    target_len = len(raw_out)
    for i in range(target_len):
        print(raw_out[i][1])

if __name__ == "__main__":
    main()

JWYN被成功识别出来。大功告成!!!!!!!
[图片上传失败...(image-b1ddac-1648449832834)]

python安装模块的时候建议使用豆瓣源,真的好快。

pip install requests  -i https://pypi.douban.com/simple/
缺陷

trwebocr的准确率达不到100%,不过依然不能掩盖它强大的OCR功能。当然也可以自己实现类似的功能,使用opencv+CNN效果也不错。

滑块

滑块验证码是在网站、APP等应用中常见的一种验证方式,通过按照一定规则滑动滑块到指定位置完成验证,才可以进行下一步操作。滑块验证码有两种设计,一种是在滑动框内“一滑到底”即完成验证的,还有一种是滑动滑块拼合拼图完成验证的,如下图所示。由于拼图式的滑块验证码安全性更高,趣味性更强,“一滑到底”式的滑块验证码已经基本被淘汰。

搞定滑块

使用滑块机制的网站也有好多,增大了爬虫的难度,常见的滑块验证:
[图片上传失败...(image-3da661-1648449832834)]
[图片上传失败...(image-f24d92-1648449832834)]

获取图片

滑块验证第一步需要获取大图片,后面统称为target,以及小图片,后面称为template。具体可参见自动登陆QQ空间(3)
(1) 如果target和template都可以正常下载的话,直接进行下载。
(2 ) 如果不能下载的话,可以使用两种方式进行获取:

第一种方式是使用chromeDriver的截图功能

def get_imgs():
    driver.save_screenshot('imgs/screenshot.png')
    bigImage = driver.find_element_by_id('bigImage')
    left = (int)(bigImage.location['x'])
    top = (int)(bigImage.location['y'])
    elementWidth = (int)(bigImage.location['x'] + bigImage.size['width'])
    elementHeight = (int)(bigImage.location['y'] + bigImage.size['height'])
    picture = Image.open('imgs/screenshot.png')
    picture = picture.crop((left, top, elementWidth, elementHeight))
    picture.save('imgs/big_full.png')
    smallImage = driver.find_element_by_id('smallImage')
    left = (int)(smallImage.location['x'])
    top = (int)(smallImage.location['y'])
    elementWidth = (int)(smallImage.location['x'] + smallImage.size['width'])
    elementHeight = (int)(smallImage.location['y'] + smallImage.size['height'])
    picture = Image.open('imgs/screenshot.png')
    picture = picture.crop((left, top, elementWidth, elementHeight))
    picture.save('imgs/small.png')
    jigimgS = driver.find_element_by_class_name('jigimgS')
    upper = (int)(jigimgS.value_of_css_property("top").split('px')[0])
    letf = 58
    right = 279
    lower = upper + 57
    picture = Image.open('imgs/big_full.png')
    picture = picture.crop((letf, upper, right, lower))
    picture.save('imgs/big.png')
    time.sleep(0.5)

第二种方式是使用代理截获相应下载图片
笔者使用的代理框架是Titanium(C#)框架。

proxyServer.BeforeResponse += OnResponse;
. . .
private async Task OnResponse(object sender, SessionEventArgs e)
{
    // read response headers
    var responseHeaders = e.HttpClient.Response.Headers;

    //if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
    if (e.HttpClient.Request.Method == "GET" || e.HttpClient.Request.Method == "POST")
    {

        if (e.HttpClient.Response.StatusCode == 200)
        {
            string stringResponse = await e.GetResponseBodyAsString();
            Console.WriteLine(e.HttpClient.Request.RequestUri.AbsoluteUri);
            if ("http://***/jigsaw".Equals(e.HttpClient.Request.Url))
            {
                //exit的时候会走第二次。 在exit之前调用StopProxyServer,防止出现第二次走这个方法的情况。
                if (!exit)
                {
                        //截获响应下载图片,此时图片尚未删除
                    SaveImage(stringResponse);
                }
        }
    }

    if (e.UserData != null)
    {
        // access request from UserData property where we stored it in RequestHandler
        var request = (Request)e.UserData;
    }
}

private void SaveImage(String stringResponse)
{
    var jo = JsonConvert.DeserializeObject(stringResponse) as JObject;
    template = jo?["smallImage"]?.ToString();
    target = jo?["bigImage"]?.ToString();
    template = "http://***/upload/jigsawImg/" + template + ".png";
    target = "http://***/upload/jigsawImg/" + target + ".png";

    WebClient client = new();
    //不加锁的话只能下载第一个图片,然后就去匹配去了,由于第二个图片还没有下载下来,导致匹配的时候报错。
    Monitor.Enter(this);
    client.DownloadFile(target, AppDomain.CurrentDomain.BaseDirectory + "\\target.png");
    client.DownloadFile(template, AppDomain.CurrentDomain.BaseDirectory + "\\template.png");
    Console.WriteLine("Finish download image ");
    Monitor.Exit(this);
}

图片不能下载却可以显示出来使用到技术是img的onload属性,onload 事件在图片加载完成后立即执行。下面的代码就是当图片加载完成后立即删除

<div class="jigimgB">
    <img src="" id="bigImage" onload="clearB()">                          
</div>
<div class="jigimgS">
    <img src="" id="smallImage" onload="clearS()">                   
</div>
图片匹配

文中代码具体可参见自动登陆QQ空间(3)和代码中相应的注释。
python版本:

def main():
    otemp = 'template.png'
    oblk = 'target.png'
    identify_gap(oblk, otemp, "D:\\books\\plantuml_picture\\target.png");

def identify_gap(bg, tp, out):
    '''
    bg: 背景图片
    tp: 缺口图片
    out:输出图片
    '''
    # 读取背景图片和缺口图片
    bg_img = cv2.imread(bg)  # 背景图片
    tp_img = cv2.imread(tp)  # 缺口图片

    # 转换图片格式
    bg_pic = cv2.cvtColor(bg_img , cv2.COLOR_GRAY2RGB)
    tp_pic = cv2.cvtColor(tp_img, cv2.COLOR_GRAY2RGB)

    # 缺口匹配
    res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  # 寻找最优匹配

    # 绘制方框
    th, tw = tp_pic.shape[:2]
    tl = max_loc  # 左上角点的坐标
    br = (tl[0] + tw, tl[1] + th)  # 右下角点的坐标
    cv2.rectangle(bg_img, tl, br, (0, 0, 255), 2)  # 绘制矩形
    cv2.imwrite(out, bg_img)  # 保存在本地

    # 返回缺口的X坐标
    return tl[0]

写在最后

大部分验证码和滑块的问题可以通过文章中的方式搞定,如果是短信验证码,可能需要接码平台来搞定了。最后上一段代码,模拟人类滑动滑块行为的:

 public static void MoveSlideByOffSet(Actions action, int distance)
 {
     Thread.Sleep(500);
     int has_gone_dist = 0;
     int remaining_dist = distance;
     Random random = new();
     int span;
     while (remaining_dist > 0)
     {
         var ratio = remaining_dist / distance;
         if (ratio < 0.1)
             span = random.Next(3, 5);
         else if (ratio > 0.9)
             span = random.Next(5, 8);
         else
             span = random.Next(15, 20);
         action.MoveByOffset(span, random.Next(-5, 5));
         remaining_dist -= span;
         has_gone_dist += span;
         Thread.Sleep(random.Next(5, 20) / 100);
     }

     action.MoveByOffset(remaining_dist, random.Next(-5, 5));
 }

公众号

更多内容,欢迎关注我的微信公众号:半夏之夜的无情剑客。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容