GitHub GraphQL API [Quick Start](新手友好)

由于软工项目选的是社交好友分析,又得捡起爬虫的那一套理论。。。社交好友分析应用数据才是王道,没有数据就没有发言权。写一个稳定的爬虫还是很重要的。准备爬github的时候发现github已经不再使用REST API,而是用的一个叫GraphQL API的东西。github上的教程在,但是把这一套教程从头到尾看完花了我几个小时,于是想在这里写一个快速上手教程。

Hint:github GraphQL API 网页版测试工具 很好用,侧面还有文档,可以随时查,很方便

简单介绍GraphQL

现在使用大部分的API都是REST架构的,每一类询问都有一个终端连接,然后通过GET或者POST方式带参数查询就好了,这样的缺点是不同类型的数据(例如用户信息,动态信息,好友信息)就得分开多次查询,而且一次询问会得到许多我们不需要的数据,当我们只想得到用户名对应的用户id时同时会得到用户的其他信息。

而在GraphQL中总共只有一个终端 https://api.github.com/graphql,我们只需要对这个终端POST我们构造好的查询,就可以得到结构上一一对应的json返回数据,有很高的自由度,一个复杂的GraphQL查询有时可以等价于上千条传统REST API查询。(由于这个原因,github对GraphQL的访问频率限制比REST高一些)。

一个简单的Sample

query{
  user(login:"Akaisorani"){
    login
    name
    avatarUrl
    bioHTML
  }
}

解释一下构造方式,QraphQL的操作分为Query和Mutation两种,一个是查询信息,另一个是修改或者发布信息。在爬虫中我们主要使用Query方式。

查询从user切入,由login(登录名,唯一的)切入,依次获得登录名,姓名,头像链接,简介。返回的json是这样的:

{
  "data": {
    "user": {
      "login": "Akaisorani",
      "name": "Akaisora",
      "avatarUrl": "https://avatars0.githubusercontent.com/u/16610448?v=4",
      "bioHTML": ""
    }
  }
}

一个复杂的Sample

query{
  user(login: "Akaisorani") {
	repositories(first: 10,affiliations:[OWNER,COLLABORATOR,ORGANIZATION_MEMBER] ,orderBy: {field: PUSHED_AT, direction: DESC}) {
	  edges {
		node {
		  name
		  owner{
			login
		  }
		  pushedAt
		  
			ref(qualifiedName: "master") {
			  target {
				... on Commit {
				  id
				  history(first: 10,author:{id:"MDQ6VXNlcjE2NjEwNDQ4"}) {
					pageInfo {
					  hasNextPage
					}
					edges {
					  node {
						committedDate
						messageHeadline
						oid
						message
						commitUrl
						author {
						  name
						  email
						}
					  }
					  cursor
					}
				  }
				}
			  }
			}
		  
		}
		cursor
	  }
	  pageInfo{
		hasNextPage
	  }
	}
  }
}

解释一下这个查询都干了啥。

首先从用户切入,获取该用户前10个repositories,角色可以是创建者,合作者以及组织成员,按照最后push的时间倒序给出(github要求这种询问必须要加first或者last数量)。接着是GraphQL中最核心的概念:

edges(边)和node(顶点)

repositories返回的结构并不是一个列表,而是一个边集,每条边指向网络中的一个顶点,repositories的边指向的就是一个个repository的node。通过这条边我们可以获得目标repo的名称,创建者,最后push时间等信息。GraphQL就是这样让我们可以把数据想象成一张网络,我们在网络上爬行,构造灵活巧妙的查询,一次性获得我们需要的一批信息。

我想获得一个repo的最近commits信息,发现在repo里怎么都找不到这个属性,后来网上查才知道是要通过ref来找到commit。

分页信息

上面的查询中可以看到edge有一个叫cursor的属性,每条边会有一个cursor属性,可以用来记录我们已经遍历到了哪一条边,在以后的询问中我们就可以用first:10,after:前一次的cursor,来访问接下来的10条记录。pageInfo属性是和edges并列的分页信息,其中的hasNextPage属性告诉我们是否存在下一页,可用作遍历时的终止条件。

