Мердж капчи в дев
このコミットが含まれているのは:
コミット
633057b8ba
17
README.rst
17
README.rst
|
@ -195,6 +195,23 @@ music.yandex.ru/album/**1193829**/track/**10994777**
|
||||||
|
|
||||||
Больше примеров тут: `proxies - advanced usage - requests <https://2.python-requests.org/en/master/user/advanced/#proxies>`_
|
Больше примеров тут: `proxies - advanced usage - requests <https://2.python-requests.org/en/master/user/advanced/#proxies>`_
|
||||||
|
|
||||||
|
Пример инициализации клиента с обработкой капчи:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
def init_client():
|
||||||
|
client = captcha_key = captcha_answer = None
|
||||||
|
while not client:
|
||||||
|
try:
|
||||||
|
client = Client.from_credentials('login', 'pass', captcha_answer, captcha_key)
|
||||||
|
except Captcha as e:
|
||||||
|
e.captcha.download('captcha.png')
|
||||||
|
|
||||||
|
captcha_key = e.captcha.x_captcha_key
|
||||||
|
captcha_answer = input('Число с картинки: ')
|
||||||
|
|
||||||
|
return client
|
||||||
|
|
||||||
--------------------
|
--------------------
|
||||||
Изучение по примерам
|
Изучение по примерам
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
yandex_music.utils.captcha_response.CaptchaResponse
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
.. autoclass:: yandex_music.utils.captcha_response.CaptchaResponse
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
|
@ -5,4 +5,5 @@
|
||||||
|
|
||||||
yandex_music.utils.request
|
yandex_music.utils.request
|
||||||
yandex_music.utils.response
|
yandex_music.utils.response
|
||||||
|
yandex_music.utils.captcha_response
|
||||||
yandex_music.utils.difference
|
yandex_music.utils.difference
|
|
@ -86,7 +86,7 @@ class Client(YandexMusicObject):
|
||||||
self.account = self.account_status().account
|
self.account = self.account_status().account
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_credentials(cls, username, password, *args, **kwargs):
|
def from_credentials(cls, username, password, x_captcha_answer=None, x_captcha_key=None, *args, **kwargs):
|
||||||
"""Инициализция клиента по логину и паролю.
|
"""Инициализция клиента по логину и паролю.
|
||||||
|
|
||||||
Данный метод получает токен каждый раз при вызове. Рекомендуется сгенерировать его самостоятельно, сохранить и
|
Данный метод получает токен каждый раз при вызове. Рекомендуется сгенерировать его самостоятельно, сохранить и
|
||||||
|
@ -95,13 +95,16 @@ class Client(YandexMusicObject):
|
||||||
Args:
|
Args:
|
||||||
username (:obj:`str`): Логин клиента (идентификатор).
|
username (:obj:`str`): Логин клиента (идентификатор).
|
||||||
password (:obj:`str`): Пароль клиента (аутентификатор).
|
password (:obj:`str`): Пароль клиента (аутентификатор).
|
||||||
|
x_captcha_answer (:obj:`str`, optional): Ответ на капчу (цифры с картинки).
|
||||||
|
x_captcha_key (:obj:`str`, optional): Уникальный ключ капчи.
|
||||||
**kwargs (:obj:`dict`, optional): Аргументы для конструктора клиента.
|
**kwargs (:obj:`dict`, optional): Аргументы для конструктора клиента.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`yandex_music.Client`.
|
:obj:`yandex_music.Client`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return cls(cls().generate_token_by_username_and_password(username, password), *args, **kwargs)
|
return cls(cls().generate_token_by_username_and_password(username, password, x_captcha_answer=x_captcha_answer,
|
||||||
|
x_captcha_key=x_captcha_key), *args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_token(cls, token, *args, **kwargs):
|
def from_token(cls, token, *args, **kwargs):
|
||||||
|
@ -120,14 +123,16 @@ class Client(YandexMusicObject):
|
||||||
return cls(token=token, *args, **kwargs)
|
return cls(token=token, *args, **kwargs)
|
||||||
|
|
||||||
@log
|
@log
|
||||||
def generate_token_by_username_and_password(self, username, password, grant_type='password',
|
def generate_token_by_username_and_password(self, username, password, grant_type='password', x_captcha_answer=None,
|
||||||
timeout=None, *args, **kwargs):
|
x_captcha_key=None, timeout=None, *args, **kwargs):
|
||||||
"""Метод получения OAuth токена по логину и паролю.
|
"""Метод получения OAuth токена по логину и паролю.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
username (:obj:`str`): Логин клиента (идентификатор).
|
username (:obj:`str`): Логин клиента (идентификатор).
|
||||||
password (:obj:`str`): Пароль клиента (аутентификатор).
|
password (:obj:`str`): Пароль клиента (аутентификатор).
|
||||||
grant_type (:obj:`str`, optional): Тип разрешения OAuth.
|
grant_type (:obj:`str`, optional): Тип разрешения OAuth.
|
||||||
|
x_captcha_answer (:obj:`str`, optional): Ответ на капчу (цифры с картинки).
|
||||||
|
x_captcha_key (:obj:`str`, optional): Уникальный ключ капчи.
|
||||||
timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания
|
timeout (:obj:`int` | :obj:`float`, optional): Если это значение указано, используется как время ожидания
|
||||||
ответа от сервера вместо указанного при создании пула.
|
ответа от сервера вместо указанного при создании пула.
|
||||||
**kwargs (:obj:`dict`, optional): Произвольные аргументы (будут переданы в запрос).
|
**kwargs (:obj:`dict`, optional): Произвольные аргументы (будут переданы в запрос).
|
||||||
|
@ -149,6 +154,9 @@ class Client(YandexMusicObject):
|
||||||
'password': password
|
'password': password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if x_captcha_answer and x_captcha_key:
|
||||||
|
data.update({'x_captcha_answer': x_captcha_answer, 'x_captcha_key': x_captcha_key})
|
||||||
|
|
||||||
result = self._request.post(url, data, timeout=timeout, *args, **kwargs)
|
result = self._request.post(url, data, timeout=timeout, *args, **kwargs)
|
||||||
|
|
||||||
return result.get('access_token')
|
return result.get('access_token')
|
||||||
|
|
|
@ -20,6 +20,38 @@ class Unauthorized(YandexMusicError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Captcha(YandexMusicError):
|
||||||
|
"""Базовый класс, представляющий исключение связанное с капчей.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
captcha (:obj:`yandex_music.utils.captcha_response.CaptchaResponse`): Объект класса
|
||||||
|
:class:`yandex_music.utils.captcha_response.CaptchaResponse` представляющий капчу.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (:obj:`str`): Сообщение с ошибкой.
|
||||||
|
captcha (:obj:`yandex_music.utils.captcha_response.CaptchaResponse`): Объект класса
|
||||||
|
:class:`yandex_music.utils.captcha_response.CaptchaResponse` представляющий капчу.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, msg, captcha, *args, **kwargs):
|
||||||
|
self.captcha = captcha
|
||||||
|
super().__init__(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaRequired(Captcha):
|
||||||
|
"""Класс исключения, вызываемый в случае необходимости ввода проверочного кода.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaWrong(Captcha):
|
||||||
|
"""Класс исключения, вызываемый в случае неправильного ввода капчи.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NetworkError(YandexMusicError):
|
class NetworkError(YandexMusicError):
|
||||||
"""Базовый класс исключений, вызываемых для ошибок, связанных с
|
"""Базовый класс исключений, вызываемых для ошибок, связанных с
|
||||||
запросами к серверу.
|
запросами к серверу.
|
||||||
|
@ -37,4 +69,4 @@ class TimedOut(NetworkError):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(TimedOut, self).__init__('Timed out')
|
super().__init__('Timed out')
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
from yandex_music import YandexMusicObject
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaResponse(YandexMusicObject):
|
||||||
|
"""Класс представляющий ответ сервера с запросом на ввод капчи.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
x_captcha_url (:obj:`str`): Ссылка на изображение с капчей.
|
||||||
|
x_captcha_key (:obj:`str`): Уникальный ключ капчи.
|
||||||
|
error_description (:obj:`str`): Описание ошибки.
|
||||||
|
error (:obj:`str`): Код ошибки.
|
||||||
|
client (:obj:`yandex_music.Client`): Объект класса :class:`yandex_music.Client` представляющий клиент Yandex
|
||||||
|
Music.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x_captcha_url (:obj:`str`): Ссылка на изображение с капчей.
|
||||||
|
x_captcha_key (:obj:`str`): Уникальный ключ капчи.
|
||||||
|
error_description (:obj:`str`): Описание ошибки.
|
||||||
|
error (:obj:`str`): Код ошибки.
|
||||||
|
client (:obj:`yandex_music.Client`, optional): Объект класса :class:`yandex_music.Client` представляющий клиент
|
||||||
|
Yandex Music.
|
||||||
|
**kwargs: Произвольные ключевые аргументы полученные от API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
x_captcha_url,
|
||||||
|
x_captcha_key,
|
||||||
|
error_description,
|
||||||
|
error,
|
||||||
|
client=None,
|
||||||
|
**kwargs):
|
||||||
|
self.x_captcha_url = x_captcha_url
|
||||||
|
self.x_captcha_key = x_captcha_key
|
||||||
|
self.error_description = error_description
|
||||||
|
self.error = error
|
||||||
|
|
||||||
|
self.client = client
|
||||||
|
self._id_attrs = (self.x_captcha_key, self.x_captcha_url)
|
||||||
|
|
||||||
|
def download(self, filename=None):
|
||||||
|
"""Загрузка изображения с капчей.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename (:obj:`str`, optional): Путь и(или) название файла вместе с расширением. По умолчанию ключ
|
||||||
|
капчи и расширение `.gif`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not filename:
|
||||||
|
filename = f'{self.x_captcha_key}.gif'
|
||||||
|
|
||||||
|
self.client.request.download(self.x_captcha_url, filename)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def de_json(cls, data, client):
|
||||||
|
"""Десериализация объекта.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (:obj:`dict`): Поля и значения десериализуемого объекта.
|
||||||
|
client (:obj:`yandex_music.Client`): Объект класса :class:`yandex_music.Client` представляющий клиент Yandex
|
||||||
|
Music.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`yandex_music.utils.captcha_response.CaptchaResponse`: Объект класса
|
||||||
|
:class:`yandex_music.utils.captcha_response.CaptchaResponse`.
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = super(CaptchaResponse, cls).de_json(data, client)
|
||||||
|
|
||||||
|
return cls(client=client, **data)
|
|
@ -3,9 +3,10 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from yandex_music.utils.captcha_response import CaptchaResponse
|
||||||
from yandex_music.utils.response import Response
|
from yandex_music.utils.response import Response
|
||||||
from yandex_music.exceptions import Unauthorized, BadRequest, NetworkError, YandexMusicError
|
from yandex_music.exceptions import Unauthorized, BadRequest, NetworkError, YandexMusicError, CaptchaRequired, \
|
||||||
|
CaptchaWrong
|
||||||
|
|
||||||
USER_AGENT = 'Yandex-Music-API'
|
USER_AGENT = 'Yandex-Music-API'
|
||||||
HEADERS = {
|
HEADERS = {
|
||||||
|
@ -78,6 +79,9 @@ class Request:
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
raise YandexMusicError('Invalid server response')
|
raise YandexMusicError('Invalid server response')
|
||||||
|
|
||||||
|
if not data.get('result'):
|
||||||
|
data = {'result': data, 'error': data.get('error'), 'error_description': data.get('error_description')}
|
||||||
|
|
||||||
return Response.de_json(data, self.client)
|
return Response.de_json(data, self.client)
|
||||||
|
|
||||||
def _request_wrapper(self, *args, **kwargs):
|
def _request_wrapper(self, *args, **kwargs):
|
||||||
|
@ -96,9 +100,13 @@ class Request:
|
||||||
if 200 <= resp.status_code <= 299:
|
if 200 <= resp.status_code <= 299:
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
message = self._parse(resp.content).error or 'Unknown HTTPError'
|
parse = self._parse(resp.content)
|
||||||
|
message = parse.error or 'Unknown HTTPError'
|
||||||
|
|
||||||
if resp.status_code in (401, 403):
|
if 'CAPTCHA' in message:
|
||||||
|
exception = CaptchaWrong if 'Wrong' in message else CaptchaRequired
|
||||||
|
raise exception(message, CaptchaResponse.de_json(parse.result, self.client))
|
||||||
|
elif resp.status_code in (401, 403):
|
||||||
raise Unauthorized(message)
|
raise Unauthorized(message)
|
||||||
elif resp.status_code == 400:
|
elif resp.status_code == 400:
|
||||||
raise BadRequest(message)
|
raise BadRequest(message)
|
||||||
|
|
読み込み中…
新しいイシューから参照