orangain flavor

じっくりコトコト煮込んだみかん2。知らないことを知りたい。

Scrapy 1.0が公開されました

Pythonの有名なWebスクレイピングフレームワークScrapyがバージョン1.0になりました。*1

f:id:mi_kattun:20150621115515p:plain

0.24からの主要な変更点は下記のとおりです。

  • SpiderでItemの代わりにdictを返せるようになった
  • Spiderごとにsettingsを設定できるようになった
  • Twistedのloggingの代わりにPythonのloggingを使うようになった
  • CrawlerのコアAPIリファクタリングされた
  • いくつかのモジュール配置場所が変更された

他にも数多くの変更点がリリースノートに記載されています。

Scrapy 1.0の感想

大きな機能の追加よりも、APIの整理と安定性の向上がメインのようです。これまではバージョンを重ねるごとに便利になっていくものの、あまりAPIが安定していない印象でしたが、APIを安定させた区切りのリリースと言えるでしょう。1.0というメジャーバージョンに到達したことで、安心して使えるようになったと思います。

Itemの代わりにdictが使えるようになったのはありがたいです。Itemの存在意義は正直謎でしたので。 個人的に気になっていた、単一の要素を取得したい場合でもlistを返すextract()しかなくて使いにくい問題も、extract_first()メソッドが追加されたことで解決したので嬉しいです。

1.0では依然としてPython 2.7のみの対応ですが、1.1のマイルストーンにはPython 3のサポートが含まれています。実際、Google Summer of Code 2015のテーマとして採択されており、この夏の成果が楽しみです。

それでは早速Scrapy 1.0を使ってクローラーを書いてみましょう。

例1:1ファイルのシンプルなクローラー

Scrapyは大規模なクローリングを得意とするフレームワークですが、1ファイルから構成されるシンプルなクローラーも書けます。

ScrapyのWebサイトのトップに表示されている、Scrapinghubのブログをクロールするクローラーを動かしてみます。ちなみにScrapinghubはScrapyの開発者が立ちあげた会社です。

表示されているコードをそのままターミナルに貼り付けて実行してもよいのですが、ちょっと手を加えて解説も加えておきます。

1. Scrapyをインストールする

Python 2.7が必要です。 --upgradeをつけることでインストール済みの場合はアップグレードします。

$ pip install --upgrade scrapy

2. service_identityもインストールしておく

service_identityはMITMを防ぐモジュールであり、これがインストールされていないと警告が出ます。

$ pip install service_identity

3. Spiderを作成する

myspider.pyという名前で以下の内容のファイルを作成します。 日本語のコメントを付けたので、1行目にエンコーディングを指定しています。ファイルはUTF-8で保存してください。

# coding: utf-8
import scrapy


# scrapy.Spiderを継承してBlogSpiderを定義する
class BlogSpider(scrapy.Spider):
    name = 'blogspider'
    start_urls = ['http://blog.scrapinghub.com']

    def parse(self, response):
        # トップページをパースするメソッド。
        # URLに /yyyy/mm/ を含むアーカイブページへのリンクを抽出してクロールする。
        # それらのページはparse_titles()メソッドでパースする。
        for url in response.css('ul li a::attr("href")').re(r'.*/\d\d\d\d/\d\d/$'):
            yield scrapy.Request(response.urljoin(url), self.parse_titles)

    def parse_titles(self, response):
        # アーカイブページからエントリーのタイトルを取得する
        for post_title in response.css('div.entries > ul > li a::text').extract():
            yield {'title': post_title}

4. クローラーを実行する

以下のコマンドで実行できます。

$ scrapy runspider myspider.py
2015-06-20 22:14:05 [scrapy] INFO: Scrapy 1.0.0 started (bot: scrapybot)
2015-06-20 22:14:05 [scrapy] INFO: Optional features available: ssl, http11
2015-06-20 22:14:05 [scrapy] INFO: Overridden settings: {}
2015-06-20 22:14:05 [scrapy] INFO: Enabled extensions: CloseSpider, TelnetConsole, LogStats, CoreStats, SpiderState
2015-06-20 22:14:05 [scrapy] INFO: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats
2015-06-20 22:14:05 [scrapy] INFO: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware
2015-06-20 22:14:05 [scrapy] INFO: Enabled item pipelines:
2015-06-20 22:14:05 [scrapy] INFO: Spider opened
2015-06-20 22:14:05 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2015-06-20 22:14:05 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2015-06-20 22:14:06 [scrapy] DEBUG: Crawled (200) <GET http://blog.scrapinghub.com> (referer: None)
2015-06-20 22:14:07 [scrapy] DEBUG: Crawled (200) <GET http://blog.scrapinghub.com/2012/07/> (referer: http://blog.scrapinghub.com)
2015-06-20 22:14:07 [scrapy] DEBUG: Crawled (200) <GET http://blog.scrapinghub.com/2011/11/> (referer: http://blog.scrapinghub.com)
...

