ヤルキデナイズド

Unclassified Articles on Software and IT

HTML の構造を YAML で吐き出すスクリプトを書いた

pixiv の HTML がどんどん変わって pixiv gem が追従できなくて困った。ので楽するやつを書いた。 HTML を読み込ませると要素の親子関係と各要素の属性を YAML にして書き出す。テキストノード他は無視する。 XPathCSS Selector で不要な要素を取り除くことができる。

!omap で書き出してあるから map の順序が固定されていて diff しやすい。 pixiv の画像一覧ページなどを定期的に取得して HTML で保存しておき、最新の2つから広告やワンタイムトークンを削除しつつ YAML 化して差分を取る。で構造に変化があれば対応する。テストも自動化したいけどそれはいずれ。

使い方:

html2yaml.rb [-x, --exclude=<xpath or css>] \
             [-i, --include=<xpath or css>] \
             <URL or path>

--exclude--include は複数回指定でき、書いた順に適用される。 --exclude はマッチした要素とその子孫要素を出力から取り除く。 --include はマッチした要素とその祖先要素を出力に含める。

コード:

Dump HTML outline as YAML

出力例:

html2yaml.rb -x "*" -i div "http://www.google.com" | head -n20
--- !omap
- :name: html
- :attributes: !omap
  - itemscope: ''
  - itemtype: http://schema.org/WebPage
- :children:
  - !omap
    - :name: body
    - :attributes: !omap
      - bgcolor: '#fff'
      - lang: ja
    - :children:
      - !omap
        - :name: div
        - :attributes: !omap
          - id: mngb
        - :children:
          - !omap
            - :name: div
            - :attributes: !omap

Raspberry Pi の Arch Linux にスワップを設定して Ruby をビルドする

何も考えずに rbenv install 2.0.0-p247Ruby をビルドしたところ、メモリ不足でプロセスが kill されてしまった。もともとメモリが512MBしかないうえに tmpfs がその半分を利用し、デフォルトではスワップ領域も設定されていないので、大きなアプリケーションのビルドは厳しいようだ。

そこでスワップファイルをスワップ領域として使うように設定する(参考:Swap (日本語) - ArchWiki):

# スワップ領域が設定されていないことを確認する
# 何も出力されなければ OK
$ swapon -s

# 空のスワップファイルを作る
# dd でもいいが fallocate を使うほうが速い
$ sudo fallocate -l 1G /var/swapfile

# スワップファイルをフォーマットする
$ sudo mkswap /var/swapfile

# スワップ領域を有効にする
$ sudo swapon /var/swapfile

# スワップ領域が設定されたことを確認する
$ swapon -s
Filename                                Type            Size    Used    Priority
/var/swapfile                           file    1048572 0       -1

# 起動時にスワップ領域をマウントするよう /etc/fstab に設定を追加する
$ sudo sh -c 'echo "/var/swapfile none swap defaults 0 0" >> /etc/fstab'

これでスワップ領域を設定できた。ついでに /tmp ディレクトリのサイズも拡張してしまおう。

/tmp ディレクトリは tmpfs によってメモリ上に確保されており、デフォルトでは実メモリの半分の容量がディレクトリサイズの上限となっている(RaspberryPi ModelB では230MB程度)。 /tmp は systemd が自動的にマウントするので、 systemd の設定ファイルを追加してマウント時にサイズを指定してやればよい(参考:systemd (日本語) - ArchWiki):

# デフォルトのマウント設定ファイルは /usr/lib/systemd/system/tmp.mount
# この一部を上書きする設定ファイルを以下のディレクトリに入れる
$ sudo mkdir -p /etc/systemd/system/tmp.mount.d
$ cd /etc/systemd/system/tmp.mount.d

# デフォルトのオプションに `size=1G` を追加したものを *.conf に保存する
# ここでは increase-size.conf とした
$ sudo sh -c 'echo "[Mount]\nOptions=mode=1777,strictatime,size=1G" >> increase-size.conf'

# 設定をリロードする
$ sudo systemctl --system daemon-reload

# 追加した設定ファイルがロードされたか確認する
$ systemctl status tmp.mount
tmp.mount - Temporary Directory
   Loaded: loaded (/usr/lib/systemd/system/tmp.mount; static)
  Drop-In: /etc/systemd/system/tmp.mount.d
           └─increase-size.conf # ←これがあれば OK
