日記

のみろぐ

主に競プロ日記です

初めてWebサイトを作ってみた

まえがき

  • どうやって作ったっけなー...って思い出すのだるいからどうやって作ったかとかを書いとく
  • 後で読み返して「懐かしいな~」みたいなことを言いたい
  • とりあえず動くことを目標にして作ったからいろいろヤバイかもしれない
  • サーバーサイドの知識ほとんど無くて間違ってること書いてるかもしれないので、指摘してもらえるとありがたいです

成果物

Future Contest

  • 複数の競プロサイトのコンテスト情報をまとめて表示してくれるサイト

開発に至った経緯

  • Twitterで「便利サイト作ってみた!!」みたいなのがたくさん流れてきて、僕もそういったWebサイトを作ってみたいと思った
  • AtCoder, Codeforces, CSA, yukicoderなど、普段使う競プロサイトのコンテスト予定を1つのサイトで見れるサイトが欲しかったので作ってみようかなーって感じだった
  • 僕がつくろうとしているサイトの上位互換みたいなサイトも見つけたが、なんか見づらかったので自分で作ることにした。
  • あと、いい感じの競プロカレンダーもあったけどカレンダーだと月ごとにしか見ることができない。僕は予定されたコンテスト一覧を1つのページで見ることができるサイトが欲しかった。

機能とか

処理の流れ

f:id:nomikura:20180925174031j:plain

  • なんか解像度が死んでる。パワポで図を作ったんだけどなんか使いづらくね?なんか良い感じのツールあったら教えて下さい~(draw.ioっていうツールが便利っぽい?)

言語

  • Go言語
  • 選んだ理由は、高速に動くから
  • あと、他の言語が気に入らなかったので

クラウド

  • GAEを使った
  • 理由は、GAEだとすぐにサイトが作れそうだったから(公式のサポートが手厚かった)

対象サイト

コンテスト情報の収集

使ったツールまとめ

  • Advanced REST client
    • GETとかPOSTを簡単に送れる
    • ヘッダとかもつけて送れる
    • これを知る前はGoからプログラムを実行していた。こんな便利なツールがあるとは...
    • ただ、ちょっと重い気がする(もっと軽いツールあったら使いたい)
  • JSON-to-Go
    • JSONを貼り付けるだけでGoの構造体にパースしてくれる
    • 構造体が分かると、GoからJSONを読み込むのが楽になる
  • goquery

情報取得方法まとめ

サイト 方法
Codeforces API
CS Academy コンテストページにて、リクエストヘッダにx-requested-with: XMLHttpRequestを追加してGETリクエストを送るとJSONが返ってくる
AtCoder コンテストページにて、リクエストヘッダにcookie: language=jaを追加してGETリクエストを送る。返ってきHTMLをスクレイピングする。
yukicoder API

Codeforces

  • APIが公開されているのでそれを使った
  • APIの説明によると、gym=falseの時に通常のコンテストを取得してgym=trueの時に通常でないコンテストを表示するっぽいことがわかる。欲しいのは通常のコンテストサイトなのでこのURLにある情報を取得することにした

CS Academy

  • 公式がAPIを公開してなかったのでスクレイピングしようとした。ただ、スクレイピングではJS実行前の情報を取得するらしかった。なので、JSで構成されてるコンテスト情報を取得できなかったらしい。
  • 調べた感じ、phantomjsというツールを使えばJS実行後のコンテスト情報が取得できることがわかった。でも、使い方がよく分からなかった(後で分かるんだけど)
  • よくわからなくなったので、先輩に頼った。
  • すると、リクエストヘッダにx-requested-with: XMLHttpRequestを追加すれば良いことがわかった。なぜ分かったのかは以下に書きます(先輩に聞いただけですが)
  • デベロッパーツールを開いてNetworkをクリックし、ページを更新すると以下の画像のようなページになる。そこにあるcontestっていう2つのファイルが怪しい。1つはDocでもう1つはXHRだった。XHRの方のレスポンスを見ると、JSON形式で返ってきていた。XHRの方のcontestsをクリックするとリクエストヘッダとかが書かれてる。そこから、Advanced REST clientを使ってどのリクエストヘッダを送ればJSONが返ってくるかを実験する。実験の結果、x-requested-with: XMLHttpRequestをリクエストヘッダに追加すればJSONが返ってくることがわかった。

f:id:nomikura:20180925174025p:plain

AtCoder

  • 開発者ツールのNetworkで確認した感じ、APIは使われていないっぽい(あんまり詳しくないけど、ResponseでJSONを返すものが無かったからAPIは使われていないと判断した)
  • なので、スクレイピングしてコンテスト情報を取得することにした
  • でも、このサイトもCSAと同様にJS実行前のサイトがスクレイピングされたらしく、コンテスト情報が上手く表示されない。
  • なので、phantomjsを使うことにした。phantomjsのダウンロードはこのサイトがわかりやすかった。
  • pahtomjsを使ってスクレイピングすることには成功したが、GAE上にダウンロードできなかった。PaaSだからサーバー側はあまりいじれないらしい。なので、Goファイルと同じディレクトリにphantomjsの実行ファイルを置いて、Goから実行することにした。それをやってみたところ、次はpahtomjsのPATHが通ってないと怒られた。PATHを通そうとしたが、PaaSだからか知らないけどなんかうまくできなかった。なのでpahtomjsを使うことは諦めた。
  • このタイミングで先輩がどうやってCSAからJSONを返す方法が分かったのか聞いたので、その方法を試した。リクエストヘッダを全部試してみた。結局はリクエストヘッダにcookie: language=jaを追加すれば良いことがわかった。cookieの情報はかなり多く探すのが大変だった(実はこの辺の作業でやらかしたんだけど、それはまた別の話ということで)
  • リクエストヘッダにそれを追加してGETリクエストを送るとHTMLが返ってくるそれを頑張ってスクレイピングすればコンテスト情報を取得できる。スクレイピングにはgoqueryを使った。go get github.com/PuerkitoBio/goqueryをしてGoでimportすれば使えるのですごい楽だなーと思った。