実行するとアーカイブページを30ページほどクロールして、タイトルを取得できます。

-o titles.jl という引数をつけると、取得したタイトルがtitles.jlという名前のファイルにJSONlines形式で書き込まれます。

複雑なクローラーの作成

続いて、1年半前の記事と同様にBBCとCNET Newsを対象として、もう少し複雑なクローラーを書いてみます。

以前の記事に書いたクローラーは、Webサイト側の変更によって既に2つとも動かなくなっています。Webスクレイピングの無常さを感じますが、めげずに書き直します。 当時のバージョンは0.20.2でしたが、1.0の機能を使うとよりシンプルに書けます。

スクレイピングした後にデータベースに保存するなどの処理を行ったり、設定を共有した複数のSpiderを動かしたりするなど、1ファイルでは収まらないクローラーを作成するときには、プロジェクトを作るのがScrapyの流儀です。

以下のコマンドで、helloscrapyという名前のプロジェクトを作成します。

$ scrapy startproject helloscrapy

以下のファイルが生成されます。

$ tree helloscrapy
helloscrapy
├── helloscrapy
│   ├── __init__.py
│   ├── items.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       └── __init__.py
└── scrapy.cfg

プロジェクトのディレクトリにcdしておきます。

$ cd helloscrapy/helloscrapy

以降では、このディレクトリ(settings.pyが存在するディレクトリ)を基準とします。

とりあえず、settings.pyに以下の設定を追加しておきます。Webページのダウンロード間隔として3秒空け、Webサイトのrobots.txtに従うようになります。

DOWNLOAD_DELAY = 3
ROBOTSTXT_OBEY = True

例2:XML Sitemapを持つサイトのクローリング

XML Sitemapを持つWebサイトをクロールするにはSitemapSpiderが便利です。

例として、CNET Newsを取り上げます。以前の記事ではXML Sitemapを持たないサイトとして紹介しましたが、リニューアルしてXML Sitemapが提供されていました。

spiders/cnet.pyを以下の内容で作成します。scrapy genspider cnet www.cnet.comを実行するとSpiderの雛形が生成されるので、これを変更しても構いません。

# coding: utf-8
from datetime import datetime

import scrapy


# SitemapSpiderを継承する
class CNETSpider(scrapy.spiders.SitemapSpider):
    name = "cnet"
    allowed_domains = ["www.cnet.com"]
    sitemap_urls = (
        # ここにはrobots.txtのURLを指定してもよいが、
        # 無関係なサイトマップが多くあるので、今回はサイトマップのURLを直接指定する。
        'http://www.cnet.com/sitemaps/news.xml',
    )
    sitemap_rules = (
        # 正規表現 '/news/' にマッチするページをparse_news()メソッドでパースする
        (r'/news/''parse_news'),
    )

    def parse_news(self, response):
        yield {
            # h1要素の文字列を取得する
            'title': response.css('h1::text').extract_first(),
            # div[itemprop="articleBody"]の直下のp要素以下にある全要素から文字列を取得して結合する
            'body'''.join(response.css('div[itemprop="articleBody"] > p ::text').extract()),
            # time[itemprop="datePublished"]のclass属性にUTCの時刻が格納されているので、パースする
            'time': datetime.strptime(
                response.css('time[itemprop="datePublished"]::attr(class)').extract_first(),
                '%Y-%m-%d %H:%M:%S'
            ),
        }

以下のコマンドでクローラーを実行します。しばらくするとitems-cnet.jlスクレイピング結果が出力されていきます。

$ scrapy crawl cnet -o items-cnet.jl

例3:XML Sitemapを持たないサイトのクローリング

XML Sitemapを持たないサイトのクローリングにはCrawlSpiderが便利です。

