如何使用GitHub API在GitHub中获取存储库的依赖项信息?

20
当我使用GitHub API v4获取某些信息时,可以通过使用repository.dependencyGraphManifests轻松获取依赖项。但是我找不到任何方法可以使用GitHub API V4来获取依赖项的信息,尽管我可以在 Insights->Dependency Graph->Dependents中看到它。
我想知道是否有可能以某种方式获取GitHub存储库中的依赖项信息?无论是使用GitHub API还是其他方式。

有一些CLI工具可以通过爬取Github来实现(速度相对较慢)。例如:https://github.com/github-tooling/ghtopdep 或 https://github.com/nvuillam/github-dependents-info。 - C. Yduqoli
6个回答

11

我认为你无法使用Github API(Rest或Graphql)获取依赖项目,一种方法是使用类似以下脚本的爬虫:

import requests
from bs4 import BeautifulSoup

repo = "expressjs/express"
page_num = 3
url = 'https://github.com/{}/network/dependents'.format(repo)

for i in range(page_num):
    print("GET " + url)
    r = requests.get(url)
    soup = BeautifulSoup(r.content, "html.parser")

    data = [
        "{}/{}".format(
            t.find('a', {"data-repository-hovercards-enabled":""}).text,
            t.find('a', {"data-hovercard-type":"repository"}).text
        )
        for t in soup.findAll("div", {"class": "Box-row"})
    ]

    print(data)
    print(len(data))
    paginationContainer = soup.find("div", {"class":"paginate-container"}).find('a')
    if paginationContainer:
        url = paginationContainer["href"]
    else:
        break

尝试这个Python脚本


2
目前已经有 DependencyGraphManifest,但撰写本文时它只包括 dependencies 而没有 dependents - Gajus
@Bertrand Martel:我试图在领英上找到你并给你点赞,但没有成功...如果你愿意,请添加我!https://www.linkedin.com/posts/nicolas-vuillamy_stats-markdown-badges-activity-6997663878016868352-sBBi?utm_source=share&utm_medium=member_desktop - Nicolas Vuillamy

6
我将所有答案改进并打包成一个Python命令行实用程序github-dependents-info
pip install github-dependents-info
github-dependents-info --repo nvuillam/npm-groovy-lint --markdownfile ./package-usage.md --sort stars --verbose

除了已有的功能外,它还可以:
- 输出为文本、JSON或markdown文件 - 在单个存储库中管理多个软件包(例如:megalinter) - 失败时重试HTTP请求 - 生成shields.io徽章

enter image description here

示例结果链接


4

在 @Bertrand Martel 的回答基础上,以下是他的代码版本,不需要预先知道 page_num

import requests
from bs4 import BeautifulSoup

repo = "expressjs/express"
url = 'https://github.com/{}/network/dependents'.format(repo)
nextExists = True
result = []
while nextExists:
    r = requests.get(url)
    soup = BeautifulSoup(r.content, "html.parser")

    result = result + [
        "{}/{}".format(
            t.find('a', {"data-repository-hovercards-enabled":""}).text,
            t.find('a', {"data-hovercard-type":"repository"}).text
        )
        for t in soup.findAll("div", {"class": "Box-row"})
    ]
    nextExists = False
    for u in soup.find("div", {"class":"paginate-container"}).findAll('a'):
        if u.text == "Next":
            nextExists = True
            url = u["href"]

for r in result:
  print(r)
print(len(result))

请记住,如果有很多依赖项,它可能会运行很长时间。


嗨@muvaf,首先感谢您的回答。我正在使用您的脚本来获取项目的所有依赖库,例如psf/requests。requests在github上有超过140万个依赖库,但是当我使用这个脚本时,它只能抓取到2831个结果。我可以看到它浏览了111页。您知道为什么它停止收集更多结果吗?非常感谢。 - Shi Tim
运行了几次后,发现结果的总数不一致,例如最后一次运行得到了2842个结果并浏览了116页。 - Shi Tim
看起来是因为页面没有完全加载。我已经用Selenium替换了requests.get。现在它正在获取所有依赖库。 - Shi Tim

4

根据Bertrand Martel(@bertrand-martel)的回答,请不要忘记添加以下代码,以便您不会被卡在第1页和第2页之间。换句话说,它将前进一步,然后后退;因为最初只有一个<a>标签,而下一页有两个这样的标签,所以它选择第一个(“previous”)并返回到上一页。

代码:

...
    paginationContainer = soup.find("div", {"class":"paginate-container"}).find_all('a')
    if len(paginationContainer) > 1:
        paginationContainer = paginationContainer[1]
    else:
        paginationContainer = paginationContainer[0]
...

2

我正在使用一个大型代码库,我需要的是至少有一定数量星星的依赖库。

因此,我修改了 @muvaf 的答案,只查找至少具有 min_stars_cnt 星的依赖库。

您也可以修改脚本中的 result_cnt 变量,以在达到一定数量的库时停止脚本 - 以防您不想等待直到爬取所有依赖项为止:

import time
import requests
from bs4 import BeautifulSoup

repo = "mochajs/mocha"
url = 'https://github.com/{}/network/dependents'.format(repo)
nextExists = True
min_stars_cnt = 50
result_cnt = 100
result = []
while nextExists and len(result) < result_cnt:
    # uncomment the line below to see progress.
    # print("url: " + url + "  " + "cnt: " + str(len(result)))
    r = requests.get(url)
    soup = BeautifulSoup(r.content, "html.parser")

    tmp = [
        {
             "name": "{}/{}".format(
                t.find('a', {"data-repository-hovercards-enabled":""}).text,
                t.find('a', {"data-hovercard-type":"repository"}).text
             ),
             "stars": int(t.find("svg", {"class": "octicon-star"}).parent.text.strip().replace(',', ''))
        }
        for t in soup.findAll("div", {"class": "Box-row"})
    ]
    tmp = list(filter(lambda repo: repo["stars"] > min_stars_cnt, tmp))
    result = result + tmp
    nextExists = False
    try:
        for u in soup.find("div", {"class":"paginate-container"}).findAll('a'):
            if u.text == "Next":
                nextExists = True
                url = u["href"]
    except Exception as e:
        print(e)
        print("waiting for 10 seconds...")
        time.sleep(10)
        nextExists = True

for r in result:
  print(r["name"] + ", " + str(r["stars"]))
print(len(result))

1
一段 脚本(类似于 Python 接受的答案),列出了仓库及其星数和分支数。如果通过管道传输,则脚本返回一个 JSON 数组,否则会生成一个 Ruby REPL。
# frozen_string_literal: true

require 'json'
require 'nokogiri'
require 'open-uri'

$repo = ARGV.fetch(0, "rgeo/rgeo")

Repo = Struct.new(:org, :repo, :stars, :forks)

url = "https://github.com/#$repo/network/dependents"
repos = []

while url
  doc = Nokogiri::HTML(URI.open(url))
  doc.css('#dependents .Box .Box-row').each do |el|
    repos << Repo.new(
      *el.css('.f5 > a').map(&:inner_text),
      *el.at_css('.d-flex').content.delete(" ,").scan(/\d+/).map(&:to_i)
    )
rescue
  binding.irb
  end
  url = doc.at_css('.paginate-container > .BtnGroup > .BtnGroup-item:nth-child(2)').attr("href")
end

if $stdin.tty? && $stdout.tty?
  # check `repos`
  binding.irb
else
  jj repos.map { { name: "#{_1.org}/#{_1.repo}", stars: _1.stars, forks: _1.forks } }
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接