(後略)

# /tmp を再マウントする
$ sudo systemctl restart tmp.mount

# 追加した設定を使ってマウントが行われたか確認する
$ systemctl status tmp.mount
(中略)
  Process: 3014 ExecRemount=/bin/mount tmpfs /tmp -t tmpfs -o remount,mode=1777,strictatime,size=1G (code=exited, status=0/SUCCESS)
  # ↑size=1G が反映されていれば OK

# /tmp ディレクトリのサイズを確認する
$ df -h
(中略)
tmpfs           1.0G     0  1.0G   0% /tmp # ←1.0Gになっていれば OK

これで Ruby をビルドする準備が整った。

$ rbenv install 2.0.0-p247

数時間かかるので気長に待つ。

DDNS サービスを利用して外出先から Raspberry Pi に SSH する

前の記事でセットアップした Raspberry Pi に外出先からログインできるようにしてみる。

まず NoIP でフリーのアカウントを作る。メールアドレスとパスワードと割り当てられたドメインをメモしておく。

Raspberry Pi に ddclient をインストールする:

$ yaourt -S ddclient

/etc/ddclient/ddclient.conf に設定を記述する:

daemon=300                         # 300秒ごとに IP をチェック
syslog=yes                         # syslog にログを出力
mail-failure=your-mail@example.com # 更新失敗時にメール送信
pid=/var/run/ddclient.pid          # プロセスの PID ファイル
ssl=yes                            # SSL を有効にする

##
## NoIP
##

# DNSPark の web ページを使って自身のグローバル IP をチェック
use=web, web=ipdetect.dnspark.com, web-skip='Current Address:'
# NoIP のアカウント設定
protocol=noip,              \
login=your-mail@example.com \
password=YOUR_PASSWORD_HERE \
your-host.no-ip.biz

ddclient サービスを起動する:

$ systemctl enable ddclient
$ systemctl start ddclient

ルータの設定で外部の22番ポートを Raspberry Pi の22番ポートにマップする。

これで外出先から ssh your-host.no-ip.biz で Raspberry Pi に接続できるようになった。

Mac で Raspberry Pi 用の Arch Linux を準備してみた

はじめに

本稿では Mac を使って Raspberry Pi 用に Arch Linux をセットアップする手順を説明する。今回構築する Linux システムでは、ルートファイルシステムを外付けハードディスクに移し、ブートファイルシステムを収めた SD カードを読み取り専用で扱うことを目指す。 Arch Linux の初期設定についてはカバーしない。また本稿は備忘録であるため内容の正確さは保証しない。内容についての指摘は歓迎する。

用意した環境

  • Arch Linux ARM (archlinux-hf-2013-07-22.img)
  • MacBook Pro 15-inch, mid 2010
  • Raspberry Pi ModelB 512MB
  • 適当な SD カード(8GB)
  • 適当な USB 接続の外付けハードディスク(120GB)
  • 5V700mA 以上で MicroUSB 出力できる電源(100円ショップにあるスマホ用充電ケーブルで十分)
  • 接続に必要なもの
    • USB キーボード
    • HDMI ケーブルとテレビ
    • 上記2つ、または LAN ケーブル

SD カードをフォーマットする

SD カードを Mac の SD カードスロットに挿入する。/アプリケーション/ユーティリティ/ディスクユーティリティを起動し、左の欄から「xxGB APPLE SD Card Reader Media」を選択する。右のタブの「消去」を選び、フォーマット:「MS-DOS (FAT)」、名前:「UNTITLED」になっていることを確認し、「消去…」ボタンを押す。

f:id:uasi:20130915142101p:plain

SD カードをアンマウントする

ターミナルを起動し、マウントされている SD カードを df コマンドで確認する:

$ df
Filesystem                        512-blocks      Used Available Capacity  iused    ifree %iused  Mounted on
/dev/disk0s2                       659597488 588953464  70132024    90% 73683181  8766503   89%   /
(中略)
/dev/disk3s1                        15681728      4288  15677440     1%        0        0  100%   /Volumes/UNTITLED

