Hi ๐, I am sarartur (Artur Saradzhyan)
I am a software engineer @SimplyBusiness from Boston, Massachussets.
Python wrapper for Chess.com Published-Data API
License: MIT License
I am a software engineer @SimplyBusiness from Boston, Massachussets.
I was trying to use get_player_profile of chessdotcom package, however, it returned the Chessdotcom error (which doesn't say much). I used the requests package and could get successful response for the url request get_player_profile uses. When I debug, I noticed that response at do_get_request method returns OSerror and zlib error. Changing getter method for windows would help. It would also be good to return original error instead of generic chessdotcom error to make it easier for debugging
@classmethod
def do_get_request(cls, path):
r = cls._https.request(
method='GET',
url=cls._base_url + path,
headers=cls.headers
)
if r.status != 200:
raise ChessDotComError(status_code=r.status)
return r
in do_get_request method there is
text = await r.text()
if r.status != 200:
raise ChessDotComError(status_code = r.status, response_text = text)
return text
but text and therefore response_text are always empty. I wanted to retrieve response header and the only way that worked was by using r.header instead of response_text and modifying ChessDotComError class.
Hi,
I am trying to understand the chess.com api wrapper and to begin I just try the testendpoint to see the format of the data retrieve.
I have troubles with this :
data = client.get_player_profile("fabianocaruana")
assert isinstance(data, ChessDotComResponse)
the assert havev a exception with no description
If I try with a simple curl a retrieve a json correctly.
Anay Idea
Hello, this library works like a charm, but I ran into a problem with rate limit of public API.
I want to get the profile of all players that i have played in a certain month, and there are about 1000 games. After certain amount of requests, the server responds with 429 code and Client.loop.run_until_complete throws ChessDotComError(too many requests). What i have tried is to send smaller requests(like 5 users per request) and this works until around 600 users with no problem, but after that a few exceptions are thrown. Is there a way to solve the rate limit problem completely?
The current documentation for 'Configuring Headers' has the following code:
#optional
from chessdotcom import Client
Client.request_config["User-Agent"] = (
"My Python Application. "
"Contact me at [email protected]"
)
Later, when making the actual request, in Client._do_sync_get_request()
in chessdotcom/client.py, this is passed is as **cls.request_config
. This then raises the following error when you actually make a call:
>> from chessdotcom import get_player_profile,Client
>>>
>>>
>>> Client.request_config["User-Agent"] = (
... "My Python Application. "
... "Contact me at [email protected]"
... )
>>>
>>> response = get_player_profile("fabianocaruana")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/me/Scripts/chess_3.11/.venv/lib/python3.11/site-packages/chessdotcom/client.py", line 118, in wrapper
return cls.do_get_request(func(*args, **kwargs))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/Scripts/chess_3.11/.venv/lib/python3.11/site-packages/chessdotcom/client.py", line 112, in do_get_request
return _do_get_request(resource)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/Scripts/chess_3.11/.venv/lib/python3.11/site-packages/chessdotcom/client.py", line 69, in _do_sync_get_request
r = requests.get(
^^^^^^^^^^^^^
File "/home/me/Scripts/chess_3.11/.venv/lib/python3.11/site-packages/requests/api.py", line 73, in get
return request("get", url, params=params, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/me/Scripts/chess_3.11/.venv/lib/python3.11/site-packages/requests/api.py", line 59, in request
return session.request(method=method, url=url, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Session.request() got an unexpected keyword argument 'User-Agent'
Instead, what you need to do is:
Client.request_config["headers"] = {
"User-Agent": (
"My Python Application. "
"Contact me at [email protected]"
)
}
I'm happy to submit a PR for this (hopefully I do it right, haven't done many of them...
In June, Chess.com changed their requirements around what is required in the User-Agent header, outlined in their post here: https://www.chess.com/announcements/view/breaking-change-user-agent-contact-info-required
This causes any calls that use the requests
module to return a 403 error, which comes up for for Client._do_sync_requests()
in version 2.0.6 of this library (and probably earlier).
The post from Chess.com says that contact info for whoever is making the call is required; however, they don't specify how to actually send that. In my testing, it looks like they are actually just blocking anything where the 'User-Agent' header contains 'python-requests' in it. I've posted that testing below here; you can see where it gets 403 responses and where it gets 200 responses.
Edit: I see there is already functionality in the code to allow the user to easily update their headers, specifically User-Agent. In that case, I think it would make more sense to raise an exception if the user has not done that and point them to it. Right now, they instead get the ChessDotComError for the response 403, which is very hard to parse.
My testing on the headers field (s here is a requests.Session
object)
>>> s.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0'
>>> r = s.get('https://api.chess.com/pub/player/scotscotscot')
>>> r
<Response [200]>
>>> r = s.get('https://api.chess.com/pub/player/scotscotscot')
>>> r
<Response [200]>
>>> s.headers['User-Agent'] = "why don't you accept python-requests???"
>>> r = s.get('https://api.chess.com/pub/player/scotscotscot')
>>> r
<Response [403]>
>>> s.headers['User-Agent'] = "asdf"
>>> r = s.get('https://api.chess.com/pub/player/scotscotscot')
>>> r
<Response [200]>
>>> s.headers['User-Agent'] = "a string with a - "
>>> r = s.get('https://api.chess.com/pub/player/scotscotscot')
>>> r
<Response [200]>
>>> s.headers['User-Agent'] = "a string with python"
>>> r = s.get('https://api.chess.com/pub/player/scotscotscot')
>>> r
<Response [200]>
>>> s.headers['User-Agent'] = "a string with python-requests"
>>> s.get('https://api.chess.com/pub/player/scotscotscot')
<Response [403]>
>>> s.headers['User-Agent'] = "a string with word-another"
>>> s.get('https://api.chess.com/pub/player/scotscotscot')
<Response [200]>
>>> s.headers['User-Agent'] = "do you just block python-requests as a string?"
>>> s.get('https://api.chess.com/pub/player/scotscotscot')
<Response [403]>
>>> s.headers['User-Agent'] = "do you just block python requests as a string?"
>>> s.get('https://api.chess.com/pub/player/scotscotscot')
<Response [200]>
from chessdotcom import get_player_profile
response = get_player_profile("fabianocaruana")
player_name = response.json['player']['name']
Running this in Jupiter notebook on Anaconda on Mac returns:
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-1-c115c49e7c76> in <module>
1 from chessdotcom import get_player_profile
2
----> 3 response = get_player_profile("fabianocaruana")
4
5 player_name = response.json['player']['name']
~/opt/anaconda3/lib/python3.7/site-packages/chessdotcom/client.py in wrapper(*args, **kwargs)
53 def wrapper(*args, **kwargs):
54 return cls.handler(func, *args, **kwargs) if Client.aio else cls.loop.run_until_complete(
---> 55 cls.handler(func, *args, **kwargs))
56 return wrapper
57
~/opt/anaconda3/lib/python3.7/asyncio/base_events.py in run_until_complete(self, future)
568 future.add_done_callback(_run_until_complete_cb)
569 try:
--> 570 self.run_forever()
571 except:
572 if new_task and future.done() and not future.cancelled():
~/opt/anaconda3/lib/python3.7/asyncio/base_events.py in run_forever(self)
523 self._check_closed()
524 if self.is_running():
--> 525 raise RuntimeError('This event loop is already running')
526 if events._get_running_loop() is not None:
527 raise RuntimeError(
RuntimeError: This event loop is already running
I've restarted the kernel and re-ran it, but get the same error. Any ideas?
in client.py, the get_player_games_by_month_pgn has the top level attribute set to 'png' instead of 'pgn'.
Hi, Chess.com suggests adding the developer's contact information in order to prevent disruption.
Could this (amazing) wrapper allow that?
In some cases, if we detect abnormal or suspicious activity, we may block your application entirely. If you supply a recognizable user-agent that contains contact information, then if we must block you application we will attempt to contact you to correct the problem. link
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.