千里之行
始于足下

绘枫和畅

千里之行,始于足下

Github的Api虽然很全面,但唯独没有提供Github Trending的Api。为了有个稳定的接口,只好自己动手了。


环境

  1. Node.js >= 7.6

  2. MongoDB >= 3.0


爬虫

这一部分没有什么难度,主要用到了两个模块:https 和 cheerio

首先用https将github trending的网页下载下来。

fetchPage() {
    const url = this.url;
    let request;
    if (this.protocol === 'http') {
      request = http;
    } else if (this.protocol === 'https') {
      request = https;
    }
    return (new Promise((resolve, reject) => {
      request.get(url, (res) => {
        let html = '';
        res.on('data', (data) => {
          html += data;
        });
        res.on('end', () => {
          resolve(html);
        }).on('error', (err) => {
          reject(err);
        });
      });
    }));
  }

用下载的网页初始化cheerio对象:

async getCheerio() {
  const html = await this.fetchPage();
  const $ = await cheerio.load(html);
  return $;
}

然后,就可以用cheerio对网页进行信息爬取了,当然在此之前少不了对网页结构进行分析。

v2-bbf73d91fc8503303595e9a3d604fad0_720x4096.jpg

这里可以爬:作者、仓库名、简介、语言、star数、fork数、贡献者、今日star数。

cheerio可以很容易完成这项操作,类似jQuery很容易上手。

async getData() {
    const $ = await this.getCheerio();
    const $repoList = $('.repo-list li');
    const data = {
      createDate: Date.parse(new Date()),
      repositories: [],
    };
    const spaceReg = /\s/ig;
    $repoList.each(function () {
      const $item = $(this);
      const repoText = $item.find('.d-inline-block h3 a').text();
      const contributors = $item.find('.f6.text-gray>span>a').attr('href');
      const $builtBy = $item.find('.f6.text-gray>span>a .avatar');
      const builtBy = [];
      $builtBy.each(function () {
        const $img = $(this);
        builtBy.push({
          avatar: $img.attr('src'),
          userName: $img.attr('title'),
        });
      });
      const starsTotalText = $item.find('.f6.text-gray.mt-2 a').eq(0).text().replace(/\s|,/ig, '');
      const forkTotalText = $item.find('.f6.text-gray.mt-2 a').eq(1).text().replace(/\s|,/ig, '');
      const starsToday = $item.find('.f6.text-gray.mt-2 .d-inline-block.float-sm-right').text().replace(/\s|stars|today|,/ig, '');
      let repoLanguageColor = $item.find('.f6.text-gray.mt-2 .repo-language-color').attr('style');
      if (repoLanguageColor) {
        repoLanguageColor = repoLanguageColor.replace(/^background-color|;$/ig, '');
      } else {
        repoLanguageColor = '';
      }
      data.repositories.push({
        owner: repoText.split(' / ')[0].replace(spaceReg, ''),
        repoName: repoText.split(' / ')[1].replace(spaceReg, ''),
        href: $item.find('.d-inline-block h3 a').attr('href'),
        description: $item.find('.py-1 p').text().replace(/^\s*|\s*$/ig, ''),
        programmingLanguage: $item.find('.f6.text-gray.mt-2 span[itemprop="programmingLanguage"]').text().replace(/\s|,/ig, '') || '',
        repoLanguageColor,
        starsTotal: starsTotalText ? parseInt(starsTotalText, 10) : 0,
        forkTotal: forkTotalText ? parseInt(forkTotalText, 10) : 0,
        starsToday: starsToday ? parseInt(starsToday, 10) : 0,
        contributors,
        builtBy,
      });
    });
    return data;
  }

好了信息爬下来了,但总不能每次都要手动爬吧,没关系,那就加个定时器,帮我们完成定时任务,这里用了node-schedule,设置好时间间隔,就可以自动爬取了。我设置的是每隔二十分钟,相对于Github Trending,这个速度不慢。

const githubTrending = async () => {
  const interval = spider.interval.githubTrending;
  const rule = new schedule.RecurrenceRule();
  const minute = [];
  for (let i = 0; i < 60; i += interval) {
    minute.push(i);
  }
  rule.minute = minute;
  schedule.scheduleJob(rule, async () => {
    const date = new Date();
    console.log(`${date}爬取github treding`);
    try {
      const gtSpider = new GithubTrendingSpider();
      const data = await gtSpider.getData();
      const gt = new GT(data);
      await gt.save();
      console.log(`${date}爬取github treding,成功!`);
    } catch (ex) {
      console.warn(ex);
      console.log(`${date}爬取github treding,失败!`);
    }
  });
};

服务

接口服务依旧选了好用的Koa,持久层选用了MongoDB。


结果

以下接口提供长期稳定的github trending。

https://baka.coolecho.net/api/github/trending

有了稳定接口,还可以用在微信上,不用每次打开网页了,例子:

WechatIMG72.jpeg

项目地址:IceEnd/node-github-trending-api





微信公众号

Master之令咒:

带我飞

©2016-2017 Powerd by Alchemy's Spruche 鄂ICP备14020745号