例として、BBCを取り上げます。以前の記事ではXML Sitemapを持つサイトとして紹介しましたが、ニュース用のXML Sitemapが404になっていて使用できなかったので、XML Sitemapを使わずにクロールします。

spiders/bbc.pyを以下の内容で作成します。scrapy genspider -t crawl bbc www.bbc.comを実行するとCrawlSpiderを使ったSpiderの雛形が生成されるので、これを変更しても構いません。

# coding: utf-8
from datetime import datetime

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


# CrawlSpiderを継承する
class BBCSpider(CrawlSpider):
    name = "bbc"
    allowed_domains = ["www.bbc.com"]
    start_urls = (
        'http://www.bbc.com/news',
    )
    rules = (
        # /news/world/*** というカテゴリページを辿る
        Rule(LinkExtractor(allow=r'/news/world/'), follow=True),
        # /news/world-*** というニュースページはparse_news()メソッドでパースする
        Rule(LinkExtractor(allow=r'/news/world-'), callback='parse_news'),
    )

    def parse_news(self, response):
        yield {
            # h1要素の文字列を取得する
            'title': response.css('h1::text').extract_first(),
            # .story-body__innerの直下のp要素文字列を取得して改行で結合する
            'body''\n'.join(response.css('.story-body__inner > p::text').extract()),
            # .story-body .dateのdata-seconds属性にタイムスタンプが格納されているので時刻に変換する
            'time': datetime.fromtimestamp(int(
                response.css('.story-body .date::attr("data-seconds")').extract_first())),
        }

以下のコマンドでクローラーを実行します。しばらくするとitems-bbc.jlスクレイピング結果が出力されていきます。たまにパースに失敗するページもありますが、無視してください。

$ scrapy crawl cnet -o items-bbc.jl

まとめ

Scrapy 1.0になり、すっきりと書けるようになったことがわかるかと思います。 使いやすくなり安定したScrapyでクローリングしていきましょう。 今後のPython 3対応も楽しみです。

*1:2015-06-21 13:00時点で公式サイトは0.24の表記のままですが、メーリングリストでアナウンスがありました。

LinuxディストリビューションにおけるPython 3デフォルト化の流れ

2015年6月2日修正:henrichさんのコメントを受け、Debianの記述を修正しました。

最近のLinuxディストリビューションにおいてPython 3がデフォルトになってきているという話をチラホラ聞くので、状況を調べてみました。

結論

PEP 394

PEP 394では、Arch Linuxが(アグレッシブにも)Python 3をデフォルトにしたことを受け、Unix-likeなディストリビューション向けに推奨されるpythonコマンドの振る舞いが記述されています。

PEP 394 -- The "python" Command on Unix-Like Systems

Recommendationを簡単に訳してみました。

推奨事項

  • Unix-likeなディストリビューションPython 2をpython2コマンドとして、Python 3をpython3コマンドとしてPATHにインストールするべきである。
  • python2コマンドを実行するとPython 2が起動し、python3コマンドを実行するとPython 3が起動するべきである。
  • Python 2がインストールされている場合は、pythonコマンドをインストールし、pythonコマンドが実行された場合はpython2コマンドが実行されたときと同じバージョンのPythonが起動すべきである。(ただしPython 3が起動する場合もある)
  • Python 2.xのidle, pydoc, python-configなどのコマンドも同様に、idle2, pydoc2, python2-configとして実行でき、バージョンがついていないコマンドが実行された時は、これらのバージョンを実行すべきである。(ただしシステム管理者の設定によってPython 3.xのものが起動してもよい)
  • プラットフォームごとの差異に対応するため、Pythonインタプリタを起動する必要のあるコードではpythonを指定すべきではなく、代わりにpython2python3(またはさらに特定のpython2.xpython3.x)を指定すべきである。Shebangに書くときも同様である。
  • 一点例外として、Python 2.xとPython 3.xの両方で動くスクリプトではpythonを指定したりShebangに書いても良い。
  • Pythonスクリプトから実行する場合は、sys.executableを使うのが望ましい。

Arch Linux

Arch Linuxはローリング・リリースを採用しており、明確なバージョンはありません。 2010-10-18に/usr/bin/pythonPython 3を指すようになったとアナウンスされています。 pythonパッケージをインストールすると、Python 2ではなくPython 3が使えます。

Arch Linux - News: Python is now Python 3

参考:

実際に使ってみる

