Skip to content

Commit

Permalink
Merge branch 'main' into issue-3
Browse files Browse the repository at this point in the history
  • Loading branch information
Xewdy444 authored Jan 28, 2023
2 parents 54af78f + 557e48f commit 0a774e5
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 57 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python package

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
python -m pip install flake8 pytest-asyncio .
python -m playwright install --with-deps chromium
- name: Install ffmpeg
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ target/
profile_default/
ipython_config.py

# venv
Scripts/
Include/
pyvenv.cfg

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
Expand Down
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
A Python libary for solving reCAPTCHA v2 and v3 with Playwright.

## Solving reCAPTCHA v2
reCAPTCHA v2 audio challenges are solved by using the Google speech recognition API to transcribe the challenge and enter the text as the response.
reCAPTCHA v2 is solved by transcribing the audio challenge using the Google speech recognition API and entering the text as the response.

## Solving reCAPTCHA v3
reCAPTCHA v3 is solved by waiting for the reload request (https://www.google.com/recaptcha/api2/reload or https://www.google.com/recaptcha/enterprise/reload) response and parsing the token.
reCAPTCHA v3 is solved by waiting for the reload POST request (https://www.google.com/recaptcha/api2/reload or https://www.google.com/recaptcha/enterprise/reload) and parsing the token from the response.

---

Expand All @@ -23,7 +23,7 @@ It's important to note that reCAPTCHA v3 uses a token-based scoring system, wher
pip install playwright-recaptcha
```

This library requires ffmpeg to be installed on your system in order to to convert the audio challenge from reCAPTCHA v2 into text.
This library requires ffmpeg to be installed on your system in order to convert the audio challenge from reCAPTCHA v2 into text.

| OS | Install |
| :-----: | :----------------------------------------------------------------------------------------: |
Expand All @@ -37,6 +37,8 @@ This library requires ffmpeg to be installed on your system in order to to conve
# Examples

## reCAPTCHA v2

### Synchronous
```py
from playwright.sync_api import sync_playwright
from playwright_recaptcha import recaptchav2
Expand All @@ -51,6 +53,7 @@ with sync_playwright() as playwright:
print(token)
```

### Asynchronous
```py
import asyncio
from playwright.async_api import async_playwright
Expand All @@ -70,6 +73,8 @@ asyncio.run(main())
```

## reCAPTCHA v3

### Synchronous
```py
from playwright.sync_api import sync_playwright
from playwright_recaptcha import recaptchav3
Expand All @@ -84,6 +89,7 @@ with sync_playwright() as playwright:
print(token)
```

### Asynchronous
```py
import asyncio
from playwright.async_api import async_playwright
Expand All @@ -103,15 +109,14 @@ asyncio.run(main())
```

# Exceptions
| Exception | Description |
| :---------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| RecaptchaError | The base class for reCAPTCHA exceptions, used as a catch-all for any reCAPTCHA-related errors. |
| RecaptchaVersionError | An exception raised when the reCAPTCHA is not version 3. This indicates that the website is using reCAPTCHA v2, not v3. To solve this issue, simply use the reCAPTCHA v2 solver instead. |
| RecaptchaNotFoundError | An exception raised when the reCAPTCHA was not found on the website. This can happen if the reCAPTCHA v2 has been removed from the page. |
| RecaptchaRateLimitError | An exception raised when the reCAPTCHA rate limit has been reached. This can happen if the library is being used to solve reCAPTCHA v2s too quickly or if the website has implemented rate limiting to prevent automated solving. |
| RecaptchaSolveError | An exception raised when the reCAPTCHA could not be solved. This can happen if the reCAPTCHA v2 could not be solved via speech-to-text conversion in the specified amount of retries. |
| RecaptchaTimeoutError | An exception raised when the reCAPTCHA solve timeout has been reached. This can happen if the library failed to solve the reCAPTCHA v3 in the specified timeout or if the website is experiencing performance issues. |

| Exception | Description |
| :---------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------: |
| RecaptchaError | The base class for reCAPTCHA exceptions, used as a catch-all for any reCAPTCHA-related errors. |
| RecaptchaVersionError | An exception raised when the reCAPTCHA v3 solver is used for reCAPTCHA v2. To solve this issue, simply use the reCAPTCHA v2 solver instead. |
| RecaptchaNotFoundError | An exception raised when the reCAPTCHA v2 was not found on the website. |
| RecaptchaRateLimitError | An exception raised when the reCAPTCHA rate limit has been reached. This can happen if the library is being used to solve reCAPTCHA v2 too quickly. |
| RecaptchaSolveError | An exception raised when the reCAPTCHA v2 could not be solved via speech-to-text conversion in the specified amount of retries. |
| RecaptchaTimeoutError | An exception raised when the reCAPTCHA v3 could not be solved within the specified timeout. |

# Disclaimer
This library is intended for use in automated testing and development environments only and should not be used for any illegal or malicious purposes. Any use of this library for activities that violate the terms of service of any website or service is strictly prohibited. The contributors of this library will not be held liable for any damages or legal issues that may arise from the use of this library. By using this library, you agree to these terms and take full responsibility for your actions.
This library is intended for use in automated testing and development environments only and should not be used for any illegal or malicious purposes. Any use of this library for activities that violate the terms of service of any website or service is strictly prohibited. The contributors of this library will not be held liable for any damages or legal issues that may arise from the use of this library. By using this library, you agree to these terms and take full responsibility for your actions.
2 changes: 0 additions & 2 deletions playwright_recaptcha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,3 @@
"AsyncSolverV3",
"SyncSolverV3",
]

__version__ = "0.0.4"
8 changes: 4 additions & 4 deletions playwright_recaptcha/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ def __init__(self) -> None:


class RecaptchaRateLimitError(RecaptchaError):
"""An exception raised when the reCAPTCHA rate limit has been reached."""
"""An exception raised when the reCAPTCHA rate limit has been exceeded."""

def __init__(self) -> None:
super().__init__("The reCAPTCHA rate limit has been reached.")
super().__init__("The reCAPTCHA rate limit has been exceeded.")


class RecaptchaSolveError(RecaptchaError):
Expand All @@ -31,7 +31,7 @@ def __init__(self) -> None:


class RecaptchaTimeoutError(RecaptchaError):
"""An exception raised when the reCAPTCHA solve timeout has been reached."""
"""An exception raised when the reCAPTCHA solve timeout has been exceeded."""

def __init__(self) -> None:
super().__init__("The reCAPTCHA solve timeout has been reached.")
super().__init__("The reCAPTCHA solve timeout has been exceeded.")
2 changes: 1 addition & 1 deletion playwright_recaptcha/recaptchav2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Recaptcha v2 solver for Playwright."""
"""reCAPTCHA v2 solver for Playwright."""
from playwright_recaptcha.recaptchav2.async_solver import AsyncSolver
from playwright_recaptcha.recaptchav2.sync_solver import SyncSolver

Expand Down
2 changes: 1 addition & 1 deletion playwright_recaptcha/recaptchav3/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Recaptcha v3 solver for Playwright."""
"""reCAPTCHA v3 solver for Playwright."""
from playwright_recaptcha.recaptchav3.async_solver import AsyncSolver
from playwright_recaptcha.recaptchav3.sync_solver import SyncSolver

Expand Down
13 changes: 10 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from setuptools import find_packages, setup

from playwright_recaptcha import __version__

setup(
name="playwright-recaptcha",
version=__version__,
version="0.0.4",
author="Xewdy444",
author_email="[email protected]",
description="A libary for solving reCAPTCHA v2 and v3 with Playwright",
Expand All @@ -20,4 +18,13 @@
"pydub==0.25.1",
"SpeechRecognition==3.9.0",
],
classifiers=[
"Programming Language :: Python :: 3.8",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Intended Audience :: Developers",
"Topic :: Software Development :: Testing",
"Topic :: Internet :: WWW/HTTP :: Browsers",
"Framework :: AsyncIO",
],
)
16 changes: 4 additions & 12 deletions tests/test_async_recaptchav2.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


@pytest.mark.asyncio
@pytest.mark.xfail(raises=RecaptchaRateLimitError)
async def test_solver() -> None:
"""Test the solver with a normal browser."""
async with async_playwright() as playwright:
Expand All @@ -17,15 +18,11 @@ async def test_solver() -> None:
await page.goto("https://www.google.com/recaptcha/api2/demo")

async with recaptchav2.AsyncSolver(page) as solver:
try:
token = await solver.solve_recaptcha()
except RecaptchaRateLimitError:
return

assert token is not None
await solver.solve_recaptcha()


@pytest.mark.asyncio
@pytest.mark.xfail(raises=RecaptchaRateLimitError)
async def test_solver_with_slow_browser() -> None:
"""Test the solver with a slow browser."""
async with async_playwright() as playwright:
Expand All @@ -34,12 +31,7 @@ async def test_solver_with_slow_browser() -> None:
await page.goto("https://www.google.com/recaptcha/api2/demo")

async with recaptchav2.AsyncSolver(page) as solver:
try:
token = await solver.solve_recaptcha()
except RecaptchaRateLimitError:
return

assert token is not None
await solver.solve_recaptcha()


@pytest.mark.asyncio
Expand Down
6 changes: 2 additions & 4 deletions tests/test_async_recaptchav3.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ async def test_solver() -> None:
await page.goto("https://antcpt.com/score_detector/")

async with recaptchav3.AsyncSolver(page) as solver:
token = await solver.solve_recaptcha()
assert token is not None
await solver.solve_recaptcha()


@pytest.mark.asyncio
Expand All @@ -30,8 +29,7 @@ async def test_solver_with_slow_browser() -> None:
await page.goto("https://antcpt.com/score_detector/")

async with recaptchav3.AsyncSolver(page) as solver:
token = await solver.solve_recaptcha()
assert token is not None
await solver.solve_recaptcha()


@pytest.mark.asyncio
Expand Down
15 changes: 4 additions & 11 deletions tests/test_sync_recaptchav2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)


@pytest.mark.xfail(raises=RecaptchaRateLimitError)
def test_solver() -> None:
"""Test the solver with a normal browser."""
with sync_playwright() as playwright:
Expand All @@ -16,14 +17,10 @@ def test_solver() -> None:
page.goto("https://www.google.com/recaptcha/api2/demo")

with recaptchav2.SyncSolver(page) as solver:
try:
token = solver.solve_recaptcha()
except RecaptchaRateLimitError:
return

assert token is not None
solver.solve_recaptcha()


@pytest.mark.xfail(raises=RecaptchaRateLimitError)
def test_solver_with_slow_browser() -> None:
"""Test the solver with a slow browser."""
with sync_playwright() as playwright:
Expand All @@ -32,12 +29,8 @@ def test_solver_with_slow_browser() -> None:
page.goto("https://www.google.com/recaptcha/api2/demo")

with recaptchav2.SyncSolver(page) as solver:
try:
token = solver.solve_recaptcha()
except RecaptchaRateLimitError:
return
solver.solve_recaptcha()

assert token is not None

def test_recaptcha_not_found() -> None:
"""Test the solver with a page that does not have a reCAPTCHA."""
Expand Down
14 changes: 8 additions & 6 deletions tests/test_sync_recaptchav3.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ def test_solver() -> None:
page.goto("https://antcpt.com/score_detector/")

with recaptchav3.SyncSolver(page) as solver:
token = solver.solve_recaptcha()
assert token is not None
solver.solve_recaptcha()


def test_solver_with_slow_browser() -> None:
Expand All @@ -28,8 +27,7 @@ def test_solver_with_slow_browser() -> None:
page.goto("https://antcpt.com/score_detector/")

with recaptchav3.SyncSolver(page) as solver:
token = solver.solve_recaptcha()
assert token is not None
solver.solve_recaptcha()


def test_recaptcha_not_found() -> None:
Expand All @@ -39,7 +37,9 @@ def test_recaptcha_not_found() -> None:
page = browser.new_page()
page.goto("https://www.google.com/")

with pytest.raises(RecaptchaTimeoutError), recaptchav3.SyncSolver(page, timeout=10) as solver:
with pytest.raises(RecaptchaTimeoutError), recaptchav3.SyncSolver(
page, timeout=10
) as solver:
solver.solve_recaptcha()


Expand All @@ -50,5 +50,7 @@ def test_recaptcha_version_error() -> None:
page = browser.new_page()
page.goto("https://cobra.ehr.com/ESS/Home/Login.aspx")

with pytest.raises(RecaptchaVersionError), recaptchav3.SyncSolver(page) as solver:
with pytest.raises(RecaptchaVersionError), recaptchav3.SyncSolver(
page
) as solver:
solver.solve_recaptcha()

0 comments on commit 0a774e5

Please sign in to comment.