用 2칼로리 센서스通過 인증 코드人機驗證

往往我們在做爬蟲或 로봇 프로그램的時候,最難跨過的問題之一大概就是那 CAPTCHA 人機驗證了,有的團隊會選擇自行開發辨識技術來通過 인증 코드人機驗證,然而面對越來越難的人機考驗,例如 구글的 레파차若真要自行開發,那得付出相當程度的時間與精神,本文要介紹的 2Captcha 是幫助我們通過 인증 코드人機驗證的服務,透過 2 카드 센서스 API讓我們可以方便快速的通過那些 인증 코드考驗,省去大量自行開發的時間與金錢.
在介紹 2칼로리 센서스前,先簡單的認識一下 인증 코드

인증 코드
인증 코드全稱是"컴퓨터와 인간을 구분하는 완전 자동화된 공공 그래픽 테스트"翻譯成華文是超級繞口的「全自動區分電腦和人類的公開圖靈測試」,這串無敵長的文字簡單講就是人機驗證,인증 코드人機驗證存在的目的通常是為了遏止網站被外部的程式惡意的大量操作,然而這樣的機制也擋住了我們家從事無害工作的可愛小爬蟲 🐛 或小機器人 🤖,所以我輩開發者就需要像 2칼로리 센서스這樣的服務幫我們通過那 인증 코드考驗.

常見的 인증 코드
在台灣常見到的 인증 코드的場合大概有這些:
台灣高鐵
台灣高鐵的 인증 코드是較為傳統的文字混淆型:

臺灣鐵路
臺鐵的則是用 구글的 다시 설명:

網路銀行
除了訂票外,網銀也很常看到 인증번호:

特別要說明的是,上面的舉例僅是用於說明 인증 코드的使用場景,請不要動搶票的歪腦筋,那有可能是違法的,請諮詢專業法律人士獲得正確的法律知識,也歡迎留言共同討論.

2칼로리 센서스

