同じ問題が発生します。 これは、問題を例示する小さなスクリプトです。
import os
from datetime import datetime
from github import Github
# Login
TOKEN = os.getenv("GITHUB_ACCESS_TOKEN")
github = Github(TOKEN)
# Get initial rate limit and reset time
rl1 = github.get_rate_limit().rate
print("RL1 | Limit: {}, Remaining: {}, Reset: {}.".format(
rl1.limit, rl1.remaining, rl1.reset))
# RL1 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.
# Perform a search
results = github.search_code("Hello World")
# Rate limit of Github instance is unchanged after a search
rl2 = github.get_rate_limit().rate
print("RL2 | Limit: {}, Remaining: {}, Reset: {}.".format(
rl2.limit, rl2.remaining, rl2.reset))
# RL2 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.
# The PaginatedList instance has a Requestor with the same info
rl3 = results._PaginatedList__requester.rate_limiting
rl3_reset = datetime.utcfromtimestamp(int(
results._PaginatedList__requester.rate_limiting_resettime))
print("RL3 | Limit: {}, Remaining: {}, Reset: {}.".format(
rl3[0], rl3[1], rl3_reset))
# RL3 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.
# However, the actual ContentFile results show a different limit
# The Requester of each individual result ...
result = results[0]
rl4 = result._requester.rate_limiting
rl4_reset = datetime.utcfromtimestamp(int(
result._requester.rate_limiting_resettime))
print("RL4 | Limit: {}, Remaining: {}, Reset: {}.".format(
rl4[1], rl4[0], rl4_reset))
# RL4 | Limit: 30, Remaining: 29, Reset: 2017-09-22 16:27:36.
# ... and headers stored in the content file directly show a different rate limit.
rl5_limit = result._headers['x-ratelimit-limit']
rl5_remaining = result._headers['x-ratelimit-remaining']
rl5_reset = datetime.utcfromtimestamp(int(
result._headers['x-ratelimit-reset']))
print("RL5 | Limit: {}, Remaining: {}, Reset: {}.".format(
rl5_limit, rl5_remaining, rl5_reset))
# RL5 | Limit: 30, Remaining: 29, Reset: 2017-09-22 16:27:36.
# In the end, the main Github instance still shows the original full rate limit
rl6 = github.get_rate_limit().rate
print("RL6 | Limit: {}, Remaining: {}, Reset: {}.".format(
rl6.limit, rl6.remaining, rl6.reset))
# RL6 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.
+1この機能は私が構築しようとしているアプリケーションに必要です
アプリケーションの@brentshermanaは、レート制限ヘッダー(最後の応答の、上記の例を参照)を検査するか、 /rate_limit
エンドポイントを自分でポーリングすることを検討してください。 これには、あらゆる種類のレート制限に関する情報が含まれており、レート制限にはカウントされません。
最終的には、PyGithubがrate
を解析するだけでなく、 /rate_limit
が返すものからresources
も解析するのがよいでしょう。 情報はそこにあります、残念ながらそれは図書館の消費者に利用可能にされません。
また、ページ付けされたリストは、そのような検索の結果、つまり_headers['x-ratelimit-*']
に格納されているものを返す場合、コード検索のレート制限を返す必要があります。
ところで:私はちょうど気づきました、 /rate_limit
によって返されるJSONからのフィールドrate
$は非推奨であり、 resources
の情報が推奨される代替手段です: https ://developer.github.com/
私はまさにそれをやっています。 誰かがこれを適応させてプルリクエストを試みたいのなら、あなたには私の祝福があります:
def wait(seconds):
print("Waiting for {} seconds ...".format(seconds))
time.sleep(seconds)
print("Done waiting - resume!")
def api_wait():
url = 'https://api.github.com/rate_limit'
response = urlopen(url).read()
data = json.loads(response.decode())
if data['resources']['core']['remaining'] <= 10: # extra margin of safety
reset_time = data['resources']['core']['reset']
wait(reset_time - time.time() + 10)
elif data['resources']['search']['remaining'] <= 2:
reset_time = data['resources']['search']['reset']
wait(reset_time - time.time() + 10)
search_issuesからの結果に対する反復が、1869件の結果があるはずの1020件の結果の後に停止するという問題が発生しています。 私のスクリプトは毎回同じ時点で停止します。 これは律速の問題でしょうか?
エラーは発生しません。結果がなくなるだけです。 クエリ文字列をGitHubWebインターフェイスに直接配置すると、期待どおりに1869件の結果がすべて表示されます。 1020は30の倍数であるため、ページ付けの問題かどうか疑問に思います。
コードは次のとおりです。
querystring = "type:pr is:closed repo:xxxx closed:2017-07-01..2018-06-30"
issues = git.search_issues(query=querystring, sort="updated", order="asc")
for issue in issues:
pull = issue.as_pull_request()
print "%s: %s" % (pull.number, pull.title)
ここで何がうまくいかないかについて共有できるヒントに感謝します。
また、 issues.reversed
を繰り返し処理して、予想される1869年の結果の最後に開始されるかどうかを確認しました。 ただし、この場合、結果の最初のページから30の問題しか取得できません。
さらに調査したところ、検索制限ごとに1000件の結果が発生しているようです。
検索レート制限にもう1つのメソッドget_search_rate_limit()
を提供し、既存のget_rate_limit()
はGithubによって提案された最新の「コア」レート制限を解析します: https ://developer.github.com/
SearchAPIのレート制限とGraphQLのレート制限が利用可能になりました。 すべてのための1つの方法。
デフォルトでは、「コア」レート制限が表示されます。 それぞれの属性にアクセスして、検索/ graphqlレート制限を取得することもできます。
r = g.get_rate_limit()
>>> r
RateLimit(core=Rate(remaining=4923, limit=5000))
>>> r.search
Rate(remaining=30, limit=30)
>>> r.graphql
Rate(remaining=5000, limit=5000)
よさそうだ、@ sfdyeに感謝!
@brentshermanaの待機関数をエミュレートして、検索レート制限の問題を回避するために、次のような操作を実行できます。
from datetime import datetime
def api_wait_search(git):
limits = git.get_rate_limit()
if limits.search.remaining <= 2:
seconds = (limits.search.reset - datetime.now()).total_seconds()
print "Waiting for %d seconds ..." % (seconds)
time.sleep(seconds)
print "Done waiting - resume!"
get_rate_limit()
を呼び出すとわずかな遅延が発生するため、これを呼び出す頻度を最小限に抑えることができます。
検索エンジンからここに到着した人のために、@ bbi-yggyの関数を少し変更しました。
from datetime import datetime, timezone
def rate_limited_retry(github):
def decorator(func):
def ret(*args, **kwargs):
for _ in range(3):
try:
return func(*args, **kwargs)
except RateLimitExceededException:
limits = github.get_rate_limit()
reset = limits.search.reset.replace(tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
seconds = (reset - now).total_seconds()
print(f"Rate limit exceeded")
print(f"Reset is in {seconds:.3g} seconds.")
if seconds > 0.0:
print(f"Waiting for {seconds:.3g} seconds...")
time.sleep(seconds)
print("Done waiting - resume!")
raise Exception("Failed too many times")
return ret
return decorator
この関数は次のように使用できます。
@rate_limited_retry(github)
def run_query(import_string):
query_string = f"language:Python \"{import_string}\""
return list(github.search_code(query_string))
results = run_query(import_string)
core / search / graphqlを考慮に入れるために上記のポーキーのデコレータの修正バージョン。
また、Githubが指定した時点でレート制限を正確にリセットしないため、30秒の遅延が追加されました。
def rate_limited_retry():
def decorator(func):
def ret(*args, **kwargs):
for _ in range(3):
try:
return func(*args, **kwargs)
except RateLimitExceededException:
limits = gh.get_rate_limit()
print(f"Rate limit exceeded")
print("Search:", limits.search, "Core:", limits.core, "GraphQl:", limits.graphql)
if limits.search.remaining == 0:
limited = limits.search
elif limits.graphql.remaining == 0:
limited = limits.graphql
else:
limited = limits.core
reset = limited.reset.replace(tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
seconds = (reset - now).total_seconds() + 30
print(f"Reset is in {seconds} seconds.")
if seconds > 0.0:
print(f"Waiting for {seconds} seconds...")
time.sleep(seconds)
print("Done waiting - resume!")
raise Exception("Failed too many times")
return ret
return decorator
最も参考になるコメント
検索エンジンからここに到着した人のために、@ bbi-yggyの関数を少し変更しました。
この関数は次のように使用できます。