query{
  user(login: "Akaisorani") {
    repositories(first: 3,after:"Y3Vyc29yOnYyOpHOBTV7vw==") {
      edges {
        node {
          name
          owner {
            login
          }
          pushedAt
        }
        cursor
      }
      pageInfo {
        hasNextPage
      }
      totalCount
    }
  }
}

得到的结果应该是

{
  "data": {
    "user": {
      "repositories": {
        "edges": [
          {
            "node": {
              "name": "PyMail",
              "owner": {
                "login": "Akaisorani"
              },
              "pushedAt": "2017-07-10T14:18:16Z"
            },
            "cursor": "Y3Vyc29yOnYyOpHOBcFw/A=="
          },
          {
            "node": {
              "name": "blhx-auto-project",
              "owner": {
                "login": "Akaisorani"
              },
              "pushedAt": "2017-09-04T05:41:57Z"
            },
            "cursor": "Y3Vyc29yOnYyOpHOBhlOIw=="
          },
          {
            "node": {
              "name": "SE-EX1-WordGraph",
              "owner": {
                "login": "Akaisorani"
              },
              "pushedAt": "2017-11-27T15:04:09Z"
            },
            "cursor": "Y3Vyc29yOnYyOpHOBiL0Zg=="
          }
        ],
        "pageInfo": {
          "hasNextPage": true
        },
        "totalCount": 14
      }
    }
  }
}

使用变量

为了使查询可以更容易的复用,GraphQL还可以使用变量,观察一下下面的例子:

query ($login_name: String!, $author_id: ID!) {
  user(login: $login_name) {
    repositories(first: 2, affiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER], orderBy: {field: PUSHED_AT, direction: DESC}) {
      edges {
        node {
          name
          owner {
            login
          }
          pushedAt
          ref(qualifiedName: "master") {
            target {
              ... on Commit {
                id
                history(first: 2, author: {id: $author_id}) {
                  pageInfo {
                    hasNextPage
                  }
                  edges {
                    node {
                      committedDate
                      messageHeadline
                      oid
                      message
                      commitUrl
                      author {
                        name
                        email
                      }
                    }
                    cursor
                  }
                }
              }
            }
          }
        }
        cursor
      }
      pageInfo {
        hasNextPage
      }
    }
  }
}

variables:

{
  "login_name": "Akaisorani",
  "author_id": "MDQ6VXNlcjE2NjEwNDQ4"
}

这样我们就可以先构造查询模板,调用的时候改变参数就好了。

如何用Python实现

怎么用python实现也是个让新手(我)很头疼的事情,一个简明的Sample usage对于新手来说简直是雪中送炭。我经过一番暗黑摸索,搞出了如下套路:

query="""
query ($login_name: String!, $author_id: ID!) {
  user(login: $login_name) {
    repositories(first: 2, affiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER], orderBy: {field: PUSHED_AT, direction: DESC}) {
      edges {
        node {
          name
          owner {
            login
          }
          pushedAt
          ref(qualifiedName: "master") {
            target {
              ... on Commit {
                id
                history(first: 2, author: {id: $author_id}) {
                  pageInfo {
                    hasNextPage
                  }
                  edges {
                    node {
                      committedDate
                      messageHeadline
                      oid
                      message
                      commitUrl
                      author {
                        name
                        email
                      }
                    }
                    cursor
                  }
                }
              }
            }
          }
        }
        cursor
      }
      pageInfo {
        hasNextPage
      }
    }
  }
}
"""

url_graphql="https://api.github.com/graphql"
session=requests.session()
session.headers['Authorization']='bearer %s'%"你的Token"
data={
	"query":query,
	"variables":
		{
		  "login_name": "Akaisorani",
		  "author_id": "MDQ6VXNlcjE2NjEwNDQ4"
		}
}
r=session.post(url_graphql,data=json.dumps(data))
result=r.json()
print(result)

requests包很好用,我们可以创建一个session保存会话,这样就不用每次都设定headers。headers里主要是添加Authorization为bearer方式的Token认证。data和普通post时候传的data不同,要多做一步json转化。

关于Token:Token是你要去自己的github上生成的,官方教程。记得不要把自己的Token给push到github上,不安全,而且会被github扫描出来给你revoke掉(不要问我是怎么知道的)。

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注