【React Native实战教程】GitHub Trending API数据的获取

项目开源地址:GitHub PopularGitHubTrending

React Native实战教程】GitHub Trending API数据的获取.png

关于GitHub Trending API的困惑

GitHub Popular中有个treding模块,该模块是GitHub的treding的手机版,在这个模块中你可以使用只有在PC上才能使用的功能。为了开发这个treding模块我们需要获取GitHub的treding的API数据。不过不幸的的是GitHub并没有开放有关trending的API,所以想调GitHub的treding的API已经是不现实的了。

github.com_trending.png
github.com_trending.png

拨开云雾见月明

为了给GitHub Populartreding模块提供可靠的数据支持,我查遍了所有看似可行的方法,但都没能达到要求。本着只要思想不滑坡,方法总比问题多态度,我打开了https://github.com/trending的页面源码研究了起来。

github.com_trending-github_popular
github.com_trending-github_popular

在源码中我发现了能够满足GitHub Populartreding模块的所有数据,但存在如下两个问题:

  1. 冗余的数据太多,我们需要从这些冗余的数据中提取出treding模块真正需要的数据。
  2. 这些数据都是HTML格式的,而我们需要的是Json格式的数据。

GitHubTrending项目的开发

经过上述的分析,我们的需求与任务也逐渐明确了,我们需要一个能为我们提供可靠的https://github.com/trending数据的模块,暂且叫它GitHubTrending吧。这个模块需要满足如下需求:

  1. 接受一个url参数,如:https://github.com/trending/
  2. 能够根据url参数返回对应的json或object数据。

为了实现这一需求,我们需要对请求url返回的数据进行解析,提取出我们所需要的数据,下面就跟大家分享GitHubTrending的具体实现:

数据模型TrendingRepoModel

我们需要让GitHubTrending返回一个包含TrendingRepoModel.js的集合,TrendingRepoModel.js的代码如下:

/**
 * TrendingRepoModel
 * 项目地址:https://github.com/crazycodeboy/GitHubTrending
 * 博客地址:http://www.devio.org
 * @flow
 */

export default class TrendingRepoModel {
  constructor(fullName,url,description,language,meta,contributors,contributorsUrl) {
    this.fullName = fullName;
    this.url = url;
    this.description = description;
    this.language = language;
    this.meta = meta;
    this.contributors = contributors;
    this.contributorsUrl = contributorsUrl;
  }
}

从上面代码中可以看出,TrendingRepoModel.js包含了https://github.com/trending/的所以数据的模型。

将HTML解析成TrendingRepoModel

我们通过TrendingUtil.js将HTML解析成包含TrendingRepoModel.js的集合。下面是TrendingUtil.js文件代码:

/**
 * TrendingUtil
 * 工具类:用于将github trending html 转换成 TrendingRepoModel
 * 项目地址:https://github.com/crazycodeboy/GitHubTrending
 * 博客地址:http://www.devio.org
 * @flow
 */

import TrendingRepoModel from './TrendingRepoModel';
import StringUtil from './StringUtil';

export default class TrendingUtil {
    static htmlToRepo(responseData) {
        responseData = responseData.substring(responseData.indexOf('<li class="repo-list-item'), responseData.indexOf('</ol>')).replace(/\n/, '');
        var repos = [];
        var splitWithH3 = responseData.split('<h3');
        splitWithH3.shift();
        for (var i = 0; i < splitWithH3.length; i++) {
            var repo = new TrendingRepoModel();
            var html = splitWithH3[i];

            this.parseRepoBaseInfo(repo, html);

            var metaNoteContent = this.parseContentOfNode(html, 'repo-list-meta');
            this.parseRepoMeta(repo, metaNoteContent);
            this.parseRepoContributors(repo, metaNoteContent);
            repos.push(repo);
        }
        return repos;
    }

    static parseContentOfNode(htmlStr, classFlag) {
        var noteEnd = htmlStr.indexOf(' class="' + classFlag);
        var noteStart = htmlStr.lastIndexOf('<', noteEnd) + 1;
        var note = htmlStr.substring(noteStart, noteEnd);

        var sliceStart = htmlStr.indexOf(classFlag) + classFlag.length + 2;
        var sliceEnd = htmlStr.indexOf('</' + note + '>', sliceStart);
        var content = htmlStr.substring(sliceStart, sliceEnd);
        return StringUtil.trim(content);
    }