來源: 2Captcha
先簡單認識 2Captcha ,做為一個辨識 인증 코드的服務,2칼로리 센서스背後的辨識機制其實是由人工完成的,我們傳送給 2칼로리 센서스的圖片,都會轉送給分散在全世界的 2칼로리 일꾼們,經過人工辨識後再把結果回傳,而那些辛苦的 노동자.們也可以透過 2칼로리 센서스的仲介機制獲得報酬,因為是人工辨識,所以我們可以預期:
  • 正確率應該是高的,2칼로리 센서스也有自己的品質把關機制,剃除掉不適任的 노동자.一般來說正確率在九成以上.
  • 應該會有些許的等待時間,才能得到辨識後的結果,在 2Captcha API文件有提到,一般形式的 카타하建議等待 5秒,而較難的 레파차則建議等待 20秒.

  • 2칼로리 센서스的收費
    在費用方面,2칼로리 센서스是採用動態計價,動態的基準是當前 2칼로리 센서스與 노동자.們的工作負荷,當負荷量大時會升價、負荷量小時會降價,當前的費率會顯示在 Statistics 頁面,例如下面是本文撰寫時的費率:

    上圖可以看到,普通的 인증 코드每一千個收費僅 0.74美金,而較難的 레파차每一千個則收費 2.99美金,相較於自行開發各種花式 인공지능辨識系統所要花費的人力物力,2칼로리 센서스算是相當低廉的價格了.
    在 통계적頁面,除了費率外,還有目前 2칼로리 센서스服務的負載程度,以及解題的速度,上圖顯示一般的 인증 코드花 15秒,而 레파차要花 46秒,儘管與 2칼로리 센서스文件內的 5秒、20秒有差距,看起來還在可以接受的程度內.
    在簡單的認識 2칼로리 센서스的機制與收費之後,下面就來實際玩玩看 2칼로리 센서스吧!

    2칼로리 센서스辨識一般 인증 코드
    在這個範例中,我們會示範用 극작가登入一個網站,並且用 2Captcha 協助我們辨識登入時的 인증 코드驗證碼.

    事前準備
    在開始前簡單的介紹一下 극작가是微軟開發的瀏覽器自動化套件,類似於陳年的 셀렌的優點有:
  • 自帶瀏覽器,包括 Firefox, WebKit, Chromium, 극작가一行指令就都幫我們配置到好.
  • 跨語言,극작가有 노드js、Python、.NET、Java版本,並且不同的語言有著共通的邏輯與相似的 API
  • 自動等待元素現身,才執行相關敘述,相當大程度的省去了手動調試等待時間的心力.
  • 做為一個瀏覽器自動化工具,當然是最適合拿來開發爬蟲或 로봇 프로그램了,特別是 클라이언트 렌더링當道的現在,很難在不使用真正的瀏覽器的條件下做出爬蟲或로봇 프로그램.
    在 2칼로리 센서스方面,開立帳號後,登入可以看到 2Captcha API 키:

    這組 API 키用於與 2칼로리 센서스程式交互,下面的範例程式中的 API 키是無效的,請務必換成您個人的 API 키

    登入與 인증 코드辨識
    回到範例的主題上,下面我們會用 파이썬 극작가以及 2Captcha 的 Python 套件 ,示範通過下面這個登入頁面:

    在這個典型的登入表單中,我們的腳本會用 극작가輸入帳密,並且把那 인증 코드圖片透過 2Captcha API取得驗證碼,最後點按「登入」:
    import base64
    
    from playwright.sync_api import sync_playwright
    from twocaptcha import TwoCaptcha
    
    
    CAPTCHA_API_KEY = '5c0f7e0306aa2e0398510ef9ce6dbca'
    solver = TwoCaptcha(apiKey=CAPTCHA_API_KEY)
    
    
    def solve(image):
      try:
        result = solver.normal(
          file=image,
          numeric=4,
          minLength=4,
          maxLength=4,
          caseSensitive=1
        )
      except Exception as e:
        print(e)
    
      print(result)
      return result
    
    
    def report(captcha_id: str, success: bool):
      solver.report(captcha_id, success)
    
    
    def main():
      while True:
        with sync_playwright() as p:
          browser = p.firefox.launch(headless=False, slow_mo=500)
          context = browser.new_context()
          page = context.new_page()
          page.goto("http://www.twoas.idv.tw/user.php")
          page.type("input[name=username]", "RSOB")
          page.type("input[name=password]", "RSOBPassword")  
          screenshot_bytes = page.locator("img[alt=captcha]").screenshot()
          captcha_image = base64.b64encode(s=screenshot_bytes).decode('utf-8')
          result = solve(image=captcha_image)
          page.type("input[name=captcha]", result['code'])
          page.click("text=立即登入")
          login_message = page.text_content("div.tips")
    
          try:
            assert login_message == "登入成功"
          except AssertionError as e:
            report(result['captchaId'], False)
            continue
    
          report(result['captchaId'], True)
          page.pause()
          context.close()
          browser.close()
          break
    
    if __name__ == '__main__':
      main()
    
    上面的腳本,可以分成 극작가操控瀏覽器的部份與 2칼로리 센서스交互的部份.
    극작가的部份,我們用 page 物件操控大多數的瀏覽器行為,相信即使是對 극작가不熟悉的朋友也可以望文生義:
  • goto() 函式前往指定網址.
  • type() 函式找到特定元素,並輸入字元.
  • locator() 函式找到元素並操控它.
  • screenshot() 函式對元素截圖.
  • click() 函式點按特定元素.
  • 在用 screenshot() 取得 인증 코드圖片後,用 base64 模組轉換成 Base64編碼,交給 solve() 函式處理.
    在 인증 코드辨識部份,此網站的 인증 코드如下例:

    這是一種相對普通的 인증 코드在 2칼로리 센서스的分類上,屬於 정상이었어型.
    在 2칼로리 센서스的部份,先定義一個 2칼로리 센서스的 solver 物件,並用下面的敘述得到辨識後的文字:
    result = solver.normal(
      file=image,
      numeric=4,
      minLength=4,
      maxLength=4,
      caseSensitive=1
    )
    
    因為 인증 코드屬於 정상이었어型,所以我們調用的是 normal() 函式,函式內的參數,除了 file 是必填,其他都是可選用的:
  • file 可接受一個經 Base64編碼的 인증 코드圖片物件,也可以是本地的圖片檔案,或圖片的 웹 주소
  • numeric 인증번호的字元型態, 4 表示會有字母與數字,其他的定義請參閱 2Captcha API 文件 .
  • minLength 最小字元長度,以此例而言,都是固定四個字元.
  • maxLength 最大字元長度,以此例而言,都是固定四個字元.
  • caseSensitive 設為 1 表示區分大小寫.
  • 經過上面的交互,取得 result , result 是一個結構如下的 딕트物件:
    {'captchaId': '69475003267', 'code': '4QR5'}
    
    其中的 cpatchaId 是 2Captcha API賦予的交互 신분증,而 code 當然就是辨識後的答案啦!

    回饋辨識與否
    取得 result['code'] 後,填入表單,若正確登入,我們用 report() 回饋成功紀錄給 2칼로리 센서스並在最後的 break 語句完全結束 while 迴圈.
    反之若是失敗,則用 report() 回饋失敗紀錄給 2칼로리 센서스並在後續的 continue 語句中斷這次的 while 迴圈,進入下一個 while 迴圈,藉此做出重試的效果.

    成果
    最後的成果可以參考這個影片:

    用 2칼로리 센서스通過 레파차
    完成前一個較簡單的例子後,我們來玩玩看如何用 2Captcha 通過難一點的 레파차也就是那著名的靈魂考驗「我不是機器人」:

    來源:網路
    要用 2칼로리 센서스通過 레파차的「我不是機器人」考驗,必須先取得目標頁面的 사이트 키在原始碼裡面就可以找到這把公開的 사이트 키:

    有了 사이트 키之後,就可以依樣畫葫蘆,呼叫 2Captcha API得到 레파차的通關密語.
    與 2칼로리 센서스交互的部份改成這樣:
    result = solver.recaptcha(
      sitekey='6LflqMEUAAAAAANhC6kkXmSLiBOLiLFsHw_anYWZ',
      url="https://naweeklytimes.com/login-2/",
      version="v2",
    )
    
    拿到的 결실也是同樣的 딕트結構,只是變成超長的 토큰:
    {'captchaId': '69482081140', 'code': '03AGdBq252s4QsHsrr9CqzkkswdnHsUPLXIcE4OI1pEX4FUP9rNY8p760yLBsd9goaMn61vw95x981lU0...'}
    
    然後我們要把那組 code 填入一個名為 g-recaptcha-response 的文字框,然而這個文字框是隱藏的,所以得先用 극작가跑一小段 JS讓它顯示出來:
    page.eval_on_selector(
      selector="textarea[name=g-recaptcha-response]",
      expression="(el) => el.style.display = 'inline-block'",
    )
    
    上面的函式中,JS的 el 就是來自 selector 抓到的元素.
    文字框顯示出來會像這樣:

    然後就比照第一個範例,帳號、密碼、레파차通關密語填一填,就可以成功登入囉!

    成果
    完整的程式碼如下:
    from playwright.sync_api import sync_playwright
    from twocaptcha import TwoCaptcha
    
    
    URL = "https://naweeklytimes.com/login-2/"
    SITEKEY = '6LflqMEUAAAAAANhC6kkXmSLiBOLiLFsHw_anYWZ'
    CAPTCHA_API_KEY = '5c0f7e0306aa2e0398510ef9ce6dbca'
    
    
    solver = TwoCaptcha(apiKey=CAPTCHA_API_KEY)
    
    
    def solve(url: str):
      try:
        result = solver.recaptcha(
          sitekey=SITEKEY,
          url=url,
          version="v2",
        )
      except Exception as e:
        print(e)
        raise Exception
    
      print(result)
      return result
    
    
    def report(captcha_id: str, success: bool):
      solver.report(captcha_id, success)
    
    
    def main():
      while True:
        with sync_playwright() as p:
          browser = p.firefox.launch(headless=False, slow_mo=500)
          context = browser.new_context()
          page = context.new_page()
          page.goto(URL)
          page.type("input[name=log]", "RSOB")
          page.type("input[name=pwd]", "RSOBPassword")
    
          try:
            result = solve(url=URL)
          except Exception as e:
            continue
    
          page.eval_on_selector(
            selector="textarea[name=g-recaptcha-response]",
            expression="(el) => el.style.display = 'inline-block'",
          )
          textarea = page.locator("textarea[name=g-recaptcha-response]")
          textarea.fill(result['code'])
          page.click("input[type=submit]")
    
          try:
            assert page.url == "https://naweeklytimes.com/"
          except AssertionError as e:
            report(result['captchaId'], False)
            continue
    
          report(result['captchaId'], True)
          page.pause()
          context.close()
          browser.close()
          break
    
    
    if __name__ == '__main__':
      main()
    
    看起來有點長,不過和黑科技 인공지능相比應該是小巫見大巫.
    下面是執行的影片:

    結語
    本文我們示範用 2Captcha 通過普通的 인증 코드及 레파차在這兩種最常見的 인증 코드外,2칼로리 센서스還支援其他各式各樣的 인증 코드形式眾多,可以參考 2Captcha 的文件demo 頁.
    在 미국 석유 학회方面,2칼로리 센서스有提供封裝好的 Python、GO、PHP、Java、C#、C++套件,即使是沒有現成套件的語言,直接呼叫 2Captcha API也不是太困難的事.
    至於 2칼로리 센서스的費用,除了真的很便宜的費率外,在設計 로봇 프로그램時,只要保留住登入後的 과자 과자或 로컬 스토리지即可省下每次都要重新登入的時間以及 인증 코드的費用,以 극작가為例,它就有 reuse authentication state 的機制,可以多加利用.

    參考資料
  • 維基百科〈 驗證碼
  • 좋은 웹페이지 즐겨찾기