A semantic search engine for source code https://bitshift.benkurtovic.com/
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

188 рядки
6.4 KiB

  1. """
  2. :synopsis: Main crawler module, to oversee all site-specific crawlers.
  3. Contains all website/framework-specific Class crawlers.
  4. """
  5. import requests, time, threading
  6. from bitshift.crawler import indexer
  7. from ..codelet import Codelet
  8. from ..database import Database
  9. class GitHubCrawler(threading.Thread):
  10. """
  11. Crawler that retrieves links to all of GitHub's public repositories.
  12. GitHubCrawler is a threaded singleton that queries GitHub's API for urls
  13. to its public repositories, which it inserts into a :class:`Queue.Queue`
  14. shared with :class:`indexer.GitIndexer`.
  15. :ivar clone_queue: (:class:`Queue.Queue`) Contains :class:`GitRepository`
  16. with repository metadata retrieved by :class:`GitHubCrawler`, and other Git
  17. crawlers, to be processed by :class:`indexer.GitIndexer`.
  18. """
  19. AUTHENTICATION = {
  20. "client_id" : "436cb884ae09be7f2a4e",
  21. "client_secret" : "8deeefbc2439409c5b7a092fd086772fe8b1f24e"
  22. }
  23. def __init__(self, clone_queue):
  24. """
  25. Create an instance of the singleton `GitHubCrawler`.
  26. :param clone_queue: see :attr:`self.clone_queue`
  27. :type clone_queue: see :attr:`self.clone_queue`
  28. """
  29. self.clone_queue = clone_queue
  30. super(GitHubCrawler, self).__init__(name=self.__class__.__name__)
  31. def run(self):
  32. """
  33. Query the GitHub API for data about every public repository.
  34. Pull all of GitHub's repositories by making calls to its API in a loop,
  35. accessing a subsequent page of results via the "next" URL returned in an
  36. API response header. Uses Severyn Kozak's (sevko) authentication
  37. credentials. For every new repository, a :class:`GitRepository` is
  38. inserted into :attr:`self.clone_queue`.
  39. """
  40. next_api_url = "https://api.github.com/repositories"
  41. api_request_interval = 5e3 / 60 ** 2
  42. while len(next_api_url) > 0:
  43. start_time = time.time()
  44. try:
  45. response = requests.get(next_api_url,
  46. params=self.AUTHENTICATION)
  47. except ConnectionError as exception:
  48. continue
  49. queue_percent_full = (float(self.clone_queue.qsize()) /
  50. self.clone_queue.maxsize) * 100
  51. for repo in response.json():
  52. while self.clone_queue.full():
  53. time.sleep(1)
  54. self.clone_queue.put(indexer.GitRepository(
  55. repo["html_url"], repo["full_name"].replace("/", ""),
  56. "GitHub"))
  57. if int(response.headers["x-ratelimit-remaining"]) == 0:
  58. time.sleep(int(response.headers["x-ratelimit-reset"]) -
  59. time.time())
  60. next_api_url = response.headers["link"].split(">")[0][1:]
  61. with open(".github_api.log", "w") as log_file:
  62. log_file.write("%s\n" % next_api_url)
  63. sleep_time = api_request_interval - (time.time() - start_time)
  64. if sleep_time > 0:
  65. time.sleep(sleep_time)
  66. def _get_repo_stars(self, repo_name):
  67. """
  68. Return the number of stargazers for a repository.
  69. Queries the GitHub API for the number of stargazers for a given
  70. repository, and blocks if the query limit is exceeded.
  71. :param repo_name: The name of the repository, in
  72. `username/repository_name` format.
  73. :type repo_name: str
  74. :return: The number of stargazers for the repository.
  75. :rtype: int
  76. """
  77. API_URL = "https://api.github.com/search/repositories"
  78. params = self.AUTHENTICATION
  79. params["q"] = "repo:%s" % repo_name
  80. resp = requests.get(API_URL,
  81. params=params,
  82. headers={
  83. "Accept" : "application/vnd.github.preview"
  84. })
  85. if int(resp.headers["x-ratelimit-remaining"]) == 0:
  86. time.sleep(int(resp.headers["x-ratelimit-reset"]) - time.time())
  87. return int(resp.json()["items"][0]["stargazers_count"])
  88. class BitbucketCrawler(threading.Thread):
  89. """
  90. Crawler that retrieves links to all of Bitbucket's public repositories.
  91. BitbucketCrawler is a threaded singleton that queries Bitbucket's API for
  92. urls to its public repositories, and inserts them as
  93. :class:`indexer.GitRepository` into a :class:`Queue.Queue` shared with
  94. :class:`indexer.GitIndexer`.
  95. :ivar clone_queue: (:class:`Queue.Queue`) The shared queue to insert
  96. :class:`indexer.GitRepository` repository urls into.
  97. """
  98. def __init__(self, clone_queue):
  99. """
  100. Create an instance of the singleton `BitbucketCrawler`.
  101. :param clone_queue: see :attr:`self.clone_queue`
  102. :type clone_queue: see :attr:`self.clone_queue`
  103. """
  104. self.clone_queue = clone_queue
  105. super(BitbucketCrawler, self).__init__(name=self.__class__.__name__)
  106. def run(self):
  107. """
  108. Query the Bitbucket API for data about every public repository.
  109. Query the Bitbucket API's "/repositories" endpoint and read its
  110. paginated responses in a loop; any "git" repositories have their
  111. clone-urls and names inserted into a :class:`indexer.GitRepository` in
  112. :attr:`self.clone_queue`.
  113. """
  114. next_api_url = "https://api.bitbucket.org/2.0/repositories"
  115. while True:
  116. try:
  117. response = requests.get(next_api_url).json()
  118. except ConnectionError as exception:
  119. time.sleep(0.5)
  120. continue
  121. queue_percent_full = (float(self.clone_queue.qsize()) /
  122. self.clone_queue.maxsize) * 100
  123. for repo in response["values"]:
  124. if repo["scm"] == "git":
  125. while self.clone_queue.full():
  126. time.sleep(1)
  127. clone_links = repo["links"]["clone"]
  128. clone_url = (clone_links[0]["href"] if
  129. clone_links[0]["name"] == "https" else
  130. clone_links[1]["href"])
  131. links.append("clone_url")
  132. self.clone_queue.put(indexer.GitRepository(
  133. clone_url, repo["full_name"], "Bitbucket"))
  134. next_api_url = response["next"]
  135. with open(".bitbucket_api.log", "w") as log_file:
  136. log_file.write("%s\n" % next_api_url)
  137. time.sleep(0.2)