├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── README.md ├── captchasv2.js └── captchas.js /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Please provide the shortest possible puppeteer/playwright/python+selenium script that reproduces the issue. Leave out any api keys. 15 | 16 | **Screenshots** 17 | If applicable, add screenshots to help explain your problem. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # captchas 2 | Solve hCaptcha / reCaptcha v2 for Puppeteer / Selenium (2captcha.com account required) 3 | 4 | ### Selenium 5 | ```python 6 | from selenium import webdriver 7 | from selenium.webdriver.chrome.options import Options 8 | import time 9 | 10 | driver = webdriver.Chrome() 11 | driver.get('https://www.site-with-captcha.com/') 12 | 13 | from requests import get 14 | body = get("https://raw.githubusercontent.com/pguardiario/captchas/master/captchas.js").content.decode('utf8') 15 | driver.set_script_timeout(120) 16 | api_key = 'Your 2captcha API key' 17 | status = driver.execute_async_script(body, api_key, {}) 18 | print(status) 19 | time.sleep(1) 20 | ``` 21 | ### Puppeteer 22 | ```javascript 23 | const puppeteer = require('puppeteer') 24 | 25 | ; (async () => { 26 | const browser = await puppeteer.launch() 27 | const page = await browser.newPage() 28 | await page.goto('https://www.site-with-captcha.com/') 29 | await page.addScriptTag({url: "https://cdn.jsdelivr.net/gh/pguardiario/captchas/captchas.js"}) 30 | let status = await page.evaluate(async () => { 31 | return new Promise((resolve, reject) => { 32 | solve([your_api_key, {}, resolve]) 33 | }) 34 | }) 35 | 36 | switch(status){ 37 | case 'NO_CAPTCHAS': 38 | case 'OK': 39 | break 40 | case 'ERROR_ZERO_BALANCE': 41 | console.log('no captcha balance!!!!!') 42 | case 'ERROR_CAPTCHA_UNSOLVABLE': 43 | console.log('retrying') 44 | // try again ? 45 | default: 46 | debugger 47 | } 48 | 49 | // evaluate returns before the submit / captcha callback so wait 1 second. 50 | await page.waitFor(1000) 51 | })() 52 | ``` 53 | -------------------------------------------------------------------------------- /captchasv2.js: -------------------------------------------------------------------------------- 1 | function getRecaptchaWidgets() { 2 | let widgets = Object.values(window.___grecaptcha_cfg?.clients || {}).map(widget => { 3 | let info = { 4 | captchaType: "recaptcha", 5 | widgetId: widget.id, 6 | version: "v2", 7 | sitekey: null, 8 | callback: null, 9 | url: location.href 10 | }; 11 | 12 | if (Object.values(widget).map(o2 => Object.values(o2 || {})).flat().find(el => el?.classList?.contains("grecaptcha-badge"))) { 13 | info.version = "v3"; 14 | } 15 | 16 | for (let k in widget) { 17 | if (widget[k] && widget[k].nodeType) { 18 | info.textarea = info.textarea || widget[k].querySelector('textarea') 19 | } 20 | } 21 | 22 | let props = Object.values(widget).reduce((acc, o) => { 23 | return acc || Object.values(o).find(o2 => o2.sitekey) 24 | }, null) 25 | 26 | let { sitekey, action, s, callback, bind } = props 27 | info = { ...info, sitekey, action, s, callback, bind } 28 | 29 | return info 30 | }) 31 | return widgets 32 | } 33 | 34 | async function waitForCaptcha(params) { 35 | let url = 'https://2captcha.com/in.php' 36 | params['soft_id'] = 2974 37 | params['json'] = 1 38 | params['header_acao'] = 1 39 | let qs = Object.keys(params).map(k => `${k}=${params[k]}`).join('&') 40 | 41 | let response = await fetch(`${url}?${qs}`).then(r => r.json()).catch(e => { 42 | alert(e.message) 43 | }) 44 | 45 | let {status, request} = response 46 | if(status !== 1){ 47 | alert("bad 2captcha status") 48 | } 49 | 50 | url = 'https://2captcha.com/res.php' 51 | 52 | params = { 53 | key: params.key, 54 | json: 1, 55 | header_acao: 1 56 | } 57 | params['action'] = 'get' 58 | params['id'] = request 59 | qs = Object.keys(params).map(k => `${k}=${params[k]}`).join('&') 60 | 61 | while(true){ 62 | await new Promise(resolve => setTimeout(resolve, 10000)) 63 | response = await fetch(`${url}?${qs}`).then(r => r.json()).catch(e => { 64 | alert(e.message) 65 | }) 66 | 67 | let {status, request} = response 68 | if(status === 0){ 69 | // waiting 70 | } else if(status === 1){ 71 | return request 72 | } else { 73 | alert("bad 2captcha status") 74 | break 75 | } 76 | } 77 | } 78 | 79 | window.solve = async (api_key, resolve) => { 80 | let widgets = getRecaptchaWidgets() || [] 81 | let [widget] = widgets 82 | if (widget) { 83 | let captcha = await waitForCaptcha({ 84 | key: api_key, 85 | method: 'userrecaptcha', 86 | googlekey: widget.sitekey, 87 | pageurl: window.location, 88 | version: widget.version 89 | }) 90 | 91 | widget.textarea.innerText = captcha 92 | if(widget.callback){ 93 | widget.callback() 94 | } 95 | 96 | setTimeout(resolve, 1000) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /captchas.js: -------------------------------------------------------------------------------- 1 | const solve = async (arguments) => { 2 | const delay = async (ms) => { 3 | await new Promise((resolve, reject) => { 4 | setTimeout(() => resolve(), ms) 5 | }) 6 | } 7 | 8 | const get = async (url) => { 9 | let response = new Promise((resolve, reject) => { 10 | fetch(url).then(res => { 11 | resolve(res.json()) 12 | }).catch(() => callback('BAD_FETCH')) 13 | }) 14 | console.log(response) 15 | return response 16 | } 17 | 18 | const waitForCaptcha = async (sitekey, method) => { 19 | let key = method === 'hcaptcha' ? 'sitekey' : 'googlekey' 20 | let api_url = `https://2captcha.com/in.php?header_acao=1&key=${api_key}&method=${method}&${key}=${sitekey}&json=1&pageurl=${document.location.href}&soft_id=2974` 21 | let div = document.querySelector('[data-s]') 22 | if(div){ 23 | api_url += `&data-s=${div.getAttribute('data-s')}` 24 | } 25 | 26 | let {status, request} = await get(api_url) 27 | if(status) { 28 | let result_url = `https://2captcha.com/res.php?header_acao=1&key=${api_key}&action=get&id=${request}&json=1&soft_id=2974`; 29 | await delay(20000) 30 | 31 | while(true){ 32 | response = await get(result_url) 33 | 34 | console.log(response) 35 | 36 | if(response.request === 'CAPCHA_NOT_READY'){ 37 | 38 | } else if (response.status === 1) { 39 | break 40 | } else { 41 | return callback(response) 42 | } 43 | await delay(10000) 44 | } 45 | 46 | [...document.querySelectorAll('[name="h-captcha-response"],[name="g-recaptcha-response"]')].map(el => { 47 | el.innerHTML = response.request; 48 | }) 49 | 50 | setTimeout(() => { 51 | let div = document.querySelector('[data-callback]') 52 | if(div){ 53 | console.log('callback found') 54 | window[div.getAttribute('data-callback')](response.request) 55 | } 56 | 57 | let form 58 | if(method === 'hcaptcha'){ 59 | let el = document.querySelector('.h-captcha') 60 | form = el ? el.closest('form') : document.querySelector('#challenge-form') 61 | } else { 62 | form = [...document.querySelectorAll('form')].find(f => f.querySelector('.g-recaptcha')) 63 | } 64 | 65 | 66 | 67 | if(form && options.submit !== false){ 68 | console.log('form found') 69 | form.submit() 70 | } 71 | },100) 72 | 73 | return callback('OK') 74 | 75 | } else { 76 | return callback(request) 77 | } 78 | } 79 | 80 | const solveHcaptcha = async () => { 81 | console.log('hcaptcha') 82 | 83 | let sitekey = document.querySelector('[data-sitekey]') ? 84 | document.querySelector('[data-sitekey]').getAttribute('data-sitekey') : 85 | document.querySelector('iframe[src*=sitekey]').src.match(/[?&]sitekey=([\w-]+)/)[1] 86 | 87 | console.log(sitekey) 88 | 89 | await waitForCaptcha(sitekey, 'hcaptcha') 90 | } 91 | 92 | const solveRecaptcha = async () => { 93 | console.log('recaptcha') 94 | let sitekey = document.querySelector('[data-sitekey]') ? 95 | document.querySelector('[data-sitekey]').getAttribute('data-sitekey') : 96 | document.querySelector('iframe[src*=api2]').src.match(/[?&]k=(\w+)/)[1] 97 | 98 | for(var iframe of document.querySelectorAll('iframe[src*=api2]')){ 99 | iframe.style.border = 'solid 3px green' 100 | } 101 | 102 | console.log(sitekey) 103 | 104 | await waitForCaptcha(sitekey, 'userrecaptcha') 105 | } 106 | 107 | const solveCaptchas = async () => { 108 | // look for hcaptcha / recaptcha\ 109 | // setTimeout(() => callback('Failed'), 60000) 110 | let el = document.querySelector('.h-captcha') 111 | let form = el ? el.closest('form') : document.querySelector('#challenge-form') 112 | if(form){ 113 | return await solveHcaptcha() 114 | } 115 | let div = document.querySelector('.g-recaptcha,#g-recaptcha-response') 116 | if(div){ 117 | return await solveRecaptcha() 118 | } 119 | callback('NO_CAPTCHAS') 120 | 121 | } 122 | 123 | const [api_key, options, callback] = arguments 124 | 125 | await solveCaptchas() 126 | } 127 | 128 | if(typeof arguments !== 'undefined'){ 129 | solve(arguments) 130 | } 131 | --------------------------------------------------------------------------------