というわけで使ってみました。basebase-develグループをインストールしただけではPythonはインストールされなかったので、pythonパッケージをインストールしました。

pythonコマンドで確かにPython 3が起動しました。

[root@archiso /]# python -V
Python 3.4.3
[root@archiso /]# which python
/usr/bin/python
[root@archiso /]# ls /usr/bin/python* -l
lrwxrwxrwx 1 root root     7 Mar 25 17:30 /usr/bin/python -> python3
lrwxrwxrwx 1 root root    14 Mar 25 17:30 /usr/bin/python-config -> python3-config
lrwxrwxrwx 1 root root     9 Mar 25 17:30 /usr/bin/python3 -> python3.4
lrwxrwxrwx 1 root root    16 Mar 25 17:30 /usr/bin/python3-config -> python3.4-config
-rwxr-xr-x 2 root root 10440 Mar 25 17:30 /usr/bin/python3.4
lrwxrwxrwx 1 root root    17 Mar 25 17:30 /usr/bin/python3.4-config -> python3.4m-config
-rwxr-xr-x 2 root root 10440 Mar 25 17:30 /usr/bin/python3.4m
-rwxr-xr-x 1 root root  3107 Mar 25 17:30 /usr/bin/python3.4m-config

なお、python2パッケージをインストールすれば、Python 2を使うことも可能です。

Fedora

次期メジャーリリースのFedora 23でPython 3をデフォルトにすることが提案されています。

Changes/Python 3 as Default - FedoraProject

ここでのデフォルトとは、以下のことを意味します。

  • Python 2のみに対応していたYumに替わってDNFがデフォルトパッケージマネージャになる。(明記されていないがFedora 22で実施済みのはず)
  • Python 3がminimal buildrootにおける唯一のPython実装になる。(Fedora 22で実施済み)
  • Python 3がWorkstation LiveDVDにおける唯一のPython実装になる。
  • Python 3がminimal cloud imageにおける唯一のPython実装になる。
  • Python 3がAtomic hostにおける唯一のPython実装になる。
  • Server LiveDVDでもPython 3が唯一のPython実装になると良いが、可能性は低い。

以下の点は議論されているようです。

参考:

ちなみに最近のFedoraのリリース日*1は以下の感じです。

Ubuntu

Ubuntu 16.04でPython 3をデフォルトにすることが提案されています。

Python/3 - Ubuntu Wiki

ここでのデフォルトとは、以下の意味です。

  • Python 3がデフォルトでインストールされる唯一のPython実装になる。
  • Python 3がインストールメディア(ISOなど)に含まれる唯一のPython実装になる。
  • Ubuntu touchのイメージではPython 3のみが使用できる。
  • Python 3をサポートするアップストリームのすべてのライブラリは、Python 3のバージョンがアーカイブに含まれる。
  • Python 3上で実行できるすべてのアプリケーションはデフォルトでPython 3を使う。
  • アーカイブに含まれるすべてのシステムスクリプトPython 3を使う。

一方で、デフォルトが意味しないこととして、以下の点が挙げられています。

  • /usr/bin/pythonPython 3を指すことはない。
  • Python 2がアーカイブから削除されることはない。

参考:

ちなみに最近のUbuntuのリリース日*2は以下の感じです。

  • 14.04 LTS (Trusty Tahr): 2014-04-17
  • 14.10 (Utopic Unicorn): 2014-10-23
  • 15.04 (Vivid Vervet): 2015-04-23
  • 15.10 (Wily Werewolf): 2015-10(予定)
  • 16.04 LTS: 2016-04(予定)

Debian

2015年4月8〜14日に開催されたPyCon 2015において、DebianのPythonメンテナ陣によるミーティングがあり、その後Python 3への移行がアナウンスされました。

移行の具体的な日付やタイミングは見つかりませんでしたが、2020年にPython 2のメンテナンスが終了することもあり、次期メジャーリリースのDebian 9 (Stretch) またはDebian 10 (Buster) でPython 3をデフォルトにするべく取り組みが始まっているようです。

参考:

ちなみに最近のDebianのリリース日*3は以下の感じです。

  • Debian 6.0 (Squeeze): 2011-02-06
  • Debian 7 (Wheezy): 2013-05-04
  • Debian 8 (Jessie): 2015-04-25
  • Debian 9 (Stretch): 2018 2017?
  • Debian 10 (Buster): 2021 2019?

結論

最初に書いたものと同じです。

