您的位置:首页 > Web前端 > React

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

2016-10-26 20:10 525 查看
项目开源地址:GitHub PopularGitHubTrending



关于GitHub Trending API的困惑

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



拨开云雾见月明

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



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

冗余的数据太多,我们需要从这些冗余的数据中提取出
treding
模块真正需要的数据。

这些数据都是HTML格式的,而我们需要的是Json格式的数据。

GitHubTrending项目的开发

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

接受一个url参数,如:https://github.com/trending/

能够根据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,然后通过
fetch
API获取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

体验一下。

最后

既然来了,留下个喜欢再走吧,鼓励我继续创作(^_^)∠※

如果喜欢我的文章,那就关注我的博客@ devio.org吧,让我们一起做朋友~~

戳这里,加关注哦:

微博:第一时间获取推送

个人博客:干货文章都在这里哦

GitHub:我的开源项目
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: