From 8441e06b003464bfcb3286848da0d238ad7a234c Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 21 Nov 2021 17:08:29 -0500 Subject: [PATCH] 1.2.1 Python: [+] Added soft_id support for 2captcha [+] Added text-image captcha support [+] Updated documentation [-] Removed redundant unnecessary exception handling code --- README.md | 16 +++++++++++--- captchatools-go/anticaptcha.go | 2 -- captchatools/anticaptcha.py | 26 ++++++++++++++--------- captchatools/capmonster.py | 37 +++++++++++++++++++-------------- captchatools/exceptions.py | 18 ++++++++++++++-- captchatools/harvesters.py | 38 +++++++++++++++++++++++++++++++--- captchatools/twocap.py | 24 +++++++++++---------- 7 files changed, 114 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 633483e..45dd102 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ pip3 install captchatools ```python pip3 install -U captchatools ``` - # How to use +### Getting reCAPTCHA Tokens ```python import captchatools solver = captchatools.captcha_harvesters(solving_site="capmonster", api_key="YOUR API KEY", sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", captcha_url="https://www.google.com/recaptcha/api2/demo") @@ -26,6 +26,14 @@ solver = captcha_harvesters(solving_site=1, api_key="YOUR API KEY", sitekey="6Le captcha_answer = solver.get_token() ``` +### Getting Normal Captcha Tokens +```python +import captchatools +solver = captchatools.captcha_harvesters(solving_site=2, captcha_type="normal", api_key="YOUR API KEY HERE") +url = "https://www.scienceabc.com/wp-content/uploads/ext-www.scienceabc.com/wp-content/uploads/2016/07/Captcha-ex.jpg-.jpg" +text_cap_answer = solver.get_normal(url) +``` + | Parameter | Required | Type | Default | Description| | :-------------: |:-------------:| :-----:| :-----:| :-----:| | api_key | true | String| -| The API Key for the captcha solving site| @@ -46,8 +54,8 @@ captcha_answer = solver.get_token() ##### Site-Specific Support: | Site |Site ID| Captcha Types Supported | Task Types Supported| | :-------------: |:-------------:|:-------------:| :-----:| -| Capmonster |1| Recaptcha V2,
Recaptcha V3,
HCaptcha | RecaptchaV2TaskProxyless,
RecaptchaV3TaskProxyless,
HCaptchaTaskProxyless | -| Anticaptcha |2| Recaptcha V2,
Recaptcha V3,
HCaptcha | RecaptchaV2TaskProxyless,
RecaptchaV3TaskProxyless,
HCaptchaTaskProxyless | +| Capmonster |1| Recaptcha V2,
Recaptcha V3,
HCaptcha | RecaptchaV2TaskProxyless,
RecaptchaV3TaskProxyless,
HCaptchaTaskProxyless,
ImageToTextTask
| +| Anticaptcha |2| Recaptcha V2,
Recaptcha V3,
HCaptcha | RecaptchaV2TaskProxyless,
RecaptchaV3TaskProxyless,
HCaptchaTaskProxyless
ImageToTextTask
| | 2Captcha |3| Recaptcha V2,
Recaptcha V3,
HCaptcha | - | @@ -63,6 +71,8 @@ captcha_answer = solver.get_token() | `WrongAPIKeyExceptionException` | Incorrect API Key for captcha solving site| | `WrongSitekeyException` | Incorrect sitekey | | `NoHarvesterException` | When the user did not / incorrectly chose a captcha harvester. Refer to the [guide](https://github.com/Matthew17-21/Captcha-Tools#how-to-use) | +| `CaptchaIMGTooBig` | The size of the captcha image is too big for the solving service. | +| `FailedToGetCapIMG`| Failed to get the captcha image from the URL.
**Tries 3 times before getting thrown.**
```python from captchatools import captcha_harvesters, exceptions as captchaExceptions diff --git a/captchatools-go/anticaptcha.go b/captchatools-go/anticaptcha.go index d77cae5..3321033 100644 --- a/captchatools-go/anticaptcha.go +++ b/captchatools-go/anticaptcha.go @@ -3,7 +3,6 @@ package captchatoolsgo import ( "bytes" "encoding/json" - "fmt" "io/ioutil" "net/http" "time" @@ -72,7 +71,6 @@ func (t *Anticaptcha) getCaptchaAnswer() (string, error) { // Parse Response body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() - fmt.Println(string(body)) json.Unmarshal(body, response) if response.Status == "ready" { return response.Solution.GRecaptchaResponse, nil diff --git a/captchatools/anticaptcha.py b/captchatools/anticaptcha.py index 430209d..bfd4b60 100644 --- a/captchatools/anticaptcha.py +++ b/captchatools/anticaptcha.py @@ -16,8 +16,11 @@ def __init__(self, parent:object): def get_token(self) -> str: return self.get_answer( self.get_id() ) + + def get_normal(self, cap_pic_url) -> str: + return self.get_answer(self.get_id(cap_pic_url), True) - def get_id(self) -> int: + def get_id(self, cap_pic_url=None) -> int: ''' Method to get Queue ID from the API. ''' @@ -44,6 +47,10 @@ def get_id(self) -> int: elif self.user_data.captcha_type == "hcap" or self.user_data.captcha_type == "hcaptcha": payload["task"]["type"] = "HCaptchaTaskProxyless" + + elif self.user_data.captcha_type == "normal": + payload["task"]["type"] = "ImageToTextTask" + payload["task"]["body"] = self.user_data.get_cap_img(cap_pic_url) # Get the Queue ID b sending a POST request to their API while True: @@ -61,16 +68,13 @@ def get_id(self) -> int: elif resp["errorCode"] == "ERROR_KEY_DOES_NOT_EXIST": # Throw Exception raise captchaExceptions.WrongAPIKeyException() - except captchaExceptions.NoBalanceException: - raise captchaExceptions.NoBalanceException() - except captchaExceptions.WrongSitekeyException: - raise captchaExceptions.WrongSitekeyException() - except captchaExceptions.WrongAPIKeyException: - raise captchaExceptions.WrongAPIKeyException() - except Exception: + + elif resp["errorCode"] == "ERROR_TOO_BIG_CAPTCHA_FILESIZE": + raise captchaExceptions.CaptchaIMGTooBig() + except requests.RequestException: pass - def get_answer(self, queue_id) -> str: + def get_answer(self, queue_id, isTextCap=False) -> str: ''' This method gets the captcha token from the API ''' @@ -84,8 +88,10 @@ def get_answer(self, queue_id) -> str: } answer = requests.post(BASEURL + "/getTaskResult", json=payload_getAnswer).json() - if answer["status"] == "ready": + if answer["status"] == "ready" and not isTextCap: return answer["solution"]["gRecaptchaResponse"] + elif answer["status"] == "ready" and isTextCap: + return answer["solution"]["text"] elif answer["errorId"] == 12 or answer["errorId"] == 16: # Captcha unsolvable || TaskID doesn't exist diff --git a/captchatools/capmonster.py b/captchatools/capmonster.py index 2de8487..8ef4058 100644 --- a/captchatools/capmonster.py +++ b/captchatools/capmonster.py @@ -16,8 +16,11 @@ def __init__(self, parent:object): def get_token(self) -> str: return self.get_answer( self.get_id() ) + + def get_normal(self, path_to_img) -> str: + return self.get_answer(self.get_id(path_to_img), True) - def get_id(self) -> int: + def get_id(self, cap_pic_url=None) -> int: ''' Method to get Queue ID from the API. ''' @@ -43,6 +46,11 @@ def get_id(self) -> int: payload["task"]["pageAction"] = self.user_data.action elif self.user_data.captcha_type == "hcap" or self.user_data.captcha_type == "hcaptcha": payload["task"]["type"] = "HCaptchaTaskProxyless" + + elif self.user_data.captcha_type == "normal": + payload["task"]["type"] = "ImageToTextTask" + payload["task"]["body"] = self.user_data.get_cap_img(cap_pic_url) + # Get the Queue ID b sending a POST request to their API while True: @@ -62,30 +70,27 @@ def get_id(self) -> int: elif resp["errorCode"] == "ERROR_KEY_DOES_NOT_EXIST": # Throw Exception raise captchaExceptions.WrongAPIKeyException() - - except captchaExceptions.NoBalanceException: - raise captchaExceptions.NoBalanceException() - except captchaExceptions.WrongSitekeyException: - raise captchaExceptions.WrongSitekeyException() - except captchaExceptions.WrongAPIKeyException: - raise captchaExceptions.WrongAPIKeyException() - except Exception: + + elif resp["errorCode"] == "ERROR_TOO_BIG_CAPTCHA_FILESIZE": + raise captchaExceptions.CaptchaIMGTooBig() + except requests.RequestException: pass - def get_answer(self, queue_id) -> str: + def get_answer(self, queue_id, isTextCap=False) -> str: ''' This method gets the captcha token from the API ''' + payload_getAnswer = { + "clientKey":self.user_data.api_key, + "taskId": queue_id + } while True: try: - payload_getAnswer = { - "clientKey":self.user_data.api_key, - "taskId": queue_id - } - answer = requests.post(BASEURL + "/getTaskResult", json=payload_getAnswer).json() - if answer["status"] == "ready": + if answer["status"] == "ready" and not isTextCap: return answer["solution"]["gRecaptchaResponse"] + elif answer["status"] == "ready" and isTextCap: + return answer["solution"]["text"] elif answer["errorId"] == 12 or answer["errorId"] == 16: # Captcha unsolvable || TaskID doesn't exist diff --git a/captchatools/exceptions.py b/captchatools/exceptions.py index b0606b4..ae91df5 100644 --- a/captchatools/exceptions.py +++ b/captchatools/exceptions.py @@ -1,4 +1,4 @@ - +import requests.exceptions class WrongAPIKeyException(Exception): ''' This exception gets thrown when the user provides a wrong API Key @@ -27,4 +27,18 @@ class NoHarvesterException(Exception): This exception gets thrown when a user doesn't properly set a harvester. ''' def __init__(self, message="[captchatools] No captcha harvester selected"): - super(NoHarvesterException, self).__init__(message) \ No newline at end of file + super(NoHarvesterException, self).__init__(message) + +class CaptchaIMGTooBig(Exception): + ''' + This exception gets thrown when the filesize of the captcha image is too big for the solving site. + ''' + def __init__(self, message="[captchatools] Size of the captcha image is too big."): + super(CaptchaIMGTooBig, self).__init__(message) + +class FailedToGetCapIMG(Exception): + ''' + This exception gets thrown when the program fails to get the captcha image after 3 tries + ''' + def __init__(self, message="[captchatools] Failed to fetch captcha image."): + super(FailedToGetCapIMG, self).__init__(message) \ No newline at end of file diff --git a/captchatools/harvesters.py b/captchatools/harvesters.py index 4ff7655..c1af7a7 100644 --- a/captchatools/harvesters.py +++ b/captchatools/harvesters.py @@ -1,8 +1,9 @@ from .twocap import Twocap from .anticaptcha import Anticap from .capmonster import Capmonster -from .exceptions import NoHarvesterException - +from .exceptions import NoHarvesterException, FailedToGetCapIMG +import requests +import base64 class captcha_harvesters: def __init__( self, solving_site=1, api_key="Key the site gave you to solve captchas", @@ -40,6 +41,37 @@ def __init__( self, solving_site=1, def get_token(self): ''' - Returns a captcha token for the provided details + Returns a recaptcha token for the provided details ''' return self.harvester.get_token() + + def get_normal(self, path_to_image): + ''' + This method will handle returning text from 'Normal Captchas.' + + As per 2captcha, + "Normal Captcha is an image that contains distored but human-readable text. To solve the captcha user have to type the text from the image." + + + Parameters: + - path_to_image + - The path where the captcha is located (you must download the image yourself and then pass it in) + ''' + return self.harvester.get_normal(path_to_image) + + @staticmethod + def get_cap_img(img_url:str) -> str: + ''' + This function tries 3 times to get the captcha image. + + If successful, it returns the base64 encoded image. + If not successful, raises a FailedToGetCapIMG exception + ''' + for _ in range(3): + try: + response = requests.get(img_url) + b64response = base64.b64encode(response.content).decode("utf-8") + return b64response + except: + pass + raise FailedToGetCapIMG() \ No newline at end of file diff --git a/captchatools/twocap.py b/captchatools/twocap.py index a82881a..e0fbc41 100644 --- a/captchatools/twocap.py +++ b/captchatools/twocap.py @@ -16,7 +16,10 @@ def __init__(self, parent:object): def get_token(self) -> str: return self.get_answer( self.get_id() ) - def get_id(self) -> int: + def get_normal(self, cap_pic_url) -> str: + return self.get_answer( self.get_id(cap_pic_url) ) + + def get_id(self, cap_pic_url=None) -> int: payload = { "key": self.user_data.api_key, "method": "userrecaptcha", # Because V2 recaptcha is defaulted on init, I'll leave this @@ -42,9 +45,12 @@ def get_id(self) -> int: payload.pop("googlekey") payload["sitekey"] = self.user_data.sitekey + elif self.user_data.captcha_type == "normal": + payload["method"] = "base64" + payload["body"] = self.user_data.get_cap_img(cap_pic_url) while True: try: - resp = requests.post(BASEURL, json=payload).json() + resp = requests.post(BASEURL, data=payload).json() if resp["status"] == 1: return resp["request"] # Return the queue ID @@ -59,16 +65,12 @@ def get_id(self) -> int: elif resp["request"] == "ERROR_WRONG_USER_KEY" or resp["request"] == "ERROR_KEY_DOES_NOT_EXIST": # Throw Exception raise captchaExceptions.WrongAPIKeyException() + + elif resp["request"] == "ERROR_TOO_BIG_CAPTCHA_FILESIZE": + raise captchaExceptions.CaptchaIMGTooBig() break - except captchaExceptions.NoBalanceException: - raise captchaExceptions.NoBalanceException() - except captchaExceptions.WrongSitekeyException: - raise captchaExceptions.WrongSitekeyException() - except captchaExceptions.WrongAPIKeyException: - raise captchaExceptions.WrongAPIKeyException() - - except Exception: + except requests.RequestException: pass def get_answer(self, queue_id) -> str: @@ -88,4 +90,4 @@ def get_answer(self, queue_id) -> str: except KeyError: self.get_token() except Exception: - pass + pass \ No newline at end of file