Python 3を明示的にインストールしなくても普通に使える環境が広がると嬉しいですね。 ディストリビューションのコミッターには本当に頭が下がります。

内容の正確さにあまり自信がないので、間違っていたりもっと良い情報がありましたらお知らせください。 他のディストリビューションの情報も歓迎です。

Joel on Softwareを読んだ

MicrosoftのJoel氏が書いたエッセイ集です。 自分の中で感じていてもうまく言語化できなかったことが明確に言語化されていて素晴らしかったです。

Joel on Software

Joel on Software

特に印象的だったのは以下の2つの章です。

第12章 5つの世界

5つの世界とは以下の5つを指し、それぞれの世界でそれぞれの苦労があるという話です。

  1. パッケージ
  2. インターナル
  3. 組み込み
  4. ゲーム
  5. 使い捨て

インターナルソフトウェア(社内システム)とパッケージソフトウェア(オープンソース、Webベース、コンサルティングウェア)の違いが非常に頷けました。

実際、インターナルソフトウェアとパッケージソフトウェアの主要な違いの1つは、インターナルソフトウェアでは、ある時点以降、それをより頑健にしたり、使いやすくしたりするのにコストをかけることへのリターンは急速に少なくなる一方、パッケージソフトの方は、最後の1%の安定性や使いやすさが競争優位の鍵となり得る点だ。

第18章 二文化主義

UnixWindowsの違いの話です。自分はUnixの方が便利だと思って常用していますが、自分が使いやすいものと一般的に受け入れられているものとのズレが的確に表現されていました。

Unixは他のプログラマにとって有用なコードに価値を置く文化であり、一方Windowsプログラマでない人たちにとって有用なコードに価値を置く文化である。

その他

これら2つの章以外にも、商用パッケージソフトウェアの開発に携わってきた作者のOSSアジャイル開発に対する懐疑的な見方も新鮮でした。

UNIXの考え方ハッカーと画家を読んだときは頷いてばかりでしたが、本書を読んだときは、そういう考え方もあるのかと視野が広がる思いでした。

ハッカーと画家を読んだ

Y Combinator創業者のポール・グレアム氏が書いたエッセイ集です。

本書も読み進めていく中で頷いてばかりでしたが、特に表題作の「ハッカーと画家」が印象的でした。

「計算機科学」という言葉のしっくりこなさが明文化されていて、自分だけじゃなかったのだと救われた気がします。

私は「計算機科学」という用語がどうにも好きになれない。一番大きな理由は、そもそもそんなものは存在しないからだ。計算機科学とは、ほとんど関連のない分野が歴史的な偶然からいっしょくたに袋に放り込まれたもので、言ってみればユーゴスラビアみたいなものだ。

また、「昼間の仕事(day job)」という概念が明文化されていて、そういう割り切り方もありなんだと気付かされました。

ソフトウェアに関してのこの問題への解答は、 実は他のもの創りの人々には既に知られている。昼間の仕事(day job)というやつだ。 この言葉はミュージシャンの間で発生した。彼らは夜に演奏するからだ。 より一般的に言えば、生活費のためにひとつの仕事を、愛のためにもうひとつの仕事をするということだ。

これまで自分が目指していく先にハッカーがあるとは思ってなかったのですが、本書を読んで自分が目指す先はハッカーなのかもしれないと思うようになりました。

そしてやはりLispを使いたくなるのでした。

UNIXという考え方を読んだ

もともとUNIXの考え方は理解しているつもりでしたが、ちゃんと言語化されているものを読んだことがなかったので読んでみました。新しい発見があった箇所や非常に頷ける箇所をピックアップします。

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

定理3:できるだけ早く試作を作成する

あらゆる試作の目的は、「第三のシステム」に早く到達することだ。

  • 第一のシステム:数人で作られ、尖っていて小さく軽い
  • 第二のシステム:委員会方式で作られ、多機能で大きくて重い
  • 第三のシステム:専門家に作られ、コンセプトは常識となり、正しく作られる

早く第三のシステムに到達するために、素早く試作して素早く失敗することが大事という話。

定理4:効率より移植性

シェルスクリプトの持つ思わぬ利点は、C言語で書かれたプログラムよりずっと移植性が高いことだ。

効率が良くても移植性のないソフトウェアの価値は、新しいハードウェアが出た途端に大暴落するので、シェルスクリプトを書くべきという話。