    static parseRepoBaseInfo(repo, htmlBaseInfo) {
        var urlIndex = htmlBaseInfo.indexOf('<a href="') + '<a href="'.length;
        var url = htmlBaseInfo.slice(urlIndex, htmlBaseInfo.indexOf('">', urlIndex));
        repo.url = url;
        repo.fullName = url.slice(1, url.length);

        var description = this.parseContentOfNode(htmlBaseInfo, 'repo-list-description');
        var index = description.indexOf('</g-emoji>');
        if (index !== -1) {
            var indexEmoji = description.indexOf('</g-emoji>');
            var emoji = description.substring(description.indexOf('>') + 1, indexEmoji)
            description = emoji + description.substring(indexEmoji + '</g-emoji>'.length);
        }
        repo.description = description;
    }

    static parseRepoMeta(repo, htmlMeta) {
        var splitWit_n = htmlMeta.split('\n');
        if (splitWit_n[0].search('stars') === -1) {
            repo.language = splitWit_n[0];
        }
        for (var i = 0; i < splitWit_n.length; i++) {
            if (splitWit_n[i].search('stars') !== -1) {
                repo.meta = StringUtil.trim(splitWit_n[i]);
                break;
            }
        }
    }

    static parseRepoContributors(repo, htmlContributors) {
        var splitWitSemicolon = htmlContributors.split('"');
        repo.contributorsUrl = splitWitSemicolon[1];
        var contributors = [];
        for (var i = 0; i < splitWitSemicolon.length; i++) {
            var url = splitWitSemicolon[i];
            if (url.search('http') !== -1) {
                contributors.push(url);
            }
        }
        repo.contributors = contributors;
    }
}

上面代码将HTML解析成一个包含TrendingRepoModel.js的集合,为了去除空行,上述代码中用到了StringUtil.js工具类:

/**
 * 字符串工具类
 * 项目地址:https://github.com/crazycodeboy/GitHubTrending
 * 博客地址:http://www.devio.org
 * @flow
 */
export default class StringUtil {
  /*
  * 去掉字符串左右空格、换行
  */
  static trim( text ){
    if (typeof(text) == "string")  {
      return text.replace(/^\s*|\s*$/g, "");
    }
    else{
      return text;
    }
  }
}

上述代码用于去除字符串中左右空格与换行。

GitHubTrending封装

经过上述步骤之后,我们的准备工作已经完成了,下面我们就可以通过GitHubTrending来提供数据了:

/**
 * 从https://github.com/trending获取数据
 * 项目地址:https://github.com/crazycodeboy/GitHubTrending
 * 博客地址:http://www.devio.org
 * @flow
 */
import TrendingUtil from './TrendingUtil';

export default class GitHubTrending {
  GitHubTrending(){//Singleton pattern
    if (typeof GitHubTrending.instance==='object') {
      return GitHubTrending.instance;
    }
    GitHubTrending.instance=this;
  }
  fetchTrending(url){
    return new Promise((resolve,reject)=>{
      fetch(url)
      .then((response)=>response.text())
      .catch((error)=>{
        reject(error);
        console.log(error);
      }).then((responseData)=>{
        try {
          resolve(TrendingUtil.htmlToRepo(responseData));
        } catch (e) {
          reject(e);
        }
      }).done();
    });
  }
}

上述代码接受一个url,然后通过fetchAPI获取url返回的HTML数据,最后将HTML解析成包含TrendingRepoModel.js的集合。

如何使用GitHubTrending

为了方面大家使用,我已将GitHubTrending发布到npm,大家可以通过下列步骤来使用GitHubTrending

安装

打开在终端中运行如下命名进行安装:

npm i GitHubTrending --save

使用

new GitHubTrending().fetchTrending(url)
    .then((data)=> {
        //
    }).catch((error)=> {
        //
});

更多用例可参考:GitHubPopular:DataRepository.js

总结

从探索使用官方API,到自己动手去实现它,虽然过程比较曲折,但最终还是完成目标。经过反复测试GitHubTrending
现在已经满足了GitHub Popular项目的需求,而且稳定性还是不错的,感兴趣的小伙伴可以下载GitHub Popular
体验一下。

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

推荐阅读更多精彩内容