/Volumes/UNTITLED としてマウントされている SD カード(の第1パーティション)は、 /dev/disk3s1 というデバイスであることが分かる。この値は環境によって異なるかもしれないので、その場合は以降の記述を適宜読み替えてほしい。

diskutil コマンドでこの SD カードをアンマウントする:

$ diskutil unmount /dev/disk3s1
Volume UNTITLED on disk3s1 unmounted

SD カードに OS のディスクイメージを書き込む

Raspberry Pi 向けの ArchLinux ARM のページの Installation タブを開き、 OS のディスクイメージをダウンロードする。ダウンロード ZIP ファイルを展開すると archlinux-hf-2013-07-22.img が作られる。このディスクイメージを dd コマンドで SD カードに書き込む:

$ sudo dd bs=1m if=/path/to/archlinux-hf-2013-07-22.img of=/dev/rdisk3
1870+0 records in
1870+0 records out
1960837120 bytes transferred in 201.702728 secs (9721421 bytes/sec)

of=/dev/rdisk3 は先ほど確認したデバイス名に合わせて読み替える(デバイス名が /dev/disk4s1 なら of=/dev/rdisk4 という具合)。

補足: Mac の BSD dd では bs オプションの数字の接尾辞を小文字にしなければならない。一方 GNU dd の bs オプションは接尾辞が大文字である。

書き込みが終わるとふたたび SD カードがマウントされるので、ファインダーを操作して SD カードを取り出す。

Raspberry Pi を起動する

Raspberry Pi に SD カードを挿入し、外付けハードディスクを基板から遠いほうの USB ポートに接続する(ポートの位置は重要ではないが、以降の説明に現れるハードディスクのデバイス名が変わってくるかもしれない)。外付けハードディスクが外部給電タイプではない場合、 Raspberry Pi との間にセルフパワード USB ハブを挟むこと。そうしなければ Raspberry Pi の電源が不安定になり、起動しなかったり突然落ちたりする。

空いている USB ポートにキーボードを接続し、 HDMI ポートにはテレビを繋ぐ。それらの代わりに、 LAN ケーブルで Raspberry Pi とルータを接続し、起動後に SSH でログインすることもできる。 この手順は詳しく解説しない。

電源ケーブルを除く各ケーブルの接続が終わったら、外付けハードディスクの電源を入れ数秒待つ。電源を安定させる意図でこのようにしたが、こうする必要はないかもしれない。

最後に Raspberry Pi の電源ケーブルを接続する。基板の PWR ランプが点灯し、テレビ画面に起動ログが出力されれば起動成功である。

外付けハードディスクをフォーマットする

ユーザ名「root」、パスワード「root」で Raspberry Pi にログインする。外付けハードディスクに対応するデバイス名を lsblk コマンドで調べる:

[root@alarmpi ~]# lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0 111.8G  0 disk 
`-sda1        8:1    0 111.8G  0 part 
mmcblk0     179:0    0   7.5G  0 disk 
|-mmcblk0p1 179:1    0    90M  0 part /boot
|-mmcblk0p2 179:2    0     1K  0 part 
`-mmcblk0p5 179:5    0   1.7G  0 part /

ディスクのデバイス名は /dev/sda、その第1パーティションは /dev/sda1 であることが分かる。ルートファイルシステムにあたる SD カードのパーティションが /dev/mmcblk0p5 であることもメモしておく。

fdisk コマンドでディスクをフォーマットする。すべてのパーティションを削除し、プライマリパーティションを1つ作成する:

[root@alarmpi ~]# fdisk /dev/sda
Command (m for help): d # パーティション削除:すべて削除するまで繰り返す
Command (m for help): n # パーティション作成:質問はすべてデフォルト値でよい
Command (m for help): w # 情報をディスクに書き込んで終了

ルートファイルシステムを外付けハードディスクに移す

SD カードの書き換え回数には上限があるため、なるべく書き込みを減らしたいものである。そこで、ルートファイルシステムを丸ごと外付けハードディスクに移してしまうことにする。

まずルートファイルシステムを SD カードから外付けハードディスクにコピーする:

[root@alarmpi ~]# dd bs=1M if=/dev/mmcblk0p5 of=/dev/sda1

if オプションの値は先ほどメモした SD カードのパーティションにすること。