定理6:ソフトウェアの梃子(てこ)を有効に活用する

1時間働いたら、5時間分、100時間分、いや1000時間分の結果を生み出すようにしなさい
既存のアプリケーションをゼロから設計し直すことは模倣ではあっても創造とは言わない

独自技術症候群(Not Invented Here Syndrome)を避け、他人の成果を利用し、他人に成果を利用されるのを認めることで個人の力を増幅できるという話。

定理8:過度の対話的インタフェースを避ける

UNIXは、人間とマシンのどちらがボトルネックなのかを分かっている
最も重要なことに、拘束的ユーザーインタフェースはソフトウェアの梃子の効果を利用できない

 対話的インタフェースを使うプログラムは、大きく醜くく他のコマンドと連携しづらいものになるので、避けたほうが良いという話。

定理9:全てのプログラムをフィルタにする

入出力をハードワイアするという裏には、プログラムの使い道が完全に分かっているという思い上がりがある。
ユーザーがソフトウェアをどう使うかは、決して予測できない。設計者である自分の意図に全員が従ってくれるはずだ、などとは絶対に考えてはならない。

プログラムをフィルタとして使えるようにしておくことで、開発者の想定を超える使い方にも対応できるという話。

まとめ

全体的に頷けるところが非常に多い書籍でした。書かれた年代が古いため、事例はピンと来ないものも多いですが、それほど気にならずに読めました。

プログラミングしながら運動できるエアロバイク FitDesk X-2.0を買った

2016-01-16追記: 購入した当初は高いと思っていたサドルですが、座り方で多少改善されるようです。身長160cmの妻でも漕げています。

平日の家に帰ってご飯を食べてから寝るまでの時間はコードを書いていることが多いのですが、まったく運動しないことが気にかかっていました。 運動するためにジムに行くのは面倒だし、コードを書く時間を減らしたくはないので、運動しながらコードを書ける方法がないか考えていました。

そんなときに以下の記事を見て、運動しないとなぁと思っていると、「サイクリングデスク」という言葉が目に入りました。

「The Healthy Programmer」を読んで自宅を快適なプログラミング環境にする - 八発白中

 オフィスでは1種類以上のイスを使うこと -- 普通のオフィスチェア、バランスボール、可動式のイス、さらに言えばサイクリングデスク

サイクリングデスクってなんだ?と思ってググって先頭に出てきたもの*1は家庭用ではなかったですが、要するにエアロバイクにPCを置ける机がついていれば良いことに気付きました。

というわけでFitDesk X-2.0という製品を買いました。

以下のページのように普通のエアロバイクに机を足すのでも良いかもしれませんが、安定性やスペースを考えると、多少高くても最初から一体となっている製品を買うのが良いと判断しました。

組み立て

組み立てには大人の男性1人で2時間弱かかりました。 必要な工具は一緒に入っているので問題ありません。

組み立てマニュアルは中国の方が英語から日本語に訳したようで、若干元の英語を想像しながら読解する必要がありますが、なんとかなります。

地味に大変だったのはサドルに棒をくっつけるところです。角度が急な箇所のナットを閉める必要があり、ちょっとずつしか進まないため時間がかかりました。

サイズ的には、アメリカンサイズでかなり大きめです。私は身長170cmですが、サドルを一番下げて、机を一番手前に引いてちょうどいいぐらいです。 小柄な人は注意したほうがいいかもしれません。

また、Amazonのページには以下のように書かれていますが、この横と長さは床に接する部分のサイズです。

サイズ 横41cmx長さ71cmx高さ114cm

実際に組み立てると机の部分が本体より前にせり出すため、空間としては横53cmx長さ110cmぐらい必要です。

 サポート

いざ組み立ててしばらく漕いでみるとカタカタと異音がしました。 代理店のWebサイトからサポートに問い合わせてみると、迅速に本体を交換して頂けました。

Amazon.comでは$300以下で販売されている一方、日本では5万円を超えているのも気になっていましたが、サポートを考えると日本の代理店で買って良かったです。

感想

漕ぎながら作業できるのかは若干疑問もありましたが、慣れれば集中して作業できます。この記事も半分ぐらいは漕ぎながら書きました。 逆に疲れたことに気づかずに漕ぎ続けてしまう危険もあると思っていて、最初のうちは意識的に30分ぐらいで辞めるよう気をつけています。

