我看到同样的问题。 这是一个说明问题的小脚本。
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 结果的迭代在 1020 个结果之后停止,而应该有 1869 个结果。 我的脚本每次都在同一点停止。 这可能是一个限速问题吗?
我没有收到错误,结果就用完了。 如果我将查询字符串直接放入 GitHub Web 界面,那么我会看到所有 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 results per search limit 。
我们再提供一种方法get_search_rate_limit()
用于搜索速率限制,而现有的get_rate_limit()
将解析 Github 建议的最新“核心”速率限制: https ://developer.github.com/
搜索 API 速率限制和 GraphQL 速率限制现已推出。 一种方法。
默认情况下,它会显示“核心”速率限制。 您还可以通过访问相应的属性来获取搜索/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)
修改了上面 pokey 的装饰器以考虑 core/search/graphql。
还增加了 30 秒的延迟,因为 Github 并没有在它说的时候准确地重置速率限制。
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 的功能:
该功能可以按如下方式使用: