瀏覽代碼

Improving loggin, add captcha support

JoostSijm 3 年之前
父節點
當前提交
f22adedfcf

+ 1 - 0
Pipfile

@@ -20,6 +20,7 @@ pathlib2 = "*"
 requests = "*"
 selenium-stealth = "*"
 webbot = "*"
+python-anticaptcha = "*"
 
 [requires]
 python_version = "3"

+ 58 - 50
Pipfile.lock

@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "a369f80373274e99e967fe2f267be5b19893c3bc9e2d56028b84a0af0c814ea6"
+            "sha256": "8c9bda89691f191d21a24eaf2d5cef398aa8d6b02b29a184fd8607a4dc16c267"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -35,10 +35,10 @@
         },
         "certifi": {
             "hashes": [
-                "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
-                "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
+                "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
+                "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
             ],
-            "version": "==2020.12.5"
+            "version": "==2021.5.30"
         },
         "cfscrape": {
             "hashes": [
@@ -66,11 +66,19 @@
         },
         "pathlib2": {
             "hashes": [
-                "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db",
-                "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"
+                "sha256:3a130b266b3a36134dcc79c17b3c7ac9634f083825ca6ea9d8f557ee6195c9c8",
+                "sha256:7d8bcb5555003cdf4a8d2872c538faa3a0f5d20630cb360e518ca3b981795e5f"
             ],
             "index": "pypi",
-            "version": "==2.3.5"
+            "version": "==2.3.6"
+        },
+        "python-anticaptcha": {
+            "hashes": [
+                "sha256:6d13df6702222b7adac29872f826079a72008177e95b4095a33d66b1050e8d86",
+                "sha256:f719fa76cdb0597ca6ff377540b999a1ecc77ce4e04c10f7e286d8dd8d189cc6"
+            ],
+            "index": "pypi",
+            "version": "==0.7.1"
         },
         "python-dateutil": {
             "hashes": [
@@ -104,11 +112,11 @@
         },
         "six": {
             "hashes": [
-                "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
-                "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
+                "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+                "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==1.15.0"
+            "version": "==1.16.0"
         },
         "soupsieve": {
             "hashes": [
@@ -120,11 +128,11 @@
         },
         "urllib3": {
             "hashes": [
-                "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
-                "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
+                "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
+                "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
-            "version": "==1.26.4"
+            "version": "==1.26.6"
         },
         "webbot": {
             "hashes": [
@@ -146,26 +154,26 @@
         },
         "astroid": {
             "hashes": [
-                "sha256:6b0ed1af831570e500e2437625979eaa3b36011f66ddfc4ce930128610258ca9",
-                "sha256:cd80bf957c49765dce6d92c43163ff9d2abc43132ce64d4b1b47717c6d2522df"
+                "sha256:38b95085e9d92e2ca06cf8b35c12a74fa81da395a6f9e65803742e6509c05892",
+                "sha256:606b2911d10c3dcf35e58d2ee5c97360e8477d7b9f3efc3f24811c93e6fc2cd9"
             ],
-            "markers": "python_version >= '3.6'",
-            "version": "==2.5.2"
+            "markers": "python_version ~= '3.6'",
+            "version": "==2.6.2"
         },
         "attrs": {
             "hashes": [
-                "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
-                "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
+                "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
+                "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
             ],
-            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==20.3.0"
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+            "version": "==21.2.0"
         },
         "distlib": {
             "hashes": [
-                "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb",
-                "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"
+                "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736",
+                "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"
             ],
-            "version": "==0.3.1"
+            "version": "==0.3.2"
         },
         "filelock": {
             "hashes": [
@@ -191,11 +199,11 @@
         },
         "isort": {
             "hashes": [
-                "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6",
-                "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"
+                "sha256:eed17b53c3e7912425579853d078a0832820f023191561fcee9d7cae424e0813",
+                "sha256:f65ce5bd4cbc6abdfbe29afc2f0245538ab358c14590912df638033f157d555e"
             ],
-            "markers": "python_version >= '3.6' and python_version < '4.0'",
-            "version": "==5.8.0"
+            "markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
+            "version": "==5.9.2"
         },
         "lazy-object-proxy": {
             "hashes": [
@@ -277,11 +285,11 @@
         },
         "packaging": {
             "hashes": [
-                "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
-                "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
+                "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
+                "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
             ],
-            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==20.9"
+            "markers": "python_version >= '3.6'",
+            "version": "==21.0"
         },
         "pluggy": {
             "hashes": [
@@ -301,11 +309,11 @@
         },
         "pylint": {
             "hashes": [
-                "sha256:209d712ec870a0182df034ae19f347e725c1e615b2269519ab58a35b3fcbbe7a",
-                "sha256:bd38914c7731cdc518634a8d3c5585951302b6e2b6de60fbb3f7a0220e21eeee"
+                "sha256:23a1dc8b30459d78e9ff25942c61bb936108ccbe29dd9e71c01dc8274961709a",
+                "sha256:5d46330e6b8886c31b5e3aba5ff48c10f4aa5e76cbf9002c6544306221e63fbc"
             ],
             "index": "pypi",
-            "version": "==2.7.4"
+            "version": "==2.9.3"
         },
         "pyparsing": {
             "hashes": [
@@ -317,11 +325,11 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634",
-                "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"
+                "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
+                "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
             ],
             "index": "pypi",
-            "version": "==6.2.3"
+            "version": "==6.2.4"
         },
         "pytest-vcr": {
             "hashes": [
@@ -333,11 +341,11 @@
         },
         "python-dotenv": {
             "hashes": [
-                "sha256:471b782da0af10da1a80341e8438fca5fadeba2881c54360d5fd8d03d03a4f4a",
-                "sha256:49782a97c9d641e8a09ae1d9af0856cc587c8d2474919342d5104d85be9890b2"
+                "sha256:dd8fe852847f4fbfadabf6183ddd4c824a9651f02d51714fa075c95561959c7d",
+                "sha256:effaac3c1e58d89b3ccb4d04a40dc7ad6e0275fda25fd75ae9d323e2465e202d"
             ],
             "index": "pypi",
-            "version": "==0.17.0"
+            "version": "==0.18.0"
         },
         "pyyaml": {
             "hashes": [
@@ -376,11 +384,11 @@
         },
         "six": {
             "hashes": [
-                "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
-                "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
+                "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+                "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==1.15.0"
+            "version": "==1.16.0"
         },
         "toml": {
             "hashes": [
@@ -392,11 +400,11 @@
         },
         "tox": {
             "hashes": [
-                "sha256:05a4dbd5e4d3d8269b72b55600f0b0303e2eb47ad5c6fe76d3576f4c58d93661",
-                "sha256:e007673f3595cede9b17a7c4962389e4305d4a3682a6c5a4159a1453b4f326aa"
+                "sha256:307a81ddb82bd463971a273f33e9533a24ed22185f27db8ce3386bff27d324e3",
+                "sha256:b0b5818049a1c1997599d42012a637a33f24c62ab8187223fdd318fa8522637b"
             ],
             "index": "pypi",
-            "version": "==3.23.0"
+            "version": "==3.23.1"
         },
         "tox-venv": {
             "hashes": [
@@ -416,11 +424,11 @@
         },
         "virtualenv": {
             "hashes": [
-                "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107",
-                "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c"
+                "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467",
+                "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
-            "version": "==20.4.3"
+            "version": "==20.4.7"
         },
         "wrapt": {
             "hashes": [

+ 1 - 0
setup.py

@@ -25,6 +25,7 @@ setuptools.setup(
         "requests",
         "selenium-stealth",
         "webbot",
+        "python-anticaptcha",
     ],
     classifiers=[
         "Programming Language :: Python :: 3",

+ 14 - 32
src/rival_regions_wrapper/authentication_handler.py

@@ -1,46 +1,21 @@
 """
-Authentication handeler module
+Authentication handler module
 """
 
 import time
-
 import sys
 import re
 
+from python_anticaptcha import AnticaptchaClient, ImageToTextTask
+
 import requests
 import cfscrape
 
 from rival_regions_wrapper import LOGGER, login_methods
 from rival_regions_wrapper.cookie_handler import CookieHandler
 from rival_regions_wrapper.browser import Browser
-
-
-class RRClientException(Exception):
-    """RR exception"""
-    def __init__(self, *args, **kwargs):
-        Exception.__init__(self, *args, **kwargs)
-        LOGGER.warning('RRClientException')
-
-
-class SessionExpireException(Exception):
-    """Raise when session has expired"""
-    def __init__(self, *args, **kwargs):
-        Exception.__init__(self, *args, **kwargs)
-        LOGGER.warning('Session has expired')
-
-
-class NoLogginException(Exception):
-    """Raise exception when client isn't logged in"""
-    def __init__(self, *args, **kwargs):
-        Exception.__init__(self, *args, **kwargs)
-        LOGGER.warning('Not logged in')
-
-
-class NoCookieException(Exception):
-    """Raise exception when there is no cookie found"""
-    def __init__(self, *args, **kwargs):
-        Exception.__init__(self, *args, **kwargs)
-        LOGGER.warning('No cookie found')
+from rival_regions_wrapper.exceptions import SessionExpireException, \
+        NoLogginException, NoCookieException
 
 
 def session_handler(func):
@@ -71,9 +46,11 @@ class AuthenticationHandler:
     username = None
     password = None
     session = None
+    captcha_client = None
 
-    def __init__(self, show_window=False):
+    def __init__(self, show_window=False, captcha_client=None):
         self.show_window = show_window
+        self.captcha_client = captcha_client
         LOGGER.info('Initialize authentication handler, show window: "%s"',
                     self.show_window)
 
@@ -113,7 +90,11 @@ class AuthenticationHandler:
 
             if self.login_method in login_method_dict:
                 browser = login_method_dict[self.login_method](
-                        browser, auth_text, self.username, self.password
+                        browser,
+                        auth_text,
+                        self.username,
+                        self.password,
+                        self.captcha_client
                     )
             else:
                 LOGGER.info(
@@ -255,6 +236,7 @@ class AuthenticationHandler:
     @classmethod
     def check_response(cls, response):
         """Check resonse for authentication"""
+        # print(response.text)
         if "Session expired, please, reload the page" in response.text or \
                 'window.location="https://rivalregions.com";' in response.text:
             raise SessionExpireException()

+ 2 - 0
src/rival_regions_wrapper/browser.py

@@ -38,12 +38,14 @@ class Browser(webbot.Browser):
                 performing an action like click ,type etc.
     """
     def __init__(self, showWindow=True, proxy=None, downloadPath=None):
+        super().__init__(showWindow, proxy, downloadPath)
         options = webdriver.ChromeOptions()
         options.add_argument("--disable-dev-shm-usage")
         options.add_argument("--no-sandbox")
         options.add_argument('--disable-web-security')
         options.add_argument('--allow-running-insecure-content')
         options.add_argument("user-agent=DN")
+        options.add_argument("--disable-blink-features=AutomationControlled")
         options.add_experimental_option(
                 "excludeSwitches", ["enable-automation"]
             )

+ 47 - 0
src/rival_regions_wrapper/exceptions.py

@@ -0,0 +1,47 @@
+"""
+Exceptions used in Rival Regions Wrapper
+"""
+
+from rival_regions_wrapper import LOGGER
+
+
+class RRClientException(Exception):
+    """RR exception"""
+    def __init__(self, *args, **kwargs):
+        Exception.__init__(self, *args, **kwargs)
+        LOGGER.warning('RRClientException')
+
+
+class SessionExpireException(Exception):
+    """Raise when session has expired"""
+    def __init__(self, *args, **kwargs):
+        Exception.__init__(self, *args, **kwargs)
+        LOGGER.warning('Session has expired')
+
+
+class NoLogginException(Exception):
+    """Raise exception when client isn't logged in"""
+    def __init__(self, *args, **kwargs):
+        Exception.__init__(self, *args, **kwargs)
+        LOGGER.warning('Not logged in')
+
+
+class NoCookieException(Exception):
+    """Raise exception when there is no cookie found"""
+    def __init__(self, *args, **kwargs):
+        Exception.__init__(self, *args, **kwargs)
+        LOGGER.warning('No cookie found')
+
+
+class NoCaptchaClientException(Exception):
+    """Raise exception when captcha client is missing"""
+    def __init__(self, *args, **kwargs):
+        Exception.__init__(self, *args, **kwargs)
+        LOGGER.warning('No Captcha client given')
+
+
+class LoginException(Exception):
+    """Raise exception when there is an error during login"""
+    def __init__(self, *args, **kwargs):
+        Exception.__init__(self, *args, **kwargs)
+        LOGGER.warning('Error during login')

+ 69 - 15
src/rival_regions_wrapper/login_methods.py

@@ -2,47 +2,101 @@
 
 import time
 
+import requests
+from python_anticaptcha import ImageToTextTask
+
 from rival_regions_wrapper import LOGGER
+from rival_regions_wrapper.exceptions import NoCaptchaClientException, \
+        LoginException
 
 
 # This should be working
-def login_google(browser, auth_text, username, password):
+def login_google(browser, auth_text, username, password, captcha_client=None):
     """login using Google"""
-    LOGGER.info('"%s": Login method Google', username)
+    LOGGER.info('"%s": Google: Login start', username)
     auth_text1 = auth_text.split('\t<a href="')
     auth_text2 = auth_text1[1].split('" class="sa')
     time.sleep(1)
     browser.go_to(auth_text2[0])
 
-    LOGGER.info('"%s": Typing in username', username)
+    # browser.get_screenshot_as_file('test_1.png')
+
+    LOGGER.info('"%s": Google: Typing in username', username)
     browser.type(username, into='Email')
 
-    LOGGER.info('"%s": pressing next button', username)
-    browser.click(css_selector="#next")
-    time.sleep(2)
+    # browser.get_screenshot_as_file('test_2.png')
+
+    LOGGER.info('"%s": Google: pressing next button', username)
+    browser.click(css_selector='#next')
+    time.sleep(1)
+
+    # browser.get_screenshot_as_file('test_3.png')
+
+    if browser.driver.find_elements_by_css_selector('#captcha-box'):
+        LOGGER.info('"%s": Google: Captcha present', username)
+        if not captcha_client:
+            raise NoCaptchaClientException()
+        captcha_url = browser \
+                .find_elements(css_selector='#captcha-img img')[0] \
+                .get_attribute('src')
+        LOGGER.debug('"%s": Google: Captcha url: "%s"', username, captcha_url)
+        image = requests.get(captcha_url, stream=True).raw
+        image.decode_content = True
+
+        task = ImageToTextTask(image)
+        job = captcha_client.createTask(task)
+        LOGGER.info('"%s": Google: Start solve captcha', username)
+        job.join()
+
+        LOGGER.info('"%s": Google: captcha: "%s"', username, job.get_captcha_text())
+        browser.type(
+                job.get_captcha_text(),
+                css_selector='#identifier-captcha-input'
+            )
+        browser.click(css_selector='#next')
+
+        # browser.get_screenshot_as_file('test_4.png')
+
+    if not browser.driver.find_elements_by_css_selector('#password'):
+        raise LoginException()
+
+    # with open('source.html', 'w') as source:
+    #     source.write(browser.get_page_source())
 
-    LOGGER.info('"%s": Typing in password', username)
-    browser.type(password, css_selector="input")
+    LOGGER.info('"%s": Google: Typing in password', username)
+    browser.type(password, css_selector='input')
 
-    LOGGER.info('"%s": pressing sign in button', username)
-    browser.click(css_selector="#submit")
+    # browser.get_screenshot_as_file('test_5.png')
+
+    LOGGER.info('"%s": Google: pressing sign in button', username)
+    browser.click(css_selector='#submit')
     time.sleep(3)
 
+    # browser.get_screenshot_as_file('test_6.png')
+
     # Some why it wont click and login immediately. This seems to work
-    time.sleep(1)
     browser.go_to(auth_text2[0])
     time.sleep(1)
+
+    # browser.get_screenshot_as_file('test_7.png')
+
     browser.go_to(auth_text2[0])
     time.sleep(1)
+
+    # browser.get_screenshot_as_file('test_8.png')
+
     browser.click(
-        css_selector="#sa_add2 > div:nth-child(4) > a.sa_link.gogo > div"
+        css_selector='#sa_add2 > div:nth-child(4) > a.sa_link.gogo > div'
     )
-    time.sleep(3)
+    time.sleep(2)
+
+    # browser.get_screenshot_as_file('test_9.png')
+
     return browser
 
 
 # IDK if this is working
-def login_vk(browser, auth_text, username, password):
+def login_vk(browser, auth_text, username, password, captcha_client=None):
     """login using VK"""
     LOGGER.info('Login method VK')
     auth_text1 = auth_text.split("(\'.vkvk\').attr(\'url\', \'")
@@ -59,7 +113,7 @@ def login_vk(browser, auth_text, username, password):
 
 
 # IDK if this is working
-def login_facebook(browser, auth_text, username, password):
+def login_facebook(browser, auth_text, username, password, captcha_client=None):
     """login using Facebook"""
     LOGGER.info('Login method Facebook')
     auth_text1 = \

+ 3 - 2
src/rival_regions_wrapper/middleware.py

@@ -22,8 +22,9 @@ class MiddlewareBase(ABC):
 class LocalAuthentication(MiddlewareBase):
     """Local authentication"""
 
-    def __init__(self, username, password, login_method, show_window=False):
-        self.client = AuthenticationHandler(show_window)
+    def __init__(self, username, password, login_method,
+            show_window=False, captcha_client=None):
+        self.client = AuthenticationHandler(show_window, captcha_client)
         self.client.set_credentials({
             'username': username,
             'password': password,

+ 9 - 1
tests/conftest.py

@@ -5,6 +5,7 @@ import os
 import pytest
 import _pytest.skipping
 from dotenv import load_dotenv
+from python_anticaptcha import AnticaptchaClient
 
 from rival_regions_wrapper.middleware import LocalAuthentication
 
@@ -49,9 +50,16 @@ def api_wrapper():
     username = os.environ.get('USERNAME', None)
     password = os.environ.get('PASSWORD', None)
     login_method = os.environ.get('LOGIN_METHOD', None)
+    captcha_key = os.environ.get('CAPTCHA_KEY', None)
     if None in (username, password, login_method):
         raise MissingAuthenticationError(
             'Load the following variables in your user environment: '
             'username, password, login_method'
         )
-    return LocalAuthentication(username, password, login_method)
+    return LocalAuthentication(
+            username,
+            password,
+            login_method,
+            False,
+            AnticaptchaClient(captcha_key),
+        )