何にせよ、日常生活に運動を取り入れられるのは嬉しいです。 エアロバイクは低めの負荷でスルスルと漕ぐのが良いらしいので、10段階中の下から3段目で漕いでいます。

肝心の机の使い心地は上々です。流石にエアロバイクと一体になっているだけあって安定しています。アームレストがついているので腕がつかれることもありません。机にはMacbook Pro 13 inch がちょうど良く収まります。

f:id:mi_kattun:20150222224712j:plain

ノートPCを置くとメーターは見えなくなるので全く使ってません。アームバンドも置いてあるだけです。机の下の引き出しも使ってないですが、滑りが悪く、使いやすくはなさそうです。

音はほとんど気になりませんが、床を保護するために以前Kinect用に買ったマットを敷いています。

ALINCO(アルインコ)  エクササイズ フロアマット 厚さ 9mm EXP150

ALINCO(アルインコ) エクササイズ フロアマット 厚さ 9mm EXP150

Re:VIEWとDockerとCircleCIで原稿を継続的インテグレーション

Re:VIEW で執筆する原稿を継続的インテグレーションしたかったので、以下の図のような仕組みを作りました。

f:id:mi_kattun:20150113001123p:plain

ローカル執筆環境の前提条件

  • Docker 1.3以降がインストールされている
  • Re:VIEW形式の原稿のフォルダがある

Re:VIEWやLaTeXはDockerイメージのものを使うので、インストール不要です。

私はMac OS X 10.9とBoot2docker 1.4.1で動かしましたが、WindowsLinuxでも動くのではないかと思います。

Re:VIEWをDockerで動かす

vvakameさんがDocker Hubにイメージを公開してくれていますので、これを使います。

https://registry.hub.docker.com/u/vvakame/review/

dockerコマンドさえ使える状態であれば、以下のようにしてサンプル書籍コンパイルしてbook.pdfを生成できます。

$ git clone -b docker-circleci https://github.com/orangain/review-sample-book
$ cd review-sample-book/src
$ docker run \
     --rm \
     -v $(pwd):/work \
     -v $(pwd)/.texmf-var:/root/.texmf-var \
     vvakame/review:latest /bin/sh -c "cd /work && review-pdfmaker config.yml"

初回実行時は2GB超のイメージをダウンロードするので、結構時間がかかりますが、2回目以降は数秒で終わります。

ホストのカレントディレクトリをコンテナ内の /work としてマウントしてその中で実行するので、コンテナ内であることをほとんど意識せずに使えます。

review-pdfmaker 実行の際に、コンテナ内の /root/.texmf-var フォルダにフォントのキャッシュ?(詳しくは知りません)が作られます。これをホスト側に永続化できるよう設定してやることで、2回目以降は高速にPDFを生成できます。

CircleCIでDockerを使う

CircleCI ではDockerを使えます。DockerネイティブなDrone.io のほうがやりやすいかもしれませんが、プライベートリポジトリでも1並列なら無料で使えるCircleCIを選択しました。(と思いましたが、Shippable などでも良かったかもしれません。いずれ試してみたいです。)

サンプル書籍では、以下のような circle.yml を置くことでビルドを自動化できました。

# Dockerを使う
machine:
  services:
    - docker

# Dockerイメージを毎回プルしなくても良いようにキャッシュする
# See: https://circleci.com/docs/docker#caching-docker-layers
dependencies:
  cache_directories:
    - "~/docker"
  override:
    - docker info
    - if [[ -e ~/docker/image.tar ]]; then docker load --input ~/docker/image.tar; fi
    - docker pull vvakame/review
    - mkdir -p ~/docker; docker save vvakame/review > ~/docker/image.tar

# 執筆環境で使うコマンドとは以下の2点が異なる
# 1. --rm オプションがエラーになるので使わない
#    See: https://github.com/docker/docker/issues/4897
# 2. .texmf-varフォルダをマウントしない
#    キャッシュするためにはdependenciesでキャッシュを作らないといけない
test:
  override:
    - cd src; docker run -v $(pwd):/work vvakame/review:latest /bin/sh -c "cd /work && review-pdfmaker config.yml"

# 生成したPDFを成果物として保存する
general:
  artifacts:
    - "src/*.pdf"

ジョブは以下の場所から閲覧できます。成果物は自分しか見えないようですが。

forkしたソースコードは以下の場所に置いてあります。

参考