コピーされたルートパーティションのファイルシステムext4、サイズは2GBになっている。サイズが小さいままではもったいないなので、パーティションをハードディスク全体に拡張する。パーティションのリサイズには resize2fs コマンドを使う:

[root@alarmpi ~]# resize2fs /dev/sda1

特にオプションを与えなければパーティションを最大まで拡張してくれる。

補足:各デバイスのファイルシステムlsblk -f で一覧できる。

外付けハードディスク内のルートファイルシステムで起動する

起動時に外付けハードディスク内のルートファイルシステムを参照するように、起動パラメータを書き換える:

[root@alarmpi ~]# vi /boot/cmdline.txt
ipv6.disable=1 selinux=0 plymouth.enable=0 smsc95xx.turbo_mode=N dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p5 rootfstype=ext4 elevator=noop rootwait

/boot/cmdline.txt 内の root=/dev/mmcblk0p5root=/dev/sda1 に修正し、保存する。

reboot コマンドで再起動し、 lsblk コマンドでデバイスを確認する。 sda1 のマウントポイントが / になっていればよい。

[root@alarmpi ~]# lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0 111.8G  0 disk 
`-sda1        8:1    0 111.8G  0 part / # ←こうなっていれば成功
mmcblk0     179:0    0   7.5G  0 disk 
|-mmcblk0p1 179:1    0    90M  0 part /boot
|-mmcblk0p2 179:2    0     1K  0 part 
`-mmcblk0p5 179:5    0   1.7G  0 part

パラメータを間違えるなどして Raspberry Pi が起動しなくなった場合、 SD カードを Mac に挿入してファインダーで開き cmdline.txt を編集するとよい。

終わりに

これで目指したとおりの環境が構築できた。この構成ならば基本的には SD カードにデータが書き込まれることはないため、書き込みプロテクトスイッチをオンにしてもよいかもしれない。

参考にしたページ

unionfs-fuse か aufs でルートファイルシステムをオーバーレイすることも考えたが面倒そうだったのでやめた。そのときの調査記録も残しておく:

できたばかりの web サービスを知るには LaunchRock Discover がよい

LaunchRock Discover にメールアドレスを登録すると、数週間に一度、ローンチしたばかりの、またはこれからローンチする web サービスのまとめが送られてくる。

ローンチ前の web サービスの情報をどうやって集めてくるのか不思議に思えるが、これは LaunchRock の本業に秘密がある。 LaunchRock は「ローンチのためのプラットフォーム」を提供している。公開前の web サービスを宣伝したい開発者は、 LaunchRock を使ってサービスの告知ページを簡単に作ることができる。また告知ページのアクセス解析や優先招待システムなどローンチに関わる様々なツールも利用できる。 LaunchRock Discover は宣伝の一環としてそれら公開前のサービスのまとめを配信するという仕組み。

最近サインアップした web サービス

便利そうなものをメモっておく。

Quip について

Google Docs よりも編集インターフェースがクリーン。本格的なワードプロセッサアプリと比較すると文書の装飾の自由度は劣るが、シンプルで見栄えのいいドキュメントを書くにはむしろほどよい制限になっている。コラボレーション機能を備えており、複数人でひとつのドキュメントを編集できるほか、各人の編集履歴やドキュメントごとに設けられたコメント欄のログがタイムラインに一覧表示される。 Evernote のように個人的なメモ用途として使うこともできるが、それよりは一人〜複数人である程度の規模の文書を作るときに使うとよさそう。

f:id:uasi:20130825003021p:plain

Handle について

Gmail の Inbox をざくざく読みながら必要なものだけタスクリストに積む、といった使い方に特化している。ほとんどの操作をキーボードショートカットで実行できるようになっており、発展途上ながらおおむね Gmail のキーボードショートカットより優れていると感じる。 Inbox にあるはずのメールが読み込まれないなど、まだまだ不安定な部分も多い。 iPhone アプリもあるがメールの閲覧はできず、タスク化したアイテムを表示できるのみ。そしてすぐ落ちる。コンセプトと操作感には強く期待できるので安定したら常用したい。

2013年8月24日現在で招待制のベータ版。 Handle のブログから招待コードを入手するとすぐにサインアップできる。

f:id:uasi:20130825002832p:plain