日付や時間を数値として計算する方法

給与計算や時間あたりのコスト計算などで、日付や時間などの型のものを数値として計算したいという場合の対処法です。

日付計算の時のよくあるミス。

HOUR()やMINUTE()といった関数を使って計算するというパターン。これ自体は問題ないと言えばないのですが、24時間を超える時間を計算する時に困ったことになります。

例えば、32時間30分00秒という時間を計算する場合。HOUR関数では以下のような結果になります。

spreadsheet1

そうなんです。32時間を24時間+8時間と計算し、8時間だけを返してくるのです。良さそうな関数を探したのですが、見つかりませんでした。

日付型を数値に変換して扱う

対処法は以外とシンプルで、日付型を数値に変換することで計算ができます。

使うのは、VALUE()関数。公式マニュアルには以下のように説明があります。

VALUE()
Google スプレッドシートで認識される日付、時刻、番号の書式の文字列を数値に変換します。

具体的には、以下のようにVALUE()関数に日付をキャストして数値に変換し、24時間表示にするために×24します。 spreadsheet3

分表示にするには、×24×60すればOKです。 spreadsheet4

補足:VALUE()関数とTO_PURE_NUMBER()関数

ちなみに、似たような関数でTO_PURE_NUMBER()という関数があり、ほとんどVALUE()と同じような説明がマニュアルには記載されています。

TO_PURE_NUMBER()
日付/時刻、百分率、通貨などの表示形式を持つ数値を表示形式なしの純粋な数値に変換します。

今回の日付型を計算する場合は、どちらでも使えると言えば使えます。

ただし、引数に文字列を渡した時の挙動が違うようです。引数に文字列「Time」を渡した場合の具体例は、以下のようになります。

spreadsheet2

VALUE()ではエラーに、TO_PURE_NUMBER()ではなぜか引数そのままの「Time」が表示されることに。

ということで、普通に使う場合はエラー表示もしてくれて、タイプ数も少なくなるVALUE()関数を使えば良いのかなと思います。

Scrapyをインストールしてバックグラウンドでスクレイピングするまで

1. Python2.7とpipのインストール

まずはPythonをソースからインストールします。

$ yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel
$ cd /usr/local/src
$ wget https://www.python.org/ftp/python/2.7.11/Python-2.7.11.tgz
$ tar xvfz Python-2.7.6.tgz
$ cd Python-2.7.6
$ ./configure --prefix=/usr/local
$ make
$ make altinstall

次にpipのインストールです。easy_installでpipを入れることもできますが、今では下記のコマンドで一発でpipを入れることができます。

$ curl -kL https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python

また、easy_installを使ってpipをインストールする方法は以下の通りです。easy_installを使うのにsetuptoolsではなくdistributeを利用しています。

$ wget http://pypi.python.org/packages/source/d/distribute/distribute-0.6.27.tar.gz
$ tar zxvf distribute-0.6.27.tar.gz
$ cd distribute-0.6.27
$ sudo python2.7 setup.py install
$ easy_install-2.7 pip

2. Scrapyのインストール

次にpipを使ってscrapyをインストールします。

$ pip install scrapy
    Compile failed: command 'gcc' failed with exit status 1
    creating tmp
    cc -I/usr/include/libxml2 -c /tmp/xmlXPathInitacjqEE.c -o tmp/xmlXPathInitacjqEE.o
    /tmp/xmlXPathInitacjqEE.c:1:26: error: libxml/xpath.h: そのようなファイルやディレクトリはありません
    *********************************************************************************
    Could not find function xmlCheckVersion in library libxml2. Is libxml2 installed?
    *********************************************************************************
    error: command 'gcc' failed with exit status 1

エラーですね。依存関係をクリアできなかったみたいです。libxml/xpath.hがないとのことなのでインストールします。

$ yum -y libxslt-devel

これで再度インストールすればうまくいきました。

$ pip install scrapy

3. Scrapyを使ってみる