yukicoder

  • このサイトを開発した当初、公式のAPIはなかった。カレンダーから情報を抜き取ることにした。しかし、GoogleカレンダーAPIを使う方法がよく分からなかった。色々調べたんだけどね。なので、デベロッパーツールから色々たどりまくってGoogleカレンダーのyukicoderのJSONが載ってるサイトまで辿り着いた。
  • だだ、今はAPIが公開されているので一瞬でコンテストデータの取得方法がわかるという...
  • いつか修正する予定

サーバー

  • サーバーって言うよりクラウドって言う方が正しいのかな?
  • 今回はGCPが提供する内のGAEを使ったと思う。というか手順通りにやってみただけなので、正直自分が何を使ってるのかとかよく分かってない。
  • このサイトに言語ごとの始め方が載ってる。手順通りにやればできるのですごいありがたい。
  • 3万円分のクレジットが1年間無料で使えるらしいので使ってみた。
  • DBとかも使ってみたいなーと思ってる。

見た目

  • HTMLとCSSをゴリゴリ書いた。
  • 開発後に知ったんだけど、Bootstrapっていう見た目を良い感じにできる便利ツールがあるらしい。
  • いろいろ苦労したけど、このブログ書くのが開発した1ヶ月後とかだからあんま覚えてない。

favicon

  • <link rel="icon" href="favicon.ico">を書いても表示されなかった
  • どうやらfaviconはサーバー側にアップロードしないといけないみたい
  • 他のサイトを参考にすると、サイト名/img/favicon.icoみたいにすると良いことがわかった
  • Golangで画像をアップロードする方法はこのサイトに書いてあった。以下のように書けば良いみたい。ただ、拡張子がicoだと上手く動かなかった(ここで割と時間使った)。拡張子をpngにしたら上手く動いた。つまり、以下のように書けばfaviconが追加できる(HTML側もいじらなきゃだけど)
func faviconHandler(w http.ResponseWriter, r *http.Request) {
    http.ServeFile(w, r, "relative/path/to/favicon.png")
}

func main() {
  http.HandleFunc("/favicon.png", faviconHandler)
}
  • もしかしたらfavicon用のURLがなくても良かったのかもしれないけど試してないからわからない。
  • faviconKaitoさんに作って頂きました!ありがとうございます!!

その他

時間の表示がおかしい

f:id:nomikura:20180925174033p:plain

  • 7時間早く表示されてしまう。
  • どうやらUTCで表示してるらしい。日本時間で表示したい(ホントは見てる場所によって時間変えたいけどサーバーから処理できるか分からない)。
  • なので日本時間にして表示した。この記事を参考にしたら無事直った。

エラー処理

  • とりあえずアプリは動いたけど、エラー処理がガバガバ過ぎるという指摘を受けた。

  • かなりヤバイ状況で使うらしいpanic()をよく分からずに普通に使っていた

  • hoge, _ := piyo()みたいに、アンダーバーでエラー処理を省いていたりもした。
  • エラーが発生したらfmt.Println()で標準出力に出力したりもしてしまっていた。
  • 他にも、エラー処理はよく分からないので結構適当に書いた
  • よく分からないが、とりあえず全部のエラー処理をlog.Print("適当なメッセージ: %v", err)にした。
  • 適切なエラー処理は未だによくわからない

これから

見た目

  • ファビコン追加したい
  • 表の見た目を改善したい
    • 本当は各サイトのロゴを入れたいけど、ロゴ使用の許可取る必要がありそうで面倒
    • サイトごとに表の色変えると良いのかなーとか思ってる
  • Aboutページとかあるとよかったかも
  • リンクをクリックしたら別タブで開くようにする

処理

  • AtCoderスクレイピング、コードがクッソ汚いから修正したい(できればAtCoderAPI追加してもらいたいけど...)
  • DBを使いたい。DBをローカル環境に追加しないと動作テストができない気がしてる(ちょっとだるい)。あと、お金も追加でかかりそうでつらい。

その他

  • Goでコンテスト情報のAPI提供してフロントのJSからAPIを操作した方が色々できそう?今はHTMLを作って返しているだけなので静的になってしまっているので色々改善したい。
  • 今回はGAEが楽そうだからそれを使ったけど、GCPにはそれ以外にもいろんな機能があるみたい
  • 1年間3万円のクレジットを無料で使うことができるのですが、無料トライアルは残り 321 日で、¥17,422.58 のクレジットがありますとか表示されてしまっている。44日間で12578円使用していることになります。これは破滅の道を歩んでますねー。なので、設定を上手いこと書き換えて無料枠で収まるように工夫しようと思ってます。

さいごに

  • まあそんな感じでとりあえず動くサイトを作ることができました。
  • 記事中で気になることがあれば教えてほしいです。「もっと効率的な方法があるよ~」とか
  • 何も考えずに作るだけなら苦労しないが、修正とかが大変だなーと思った(精神的にも)。今回はとりあえず動くことを目標にしたこともあって、かなりコードが汚い。次に作るときは構成を考えてから書きたい。