カテゴリー別アーカイブ: Program

最近ひっそりと公開した golang のパッケージ 2 つ

最近ひっそりと公開した golang のパッケージ 2 つ

疲れ果てて早い時間に寝落ちし変な時間に起きて暇なので, 最近ひっそりと公開した Go 言語のパッケージを 2 つ紹介だけしておこう思います。

go-webpage

https://github.com/yterajima/go-webpage

goquery の Web サイトのページ用エイリアスパッケージ。goquery は Ruby でいえば nokogiri のような動作をするパッケージで HTML から要素を取得できます。

“goquery そのまんま使えよ” という話ではあるんですが, meta タグのデータとったりするとコードが長くなりがちだったので別パッケージにしました。

雑な使い方

package main

import (
  "fmt"

  "github.com/PuerkitoBio/goquery",
  "github.com/yterajima/go-webpage"
)

func main() {
  // []byte を返す html ページを取得する処理
  page := getHtmlPage("http://example.com/hoge.html")

  // 初期化
  // この時点で wp.Title とか取得できる
  wp := webpage.New(page)

  // wp.Title から余計なスペースを除去したデータを返す
  fmt.Println(wp.GetTitle())
}

go-dtf

https://github.com/yterajima/go-dtf

go-dtf は W3C Date and Time Formats (通称: W3C-DTF) に該当する時刻文字列を time.Time に変換するだけのパッケージです。この W3C-DTF が少々曲者で某プロダクトの中でコードを一緒にしておきたくなかったので分離してパッケージにしました。

W3C-DTF は 6 パターンの文字列を日時定義としています。

  • YYYY ex: 2015
  • YYYY-MM ex: 2015-12
  • YYYY-MM-DD ex: 2015-12-10
  • YYYY-MM-DDThh:iiTZD ex: 2015-12-10T13:00+09:00
  • YYYY-MM-DDThh:ii:mmTZD ex: 2015-12-10T13:00:00+09:00
  • YYYY-MM-DDThh:ii:mm.sTZD ex: 2015-12-10T13:00:00.123456789+09:00

詳しくは 日付の表記に関するノート にとてもよくまとめられています。末尾の +09:00 はタイムゾーン指定で日本を表します。UTC の場合は +00:00 と書くか 2015-12-10T13:00Z のように末尾に “Z” 1文字で UTC を表します。

パッケージとしては文字列が W3C-DTF にマッチするか判定することと, それぞれの形式に応じて time.Time に変換しているだけですが golang のタイムゾーンのテストあたりは勉強になりました。

雑な使い方

package main

import (
  "fmt"

  "github.com/yterajima/go-dtf"
)

func main() {
  // エクスポートしている関数は沢山あるけれど
  // 単にパースするだけなら dtf.Parse だけ使えばできる
  parsedTime, _ := dtf.Parse("2015-12-10T02:50+09:00")
  fmt.Println(parsedTime)
}

最後に

golang 製のプロダクトを github の private リポジトリで勢いよく作ると分離したい汎用パッケージが増えて思わず github の契約プランを上げてしまう現象, もう少しどうにかならないだろうか。

sitemap.xml をよしなに取得する go-sitemap を公開しました

golang で開発・運用している Web クローラの設計を見直すに当たり, 分離できそうな機能を分割しパッケージにすることにしました。そのうちの 1 つとして sitemap.xml を取得し Sitemap 構造体を生成する部分を公開しました。

go-sitemap

どういう部分で使っているのか

某 Web クローラでは各 Web サイトの sitemap.xml から全ページの URL を取得, さらにそこからデータを加工, ページデータの取得といった処理を行っています。その sitemap.xml からのページ URL 取得処理部分を go-sitemap で実装しています。

sitemap.xml 取得の面倒なところ

ファイル名が sitemap.xml だったとしても中身を見るまで サイトマップインデックス なのか サイトマップ かわからない場合があります。

例えば, この blog の sitemap.xml の中身は <sitemapindex...>...</sitemapindex> で囲まれているサイトマップインデックスです。サイトマップインデックスは複数のサイトマップ(<urlset...>...</urlset>) への参照を持っています。1 つのデータとしてページ URL をまとめるにはすべてのサイトマップファイルをパースしまとめる必要があります。

go-sitemap ではサイトマップインデックス or サイトマップか自分で判定せずに呼び出し Sitemap 構造体として返します。

続きを読む

クローラをテストする場合の少し気を使ったテストサーバ

まだあまり細かい話は外に出せないですが, golang で Web クローラと API を使ったシステムを開発しています。

クローラを書くにあたり net/http/httptest を使ってこんなテスト用サーバを書いて対応しました。

package main

import (
  "fmt"
  "io/ioutil"
  "net/http"
  "net/http/httptest"
)

func testServer() *httptest.Server {
  server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // User-Agent が想定してないものの場合 400
    if r.Header.Get("User-Agent") != "Mybot" {
      http.Error(w, "User-Agent is invalud", http.StatusBadRequest)
    }

    // Basic 認証が設定されている場合照合し判定
    // 不正な場合 401 を返す
    if authName, authPass, authStatus := r.BasicAuth(); authStatus {
      if authName != "correct-username" || authPass != "correct-password" {
        http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
      }
    }

    // サイト root はテストしないので 404
    if r.RequestURI == "" {
      // index page is always not found
      http.NotFound(w, r)
    }

    // RequestURI と同名のファイルを testdata ディレクトリから取得
    // 存在しなければ 404
    res, err := ioutil.ReadFile("../testdata" + r.RequestURI)
    if err != nil {
      http.NotFound(w, r)
    }

    // 取得したコンテンツを返す
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, str)
  }))
  return server
}