Scrapyを使う簡単な流れは以下のようになります。

  1. Scrapy プロジェクトの作成
  2. 基本設定と抽出するアイテムの定義
  3. スクレイピング&クロール用のSpider作成
  4. 抽出したアイテムのパイプライン処理作成

3-1. Scrapy プロジェクトの作成

scrapyコマンドを使うことでプロジェクトを作成できます。プロジェクト名は英数字またはアンダースコアしか使えないので注意です。

$scrapy startproject sample_crawler

すると以下のようなディレクトリが作成されます。

sample_crawler/
├ sample_crawler
│ ├ __init__.py
│ ├ items.py
│ ├ pipelines.py
│ ├ settings.py
│ └ spiders
│    └ __init__.py
└── scrapy.cfg

ここで主に編集するのは、以下のファイルです。

items.py      データ保存のためのクラス
settings.py   基本的な設定
pipelines.py  抽出データの出力先を指定
spiders/      spiderのディレクト

3-2. 基本設定と抽出するアイテムの定義

settings.pyでクローラーの設定をすることができます。今回はサンプルなので、以下の部分のみ変更しました。

DOWNLOAD_DELAY=5
BOT_NAME = 'samplespider'

*settings.pyの詳しい設定内容はScrapyの公式ドキュメント: http://doc.scrapy.org/en/latest/topics/settings.html

また、クロールで抽出するアイテムをitem.pyで定義します。以下はURLとタイトルを取得する例です。

# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html
import scrapy

class CrawlerItem(scrapy.Item):
    # define the fields for your item here like:
    URL = scrapy.Field()
    title = scrapy.Field()
    pass

3-3. スクレイピング&クロール用のSpider作成

scrapyコマンドでspiderのテンプレートを作成します。

$ scrapy genspider -t crawl sample_spider example.com

すると、spiders/以下にsample_spider.pyというファイルが作られていると思います。以下は簡単なサンプルです。

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy.selector import Selector

class BaseSpider(CrawlSpider):
    name = 'sample_spider'
    # スクレイピングするドメインを指定します
    allowed_domains = ['example.com']

    # スクレイピングを開始するURL
    start_urls = ['http://example.com/']

    # ドメイン以下のURLに関して正規表現で指定します
    # /123など数字が連続しているURLはスクレイピングします
    allow_list = ['/\d+']
    # category, tag, pageなどが含まれるページはスクレイピングしません
    deny_list = ['category', 'tag', 'page']

    rules = (
        # スクレイピングするURLのルールを指定
        # parse_item関数がコールバック
        Rule(LinkExtractor( allow=allow_list, deny=deny_list ), callback='parse_item'),

        # spiderがたどるURLを指定
        # 今回はallowed_domainsの場合は全てたどります。
        Rule(LinkExtractor(), follow=True),
    )

    def parse_item(self, response):
        i = {}
        selector = Selector(response)
        i['URL'] = response.url
        i['title'] = selector.xpath('/html/head/title/text()').extract()
        return i

sitemap.xmlからスクレイピングをする場合は以下のようになります。


# -*- coding: utf-8 -*-
import scrapy
from scrapy.selector import Selector
from scrapy.spiders import SitemapSpider

class SitemapSpider(SitemapSpider):
    name = 'sitemap'
    allowed_domains = ['www.green-japan.com']
    sitemap_urls = ['https://www.green-japan.com/sitemap/companies.xml']

    sitemap_rules = [
        (r'/company/', 'parse_item'),
    ]

    def parse_item(self, response):
        i = {}
        selector = Selector(response)

        i['url'] = response.url
        i['title'] = selector.xpath('/html/head/title/text()').extract()
        return ii

3-4. スクレイピングの実行

以下のコマンドでスクレイピングを実行します。実行結果は、test.csvに出力されます。

$ scrapy crawl sample_spider -o test.csv

クロールをはじめると、以下のような表示がされます。

▼クロール除外時
2015-12-11 16:21:46 [scrapy] DEBUG: Filtered offsite request to 'www.not-target.com': 

