【React Native實戰(zhàn)教程】GitHub Trending API數(shù)據(jù)的獲取

項目開源地址:GitHub PopularGitHubTrending

React Native實戰(zhàn)教程】GitHub Trending API數(shù)據(jù)的獲取.png

關于GitHub Trending API的困惑

GitHub Popular中有個treding模塊,該模塊是GitHub的treding的手機版,在這個模塊中你可以使用只有在PC上才能使用的功能。為了開發(fā)這個treding模塊我們需要獲取GitHub的treding的API數(shù)據(jù)。不過不幸的的是GitHub并沒有開放有關trending的API,所以想調(diào)GitHub的treding的API已經(jīng)是不現(xiàn)實的了。

github.com_trending.png
github.com_trending.png

撥開云霧見月明

為了給GitHub Populartreding模塊提供可靠的數(shù)據(jù)支持,我查遍了所有看似可行的方法,但都沒能達到要求。本著只要思想不滑坡,方法總比問題多態(tài)度,我打開了https://github.com/trending的頁面源碼研究了起來。

github.com_trending-github_popular
github.com_trending-github_popular

在源碼中我發(fā)現(xiàn)了能夠滿足GitHub Populartreding模塊的所有數(shù)據(jù),但存在如下兩個問題:

  1. 冗余的數(shù)據(jù)太多,我們需要從這些冗余的數(shù)據(jù)中提取出treding模塊真正需要的數(shù)據(jù)。
  2. 這些數(shù)據(jù)都是HTML格式的,而我們需要的是Json格式的數(shù)據(jù)。

GitHubTrending項目的開發(fā)

經(jīng)過上述的分析,我們的需求與任務也逐漸明確了,我們需要一個能為我們提供可靠的https://github.com/trending數(shù)據(jù)的模塊,暫且叫它GitHubTrending吧。這個模塊需要滿足如下需求:

  1. 接受一個url參數(shù),如:https://github.com/trending/。
  2. 能夠根據(jù)url參數(shù)返回對應的json或object數(shù)據(jù)。

為了實現(xiàn)這一需求,我們需要對請求url返回的數(shù)據(jù)進行解析,提取出我們所需要的數(shù)據(jù),下面就跟大家分享GitHubTrending的具體實現(xiàn):

數(shù)據(jù)模型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/的所以數(shù)據(jù)的模型。

將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封裝

經(jīng)過上述步驟之后,我們的準備工作已經(jīng)完成了,下面我們就可以通過GitHubTrending來提供數(shù)據(jù)了:

/**
 * 從https://github.com/trending獲取數(shù)據(jù)
 * 項目地址: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數(shù)據(jù),最后將HTML解析成包含TrendingRepoModel.js的集合。

如何使用GitHubTrending

為了方面大家使用,我已將GitHubTrending發(fā)布到npm,大家可以通過下列步驟來使用GitHubTrending。

安裝

打開在終端中運行如下命名進行安裝:

npm i GitHubTrending --save

使用

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

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

總結

從探索使用官方API,到自己動手去實現(xiàn)它,雖然過程比較曲折,但最終還是完成目標。經(jīng)過反復測試GitHubTrending
現(xiàn)在已經(jīng)滿足了GitHub Popular項目的需求,而且穩(wěn)定性還是不錯的,感興趣的小伙伴可以下載GitHub Popular
體驗一下。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容