14
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Qiitaの新着記事を見たい。なのでQiita APIを叩こう。

Posted at

はじめに

2017年12月にQiitaのデザインが変わりました。デザイン変更に伴い、いろいろとできることが増えたり、できないことがあったりと悲喜交交ではあるのですが、新デザイン 個人的には好きです。1点を除いては。

新デザインではQiitaに投稿される新着記事を読むことができない。

Qiitaで新しい技術やサービスの情報を拾っていたため、新サービス関連のネタを仕入れることができず困ってしまいました。QiitaではQiitaユーザーの一覧が取得できるので全ユーザー分のページをスクレイピングして、新着記事を得るしか無いかな?と思っていたところ、QiitaではAPIを公開しているので、APIを使って新着記事を取得する方法を考えてみました。

Qiita API v2

全貌はコチラのドキュメントを参照してください。

新しく記事を投稿、更新するにはユーザー認証などの一手間が必要になりますが取得系APIは認証いらずで使えるので、Getリクエストで情報が取得できます。とても便利。

/api/v2/items

投稿の一覧を作成日時の降順で返します。

http://qiita.com/api/v2/items?page=1&per_page=10

これで、新着記事がjson形式で10件取得できます。ね、簡単でしょう?

出力先は?

せっかくなので自分が使いやすく新着記事を並べていこうと思い、Go言語でMarkdown記法を使って一覧を作るプログラムを作ってみました。最新版はGistに公開しています。

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

type structTags struct {
	Name    string   `json:"name"`
	Version []string `json:"versions"`
}

type structUser struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}

type Post struct {
	Created_at     string       `json:"created_at"`
	Updated_at     string       `json:"updated_at"`
	Title          string       `json:"title"`
	Url            string       `json:"url"`
	Likes_count    int          `json:"likes_count"`
	Comments_count int          `json:"comments_count"`
	User           structUser   `json:"user"`
	Tags           []structTags `json:"tags"`
}

const (
	url_base  = "http://qiita.com/api/v2/items?per_page=10"
	path_base = "./github/"
)

func main() {

	jst := time.FixedZone("Asia/Tokyo", 9*60*60)

	beginTime := time.Now().Add(-2 * time.Hour).In(jst)
	beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), beginTime.Hour(), 0, 0, 0, jst)
	toTime := time.Now().Add(-1 * time.Hour).In(jst)
	toTime = time.Date(toTime.Year(), toTime.Month(), toTime.Day(), toTime.Hour(), 0, 0, 0, jst)

	log.Printf("%#v %#v \n", beginTime.Format("2006-01-02T15:04:05-07:00"), toTime.Format("2006-01-02T15:04:05-07:00"))
	textmd := fmt.Sprintf("# %s\n", beginTime.Format("2006-01-02 15:00~"))

	for page := 1; page < 3; page++ {
		url := fmt.Sprintf("%s&page=%d", url_base, page)
		resp, err := http.Get(url)
		if err != nil {
			log.Fatal(err.Error())
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err.Error())
		}
		resp.Body.Close()

		var postsTemp []Post
		err = json.Unmarshal(body, &postsTemp)
		if err != nil {
			log.Fatal(err)
		}

		for _, post := range postsTemp {

			t, _ := time.Parse("2006-01-02T15:04:05-07:00", post.Created_at)
			fmt.Println(beginTime, ", ", t, ", ", toTime)
			if t.After(beginTime) && t.Before(toTime) {
				log.Printf("%#v \n", post)

				textmd += fmt.Sprintf("## [%s](%s)\n", post.Title, post.Url)

				textmd += fmt.Sprintf("##### ")
				for _, tag := range post.Tags {
					textmd += fmt.Sprintf("[%s](https://qiita.com/tags/%s), ", tag.Name, tag.Name)
				}
				textmd += fmt.Sprintf("\n")

				textmd += fmt.Sprintf("###### update:%s\n", t.Format("2006-01-02 15:04"))
				textmd += fmt.Sprintf("---\n")
			}
		}
	}

	filename := path_base + beginTime.Format("2006-01-02") + ".md"
	oldfile, _ := ioutil.ReadFile(filename)
	ioutil.WriteFile(filename, []byte(textmd), 0666)

	addfile, _ := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND, 0666)
	defer addfile.Close()

	fmt.Fprintln(addfile, "\n\n\n")
	fmt.Fprintln(addfile, string(oldfile))
}

苦労した点

更新時間でのフィルタリング

使用していたサーバーがUTCで稼働していたため、日本時間に変換する必要がありました。また、1~2時間前の間に更新された記事を絞り込む条件判定が手こずりました。

    jst := time.FixedZone("Asia/Tokyo", 9*60*60)

    beginTime := time.Now().Add(-2 * time.Hour).In(jst)
    beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), beginTime.Hour(), 0, 0, 0, jst)
    toTime := time.Now().Add(-1 * time.Hour).In(jst)
    toTime = time.Date(toTime.Year(), toTime.Month(), toTime.Day(), toTime.Hour(), 0, 0, 0, jst)

    if t.After(beginTime) && t.Before(toTime) {
      //処理
    }

ファイルへ追記。ただしファイルの先頭に。

    filename := path_base + beginTime.Format("2006-01-02") + ".md"
    oldfile, _ := ioutil.ReadFile(filename)
    ioutil.WriteFile(filename, []byte(textmd), 0666)

    addfile, _ := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND, 0666)
    defer addfile.Close()

    fmt.Fprintln(addfile, "\n\n\n")
    fmt.Fprintln(addfile, string(oldfile))

シンプルに、下記の流れでファイルの先頭に追記してます。

  1. 既存ファイルを読み出す
  2. 先頭に追加したい内容をファイル出力(上書き)
  3. 同じファイルに、読み出しした内容をファイル出力(追記)

生成したファイルの公開先

新着が拾いやすい方法は何か……githubでいいんじゃない?ってことで、githubでQiitaの新着記事を公開しています。

###git
本当はgit add, commit, pushをプログラム内でやりたかったのですが、シェルスクリプトで書いたほうが手数が少なくて済んだので、コードとして実装していません。

#!/bin/sh
from_date=`date -d "${target_date} 2 hours ago" +"%Y-%m-%d %H:00:00"`
to_date=`date -d "${target_date} 1 hour ago" +"%Y-%m-%d %H:00:00"`

go run QiitaNews.go

git add *.md
git commit -m "'${from_date} -> ${to_date}'"
git push

他にもっといい方法があるかもしれませんが、シンプルです。

最後に(Qiitaの中の人へ)

Qiita のデザインが新しくなりました - Qiita Blog

これまでご提供していた、全タグの新着記事が表示される「すべて」フィードにつきましては現在再度ご用意すべく開発中です。ご不便おかけいたしますが少々おまちくださいませ。

期待して待ってます!

14
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?