搞定驗證碼和滑塊

Frida可視化工具Dwarf2已經(jīng)開源,大家有什么問題,可以一起交流。

本文內(nèi)容僅用于學習,嚴禁用作非法目的。

驗證碼

作用

驗證碼作為一種人機識別手段,其終極目的,就是區(qū)分正常人和機器的操作。 區(qū)分人機行為的作用不言而喻。互聯(lián)行為的注冊、登錄、發(fā)帖、領優(yōu)惠券、投票等等應用場景,都有被機器刷造成各類損失的風險,如果不對各類機器垃圾的行為加以防范,灌水內(nèi)容、垃圾注冊、惡意登錄、刷票、撞庫、活動作弊、垃圾廣告、爬蟲、羊毛黨等用戶行為一旦發(fā)生,將對產(chǎn)品自身發(fā)展、用戶體驗造成極大的影響。

搞定驗證碼

很多網(wǎng)站都使用了驗證碼進行人機識別,給爬蟲帶來了一定的困擾。常見的驗證碼如下:
[圖片上傳失敗...(image-851c8f-1648449832834)]
[圖片上傳失敗...(image-c2de3e-1648449832834)]

trwebocr

一個開源的ocr工具,非常強大。官方介紹是: 開源易用的中文離線OCR,識別率媲美大廠,并且提供了易用的web頁面及web的接口,方便人類日常工作使用或者其他程序來調(diào)用~ 。筆者在github上有關于它的使用。

通過一個例子來感受一下這個工具。以下是我2022虎年快樂這個公眾號里面部分內(nèi)容的截圖:
[圖片上傳失敗...(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功能。當然也可以自己實現(xiàn)類似的功能,使用opencv+CNN效果也不錯。

滑塊

滑塊驗證碼是在網(wǎng)站、APP等應用中常見的一種驗證方式,通過按照一定規(guī)則滑動滑塊到指定位置完成驗證,才可以進行下一步操作?;瑝K驗證碼有兩種設計,一種是在滑動框內(nèi)“一滑到底”即完成驗證的,還有一種是滑動滑塊拼合拼圖完成驗證的,如下圖所示。由于拼圖式的滑塊驗證碼安全性更高,趣味性更強,“一滑到底”式的滑塊驗證碼已經(jīng)基本被淘汰。

搞定滑塊

使用滑塊機制的網(wǎng)站也有好多,增大了爬蟲的難度,常見的滑塊驗證:
[圖片上傳失敗...(image-3da661-1648449832834)]
[圖片上傳失敗...(image-f24d92-1648449832834)]

獲取圖片

滑塊驗證第一步需要獲取大圖片,后面統(tǒng)稱為target,以及小圖片,后面稱為template。具體可參見自動登陸QQ空間(3)。
(1) 如果target和template都可以正常下載的話,直接進行下載。
(2 ) 如果不能下載的話,可以使用兩種方式進行獲?。?/p>

第一種方式是使用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之前調(diào)用StopProxyServer,防止出現(xiàn)第二次走這個方法的情況。
                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 事件在圖片加載完成后立即執(zhí)行。下面的代碼就是當圖片加載完成后立即刪除

<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)  # 尋找最優(yōu)匹配

    # 繪制方框
    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));
 }

公眾號

更多內(nèi)容,歡迎關注我的微信公眾號:半夏之夜的無情劍客。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容