▼クロール時
2015-12-11 16:21:49 [scrapy] DEBUG: Crawled (200)  (referer: http://sample.com/)

また、ターミナルを閉じてでもバックグラウンドで実行したい場合は、nohupコマンドで使います。以下はバックグラウンドで実行し、title.csvに実行結果を吐き出す例です。

$ nohup scrapy crawl sample_spidersimple -o titles.csv &
  [1] 11564
  nohup: ignoring input and appending output to `nohup.out'

実行結果は以下のコマンドで確認できます。

$ tail -f nohup.out

【参考】 Scrapy 1.0が公開されました ScrapyでWebサイトのタイトルとURLを再帰的に取得する scrapy を用いてデータを収集し、mongoDB に投入する

SassをMac(El Capitan)にインストールした時のエラー回避

「もうそろそろCSSをどうにかしたい…」と重い腰をあげSassを導入しようとした時に遭遇したエラーの内容と対処法をメモしておきます。

El CapitanのSassインストールエラー

gemをアップデートしておきます

$ sudo gem update --system

sassをインストール。

$ sudo gem install sass
  Fetching: sass-3.4.21.gem (100%)
  ERROR:  While executing gem ... (Errno::EPERM)
    Operation not permitted - /usr/bin/sass

「Operation not permitted」と出てインストールができませんでした。

エラーの原因

El Capitanから導入された「System Integrity Protector(通称SIP)」によって、/usr/binがプロテクトされているからだそう。

これをオフにするにはcsrutilコマンドを使えばいいそうですが、今回は試していません。

エラー解決策

インストール先を/usr/binから/usr/local/binに変更します。

$ sudo gem install -n /usr/local/bin sass
  Successfully installed sass-3.4.21

今度はインストールできました!

$ sass -v
  Sass 3.4.21 (Selective Steve)

これでSassのインストールが完了しました。

投稿時間と現在時間の差を表示する2つの方法

「◯分前の投稿」といった表示をWordPressでしたい時のメモです。

方法1.組み込み関数を使って表示する

echo human_time_diff( get_the_time("U"), current_time('timestamp') ) . __(' ago');

ほとんどのケースはこれで大丈夫です。

この場合、日本語だと例えば「1分前」と表示されて問題ないのですが、英語だと「1 day ago」と表示され「a day ago」と表示することができません。

「1 day ago」でも問題はないのですが、これが気になるという場合は、方法2の独自関数を作って対応するのが良いと思います。

方法2.独自関数で投稿時間と現在時間の差を表示する

本当は別に独自関数を作る必要はないのだけど、方法1.の関数を知らずに作ってしまったのでメモがてら残しておきます。

(本当は英語対応もhuman_time_diff()が1だったらaにするとかすればOKという話は置いておくとして)

注意点は、WordPressではPHPの組み込み関数のtime()が出力するのは常にUTCとなっているので、JSTと比べると9時間ずれてしまうというとこです。

これを回避するには、current_time()かdate_i18n()を使います。

function the_time_diff(){
    global $post;
    $seconds_ago = intval( current_time( 'timestamp' ) ) - intval( get_the_time( 'U', $post->ID ) );

    $years_ago = intval( floor( $seconds_ago / ( 3600 * 24 * 30 * 365 ) ) );
    $months_ago =  intval( floor( $seconds_ago / ( 3600 * 24 * 30 ) ) );
    $weeks_ago = intval( floor( $seconds_ago / ( 3600 * 24 * 7 ) ) );
    $dates_ago = intval( floor( $seconds_ago / ( 3600 * 24 ) ) );
    $hours_ago = intval( floor( ( $seconds_ago / 3600 ) % 24) );
    $minutes_ago = intval( floor( ( $seconds_ago / 60 ) % 60 ) );

    if ( $years_ago === 1 ) :
        _e('a year ago');
    elseif ( $years_ago > 1 ) :
        echo "{$years_ago}".__(' years ago');
    elseif ( $months_ago === 1 ) :
        _e('a month ago');
    elseif ( $months_ago > 1 ) :
        echo "{$months_ago}".__(' months ago');
    elseif ( $weeks_ago === 1 ) :
        _e('a week ago');
    elseif ( $weeks_ago > 1 ) :
        echo "{$weeks_ago}".__(' weeks ago');
    elseif ( $dates_ago === 1 ) :
        _e('a day ago');
    elseif ( $dates_ago > 1 ) :
        echo "{$dates_ago}".__(' days ago');
    elseif ( $hours_ago === 1 ) :
        _e('an hour ago');
    elseif ( $hours_ago > 1 ) :
        echo "{$hours_ago}".__(' hours ago');
    elseif ( $minutes_ago === 1 ) :
        _e('a minute ago');
    elseif ( $minutes_ago > 1 ) :
        echo "{$minutes_ago}".__(' minutes ago');
    elseif ( $seconds_ago === 1 ) :
        _e("a second ago");
    else :
        echo "{$seconds_ago}".__(' seconds ago');
    endif;
}

初心者でもできるイラストレーターを使った自作アイコンフォントの作り方

高速化のためにアイコンフォントを導入しようと思い、自作アイコンフォントを作ったのでその作業メモです。

必要なものは、イラストレーターだけです。

アイコンフォントのメリットとCSSスプライト&SVGとの比較

作業の前にアイコンフォントについてのざっくりまとめです。

アイコンフォントのメリット

・拡大しても劣化しない ・CSSで色を変更することができる ・ファイルサイズが小さいので速い

20個程度のアイコンフォントでもファイルサイズは5.5KB程度なのでダウンロードは速いです。さらに、ラスター画像ではないので拡大しても劣化しません。

なので、単色のアイコンはアイコンフォント化してしまう方が良さそうです。

CSSスプライトやSVGと比較すると?

多色のアイコンの場合はCSSスプライトかSVGが選択肢としてありそうですが、Internet Explorer8以下はSVG未対応なので悩ましいですね。

アイコンフォントはEOT形式であれば、IE6から対応しているのである程度は安心かと思います(試してないので断定できないですが)

アイコンフォント作成の手順(Mac・Win共通)

1.イラストレーターでアイコンを作成

パスはなるべく整理して、文字や枠線もアウトライン化することが必要です。簡単な図形なら以下の操作を知っていれば作れると思います。

【文字のアウトライン化】
文字を右クリック > アウトラインを作成
【枠線のアウトライン化】
オブジェクト > パス > パスのアウトライン
【パスを型抜き】
オブジェクト > 複合パスを作成
【図形のパスを合体】
パスファインダー > 合体
*パスファインダーはウィンドウから表示できます
【オープンパスを結合】
1. ダイレクト選択ツールで結合する2点のアンカーポイントを選択する
2. (選択状態のまま)オブジェクト > パス > 連結

2.SVGファイルで保存

ファイル > 別名で保存 > ファイル形式SVGsvg)を選び保存

3.FontelloまたはIcon Moonでウェブフォント作成

作成したSVGファイルを元にウェブフォントを作成します。

GlyphsやFontographerなどのフォント作成アプリがあるらしいのですが、今回はアイコンフォントを作るだけなのでFontello、Icon Moonなどのウェブアプリを使います。

複雑なパスのイラストだとウェブフォントにする時に崩れてしまうことがあるので、その時は再度イラストレータで調整してください。

TreeTaggerをインストールしてPython2.7で英語の形態素解析をしてみた

英語の形態素解析をするにあたり、TreeTaggerをいれてみました。

NLTK、Stanford NLPというのもあるそうなのですが、なんとなくTreeTaggerを選んでみました。

TreeTaggerをインストー

/usr/local/srcにインストールする場合の例です。 公式サイトから必要なものをダウンロードしてインストールします。

$ cd /usr/local/src
$ mkdir tree-tagger
$ cd tree-tagger/
# TreeTaggerのインストールに必要なものをダウンロード
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/tree-tagger-linux-3.2.tar.gz
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/tagger-scripts.tar.gz
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/english-par-linux-3.2-utf8.bin.gz
# インストール用シェルスクリプトをダウンロード
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/install-tagger.sh
# パーミッションを変更してシェルスクリプトを実行
$ chmod u+x install-tagger.sh
$ ./install-tagger.sh

必須ではないですが、.bashrcに追記をしてパスを通しておきます。

$ vi ~/.bashrc
    #以下の内容を追記
    PATH="$PATH":/usr/local/src/tree-tagger/cmd
    PATH="$PATH":/usr/local/src/tree-tagger/bin
$ bash

以下が実行できればOKです。

$ echo 'Hello world!' | tree-tagger-english 

PythonでTreeTaggerを使う方法

TreeTaggerには有志の人が作成したPythonラッパーがあるのでそれを利用します。

$ pip install treetaggerwrapper

pipでのインストールが成功したら、Pythonから使えることを確かめてみます。

$ echo "This is the sentence." | python -m treetaggerwrapper --pipe --debug

  ERROR Failed to find TreeTagger from automatic directories list.
  ERROR If you installed TreeTagger in a standard place, please contact the treetaggerwrapper author to add this place to this list.
  ERROR To continue working, setup TAGDIR env var to TreeTagger directory.
  ERROR Can't locate TreeTagger directory (and no TAGDIR specified).

TreeTaggerがインストールされているディレクトリが見つからなかったよとのことです。

TreeTaggerWrapperのドキュメントによると、TreeTageerオブジェクトに引数でTAGDIRを指定して渡せば良いそうなので下記のようなexample.pyを作成して実行してみます。

import treetaggerwrapper
tagger = treetaggerwrapper.TreeTagger(TAGLANG='en',TAGDIR='/usr/local/src/tree-tagger')
tags = tagger.TagText("This is the sentence.")
for tag in tags:
    print tag

スクリプトを実行。

$ python example.py

すると、以下のようなエラーが発生。Unicodeじゃないとダメだよってことらしいです。

treetaggerwrapper.TreeTaggerError: Must use *unicode* string as text to tag.

Python3だとデフォルトがUnicodeらしいのですが、Python2の場合はu"string"というように「u」をつけてあげる必要があります。

なので、先ほどのスクリプトを以下のように変更します。

tags = tagger.TagText(u"This is the sentence.")

そして、実行。

$ python example.py
  This      DT   this
  is        VBZ  be
  the       DT   the
  sentence  NN   sentence

うまく形態素解析ができました。

TreeTagger公式サイト

rsyncでpermission denied (publickey) となった時の対処法

rsyncでローカル環境とリモートサーバーのディレクトリを同期したい!

ということありますよね。そう思い、rsyncを使ってみたらつまずいた時の対処法です。

エラーの内容と原因

ローカル環境の現在のディレクトリと、リモートサーバーの/home/wwwを同期しよう思い、実行したのが以下のコマンド。

$ rsync --delete -avzc ./* www@11.22.33.444:/home/www
 Permission denied (publickey).
 rsync: connection unexpectedly closed (0 bytes received so far) [sender]
 rsync error: unexplained error (code 255)

見事にPermission deniedされてますね。

もちろん、~/.ssh/configには以下のように設定を追記済みで、ssh remoteで接続できることも確認済です。

Host remote
  HostName 11.22.33.444
  User www
  Port 22
  IdentityFile ~/.ssh/id_rsa

rsyncのエラー解決策

rsyncは、裏側でsshをデフォルトでは使っているそうです。そこで、rsyncのeオプションを使い秘密鍵を指定してssh接続をしてみたいと思います。

eオプションを使いダブルクオーテーションで囲むことでsshのiオプションを使うことができます。

-e "ssh -i /Users/sample_user/.ssh/id_rsa"

コマンド全体としては↓のような感じになります。

$rsync --delete -avzc -e "ssh -i /Users/sample_user/.ssh/id_rsa"  ./* www@11.22.33.444:/home/www

これで実行したところ…できた!

秘密鍵がちゃんと指定できなかったことが問題だったようです。rsyncはあまり使ったことがないので、割りと雑な対処ですがこれでなんとか同期ができました。