Github API: 用python爬取相关数据

这学期选修《社会网络分析》需要爬取些数据,刚接触python对爬虫还不是很熟悉,过程中遇到一些问题,把心得分享给同样学习python爬虫的同学。

  • 教科书般的API接口信息
    Github作为一个出色的代码托管平台,也为开发者们提供了结构非常清晰的API接口信息,浏览器安装json插件后阅读更佳。
  • 详细的开发者文档
    想了解相关参数设置和可爬取的数据,可阅读Github Developer Guide
  • 爬取目标:
    "digital,library"主题下的开源项目合作情况,包含加权贡献值commit,additions,deletions.

  • 注意事项:
    Github的关键词检索功能比较有限,用双引号和逗号相结合表示AND检索.

  • 逻辑思路:

    • 先通过repository_search_url 获取检索结果下的项目信息
      https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}
  • 再根据项目信息中的{owner}{name}信息传递到stats/contributors页面获取相关和做贡献信息
    https://api.github.com/repos/{owner}/{name}/stats/contributors

  • 问题思考:

  1. 使用urlopen()需要导入、安装什么包?如何导入urllib包?
    python3.x已经包含urlllib包,无需再安装,且不同于以往的urllib2urllib分为urllib.requesturllib.error,导入urlopen的方法
from urllib.request import urlopen
  1. 如何解决API Rate Limiting限制?
    初次爬取到的数据只有200多条记录,与事先的搜索结果不符,而且返回http error 403 forbidden,访问请求被禁止,阅读Github Developer Guide后才发现未经过认证的请求上限是60次/hour,打开api url也会发现该页只有30条记录,为了爬取到较为完整的数据,需要添加Authentication认证Pagination分页
  2. 如何实现Authentication认证
    Github提供了三种认证方式,考虑到源码分享后的账户安全问题,用户+密码认证方式不太建议使用,另外就是令牌访问方式。
    首先是如何生成令牌,在你的个人主页setting/personal access token/ generate new token,把生成的token复制保存下来,后面即将用到
  • 方法一:sent in a header
    一开始我是这么请求的
headers = {'Authorization':'ef802a122df2e4d29d9b1b868a6fefb14f22b272'}

然后得到了http error 401 unauthorizated,以为是令牌的问题regenerate了几次,后来才发现是要加上token前缀,网上很多教程都提到要如何生成tokentoken要加在headers里,但真的很少提到这点,敲这篇文章的时候发现Github Developer Guide已经写得很清楚了(阅读说明文档很重要-摊手.jpg)

headers = {'User-Agent':'Mozilla/5.0',
               'Authorization': 'token ef802a122df2e4d29d9b1b868a6fefb14f22b272',
               'Content-Type':'application/json',
               'Accept':'application/json'
               }
  • 方法二:sent as a parameter
 https://api.github.com/?access_token=OAUTH-TOKEN
  1. 如何实现对结果分页?
    Github一页的上限是100,在后面加上page=1&per_page=100即可,建议加上升序或降序排列,后续处理数据将更加方便,根据star值降序排列采用的是e.g.
https://api.github.com/search/repositories?q={search}&page=4&per_page=100&sort=stars&order=desc
  1. json解析器错误
    raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
    网上很多人都遇到了该问题,起初我也以为我也是,有点奇怪的是我在爬取第一页时没有该报错,是后面才有的......
    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,可以简单把它理解为list数组字符串,我们的目的是要将已编码的 JSON 字符串解码为 Python 对象来处理
response = urlopen(req).read()
result = json.loads(response.decode())

注意必须要加上decode(),如果涉及到中文还要加上具体的编码方式如decode('utf-8'),因为在这里response返回的页面信息是bytes,我们要把他转为string
显然我并不是这类解码问题,我也尝试把json的单引号替换成双引号,然而并没有什么用,这个问题真的弄得我寝食难安了一两天。直到我看到某个论坛里有人说可能你要解析的object就是空list,这我需要验证一下了,于是我在代码中加上了print(item['name']),果然在打印了十几条后中止了又跳出该报错,我拿出事先爬取的RepoList比对(这个时候你会发现当初的降序排列是个多么明智的选择)
到该停止结点的页面去看发现果然是空的(由于时间和权限关系,某些repositories已经失效或者没有contributors信息),只有{},所以需要在代码中加上判断条件,但并不是页面为空,而是列表为空,前面提到可以简单把json理解成list数组,所以判断条件是len(JSON)是否为0

完善后成功爬取数据的代码如下:

from urllib.request import urlopen
from urllib.request import Request
import datetime
import json

def get_statistics(owner,name,headers):
    url = 'https://api.github.com/repos/{owner}/{name}/stats/contributors?page=2&per_page=100'.format(owner=owner, name=name)
    req = Request(url,headers=headers)
    response = urlopen(req).read()
    if len(response)==0:
        return response
    else:
        result = json.loads(response.decode())
        return result

def get_results(search,headers):
    url = 'https://api.github.com/search/repositories?q={search}&page=4&per_page=100&sort=stars&order=desc'.format(search=search)
    req = Request(url,headers=headers)
    response = urlopen(req).read()
    result = json.loads(response.decode())
    return result
    

if __name__ == '__main__':
    # 这里用不用转义符没差别
    search = '\"digital,library\"'
    
    headers = {'User-Agent':'Mozilla/5.0',
               'Authorization': 'token ef802a122df2e4d29d9b1b868a6fefb14f22b272',
               'Content-Type':'application/json',
               'Accept':'application/json'
               }

    results = get_results(search,headers)

    f = open('ContributorsInfo4.txt', 'w')
    for item in results['items']:
        name = item['name']
        star = item['stargazers_count']
        owner = item['owner']['login']
        language = str('0') 
        user = str('0')
        commits = 0
        language = item['language']
        stats = get_statistics(owner,name,headers)
        contributor_list = []
        count = len(stats)
        for i in range(0,count):
            user = stats[i]['author']['login']
            commits = stats[i]['total']
            deletions = 0
            additions = 0
            first_commit = None
            last_commit = None
            for week in stats[i]['weeks']:
                deletions += week['d']
                additions += week['a']
                # assume that weeks are ordered
                if first_commit is None and week['c'] > 0:
                    first_commit = week['w']
                if week['c'] > 0:
                    last_commit = week['w']
            contributor_list.append([name,
                                     owner,
                                     star,
                                     language,
                                     count,
                                     user,
                                     commits,
                                     additions,
                                     deletions,
                                     datetime.datetime.fromtimestamp(first_commit).strftime('%Y-%m-%d'),
                                     datetime.datetime.fromtimestamp(last_commit).strftime('%Y-%m-%d')
                                    ])
        for contributor in contributor_list:
            print(contributor,file = f)

参考项目 Github
详细接口信息 API接口
请详细阅读 Github Developer Guide

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容