続きを読む

gitoliteのリポジトリ管理ツールをgolangで書きなおしてみた

最近では github をメインにリポジトリ管理をしていますが, 特に公開する必要がないものはサーバに gitolite で Git サーバを用意して管理しています。2 年ほど前に gitolite 用のリポジトリを管理するコマンドを Ruby で適当に書いて使っていたのですが不便だったのと最近 golang を書いているので書きなおしてみました。

2 年前の記事: gitolite 管理のリポジトリを簡単に追加できるようにした

そもそもの仕組み

gitolite は git サーバのリポジトリ自体を git で管理しています。

例えば, 新たにリポジトリを追加する場合, conf/gitolite.conf に対して次の行を追加します。

repo    namespace/new-repository
        RW+  =  @develop

このまま commit し push すると git@example.jp:namespace/new-repository.git をリモートリポジトリとして利用できるようになります。作成したコマンドではこの gitolite.conf を管理するためのリポジトリの内容を確認, 変更 して利用します。

続きを読む

Lumenで簡単なAPI作ってみた

lumen-test

yterajima/lumen-test

LumenLaravel 系統の小さなフレームワークとして開発されているものらしい。routes.php の書き方を見る限りでは SilexSlim と同じようなものに見えます。

ちょうど Laravel5 を使った案件に対応しているタイミングだったので少し気になって動かしてみました。

続きを読む

golangでsitemap.xmlのページ情報を取得する

開発中のあるツールの処理を golang で書き直せないかと思ったので試しに書いてみました。golang は手元のちょっとしたツールを書いてみたりしていた程度だったのですが, なかなか便利でよいですね。

お作法的なものとかまだよく分かっていないのでもう少し書けるようになりたいです。

package main

import (
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

type Page struct {
    Loc     string `xml:"loc"`
    LastMod string `xml:"lastmod"`
}

type Sitemap struct {
    XMLName xml.Name `xml:"urlset"`
    Pages   []Page   `xml:"url"`
}

func main() {
    url := "http://www.e2esound.com/sitemap.xml"
    sitemap := GetSitemap(url)
    for _, page := range sitemap {
        fmt.Println(page.Loc, page.LastMod)
    }
}

func GetSitemap(url string) []Page {
    response, err := http.Get(url)
    checkErr(err)

    XMLdata, err := ioutil.ReadAll(response.Body)
    checkErr(err)

    var sitemap Sitemap
    xml.Unmarshal(XMLdata, &sitemap)
    return sitemap.Pages
}

func checkErr(err error) {
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

参考: Tutorial on how to read XML file in Go. – SocketLoop

縦棒からの開放 -Slimのsmart text-

前の記事 に続いて Middleman の話ではなく, テンプレートエンジン Slim 単体の話です。

実はインクルード以外にも Plugin が追加されています。スマートテキストです。Slim を使い時に, HTML の通常テキスト部分を表すには | を使わなければならないことがありますが, これを使わずに書けるようになったようです。

またまたとりあえず動かしてみる

前回と同じリポジトリにディレクトリを追加しています。

yterajima/slim-plugin-sample

前回と同じ手順で下準備します。まだ rubygems 版には反映されていないので直接 github のコードを bundler で持ってきてます。

$ git pull git@github.com:yterajima/slim-plugin-sample.git
$ bundle install --path vendor/bundle 
$ bundle exec slimrb -v 
  Slim 2.0.3

slimrb -v の結果が得られれば問題ありません。ファイルが用意してあるディレクトリに移動して, コマンドを実行します。

続きを読む

テンプレートエンジンSlim単体でファイル分割する

Middleman の話ではなく, テンプレートエンジン Slim 単体の話です。

Slim のリポジトリをチェックされている方なら既に当然のようにお気づきのことかと思いますが, 少し前に Slim に新しい Plugin が追加されました。その 1 つが外部ファイルのインクルード(ファイル分割)です。

とりあえず動かしてみる

説明が面倒なので, github にコードを用意してみました。
yterajima/slim-plugin-sample

git を使って pull, bundle install を実行する必要があります。

$ git pull git@github.com:yterajima/slim-plugin-sample.git
$ bundle install --path vendor/bundle 
$ bundle exec slimrb -v 
  Slim 2.0.3

slimrb -v の結果が得られれば問題ありません。ファイルが用意してあるディレクトリに移動して, コマンドを実行します。 続きを読む

Middlemanに続いてSlimのREADME.mdも本家にmergeされた

自分のリポジトリで1年以上日本語化をしていましたが, 手元にあるのも変な話だよなー、ということで pull-req したら merge してもらえました。

「普段はこういうのmergeしないんだけど、Youが面倒みてくれるっていうからmergeすんぜ!」みたいな話です。

Add Japanese README by yterajima · Pull Request #486 · slim-template/slim : https://github.com/slim-template/slim/pull/486

これでMiddlemanの日本語版もSlimのREADMEの日本語版も公式なリポジトリで管理されるようになりました。

Middleman+Slim+Sassあたりで静的サイトを作らない理由がまた1つ減った(ような。

ところで、このところのRubyを書いていない感がすごいので、もう少し時間を作りたいと思います。時間がなかったのは主に確定申告をしていたからです。

Rubymotion はじめました

motion

少し前から気になっており, 先日参加した Niigata.rb#3 でライブコーディングを見る機会があり購入しました。

以前 Rubymotion の書き方を見た時には, ほぼ Obj-C の形式で GC を気にしなくても平気, 程度のものでしたが話を聞いてみるといろいろと入れるとより Ruby らしく書けるとのことでした。

まずはチュートリアルを試しています。