読者です 読者をやめる 読者になる 読者になる

Text::Hatenaを活用しまくる

Ruby

目次

  • はてな記法で静的コンテンツ生成のためにText::Hatenaを使うことにした
    • わりとうまくいった
    • なぜかTeX記法も見れた
  • iPhone上での使いやすさが結構やばい
  • 変換用のソースと一括変換用のソース

内容

Blogとか動的なコンテンツじゃなくて、静的なコンテンツを作りたいと思っている。MakeWebを利用しようかと思ったんだけど、はてな記法でいうリスト記法(かな?)を多用しまくる僕にはMakeWebの記法だとつらい。他のwikiの記法を学ぶコストもでかいし、何よりはてな記法になじみまくっているからはてな記法を使いたい。simple-hatena-modeとかなじみのあるやつで書けるっていうのも大きいな。で、Junya Kondo / Text-Hatena - search.cpan.orgがあるんだけど、昔cpanでインストールしようとするとこけた記憶があるし*1、Perlで3行以上もう書ける気しないというわけで探していたらRubyのやつを見つけた。

なお、はてぶコメントでid:noplansさんにhparserというのもあることを教えてもらいました。JavaScriptのやつは知ってたんだけどな。rake installしました。gemじゃないインストールは何気に初めて。Rubyなら分かる気がするので、書いてみた。こんな感じで実行すると

/Users/yasuhisa% ruby test.rb -i /Users/yasuhisa/hatena/syou6162/diary/2009-01-04.txt -o fuga.html

こんな感じのhtmlを吐いてくれる。cssは別途容易した。

追記

別ファイルにするのが面倒だったので、cssも一緒にした。
Text::Hatena
はてな記法で書いたテキストファイルの数を見ると

/Users/yasuhisa% ls hatena/syou6162/diary/*.txt hatena/syou6162/group/syou6162/*.txt hatena_keyword/syou6162/*.txt | wc -l
    1275

とかなっている(はてなグループのキーワードもローカルから飛ばすようにしている)僕なので、かなり使えそうな感じである。あと、こんな感じで、TeX記法までできちゃうんだけど、これはいいんだろうか。。。いや、便利なんですが。

追記

見れるのはローカルだけっぽい。他のサイトにhtmlファイルを置いても見れないようになっているようだ。安心した(何。
hogehoge

See it on iPhone

いや、何を隠そうiPhoneで見たかったのです。
Text::Hatena on iPhone
iPhoneだと操作性が限定されてくるので

  • 上部に目次を作って下のページでもすぐに行けるようにする
  • 下部のページにいても目次にすぐに戻れるようにする(エピソード3のまとめの右の■で戻れるようにしてある)

というどうでもよさげなライフハック(笑)を実行してあります。iPhoneに大量のhtmlを仕込むと容量を圧迫するので、DropBoxなど雲の上に置いておくといいかなーと思います。touchだったら仕込んだほうがいいかもしれないけど。

ソースは以下。

# -*- coding: utf-8 -*-
require "rubygems"
require "mechanize"
require "optparse"
require "text/hatena"
require "pp"

@options = {
  :i  => nil,
  :i  => nil,
  :s  => "",
  :p  => true
}

opts = OptionParser.new

opts.on('-i file', '--input file',
  'specify the input file name') {|f| @options[:i] = f}
opts.on('-o [file]', '--output [file]',
  'specify the output file name') {|f| @options[:o] = f}
opts.on('-s chat', '--sectionanchor char',
  'specify the sectionanchor') {|c| @options[:s] = c }
opts.on("-p", "--presentation",
        TrueClass){|boolean| @options[:p] = !boolean}

opts.parse!(ARGV)

if @options[:i].nil?
  puts "file name is needed."
  exit
end

sectionanchor = @options[:s]

parser = Text::Hatena.new({
  :parmalink => 'http://www.example.com/entry/123',
  :sectionanchor => sectionanchor
})

file = @options[:i]
title = File.basename(file,".*")
f = open(file,"r")
text = f.read
f.close

header =<<EOS
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC
  "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rev="made" href="mailto:<-mail>" />
<link rel="index" href="index.html" />
<link href="./main.css" type="text/css" rel="stylesheet" />
<title>hogehoge</title>
</head>

<style type="text/css">
<!-- 
body {
    margin-left: 88px;
    margin-right: 7%;
    background-color: #fff;
    background-repeat: repeat-y;
    text-align: left;
    font-family: sans-serif;
}

h3 {
    font-size: 120%;
    color          : #000000;
    text-align     : left;
    background     : #fefeee;
    padding    : 0.3em 0.3em 0.3em 0.5em;
    border-width   : 1px 1px 1px 1px;
    border-style   : solid;
    border-color   : #aa2;
}

h4 {
    font-size: 110%;
    margin: 1em 0 1em 0;
    padding-left   : 0.2em;
    color          : #000;
    text-align     : left;
    background     : transparent;
    border-width   : 0em 0em 1pt 0pt;
    border-style   : dashed;
    border-color   : #aaa;
}

h5 {
    font-size: 100%;
    color          : #000;
    text-align     : left;
    background     : transparent;
    border-width   : 0em 0em 1pt 0pt;
    border-style   : dotted;
    border-color   : #274;
    margin-left   : 0.4em;
}

img { 
    border: 0pt;
}

.title {
    color: #fff;
    background-color: #225;
    border-color   : #9ad;
    margin: 1em 0 1em 0;
    font-size: 170%;
    padding: 0.4em 0.6em;
    border-width   : 0px 4px 4px 0px;
    border-style   : solid;
}

a {
    text-decoration:none;
}

a:hover {
    background-color: #CCCCFF;
}

p, ul, ol, li, pre {
    margin-left: 1em;
    line-height: 120%;
}

pre.hatena-super-pre {
    padding: 0.5em 0.5em 0.5em 0.5em;
    background-color: #efd;
    font-size: 100%;
}

table {
    width: 90%;
    margin-left: 1em;
    border-spacing: 0.2em 0.5em;
}

th { 
    border-color   : #aaa;
    border-width   : 0px 2px 2px 0px;
    border-style   : solid;
}

td { 
    border-color   : #aaa;
    border-width   : 0px 2px 2px 0px;
    border-style   : solid;
}

tr { 
    margin: 1em 0 1em 0;
}

blockquote {
    margin: 0 1em 0 2em;
    padding: 1em 1em 1em 1em;
    background-color: #efd;
    font-size: 90%;
}

.footer {
    border-top: gray thin solid;
    margin: 3em 0 0 0;
    padding: 1em 0 0 0;
}

-->
</style>
<body>
<a name="top"></a>
<h1 class="title">#{title}</h1>

EOS

footer =<<EOS
</body>
</html>
EOS

text.sub!(/\[:presentation\]/,"") if @options[:p]
text.gsub!(/(asin:(.*?)):(title|detail|image)/){$1}

parser.parse(text)

source = Hpricot(parser.html)

tableofcontents = "<ul>\n"
(source/"div>h3").each_with_index{ |item,index|
  tableofcontents = tableofcontents + "\t<li><a href=\"#p#{index}\">"  + item.inner_text.sub(/^#{@options[:s]}\s(.*?)/){$1} + "</a></li>\n"
}

tableofcontents = tableofcontents + "</ul>\n"

html = header + tableofcontents + parser.html.gsub(/(<\/h3>)/){
  "\s<a href=\"#top\"><span class=\"sanchor\">#{@options[:s]}</span></a>#{$1}"
} + footer

if @options[:o].nil?
  puts html
else
  f = File.open(@options[:o],'w')
  f.puts html
  f.close
end

ファイルを一斉にtxtからhtmlに変換

ターミナルで回せばよかったんだろうけど、なんか怖かったので(ぉ。

Dir.chdir("/Users/yasuhisa/hatena_keyword/syou6162")

Dir.glob("*.txt").each{|f|
  path = "/Volumes/syou6162/Documents/" + File.basename(f,".txt") + ".html"
  system "ruby /tmp/aaa.rb -i \"#{f}\" -o \"#{path}\""
}

とやってもいいけど、100個くらいhtmlができて大変なことになるので、見たいやつだけ

Dir.chdir("/Users/yasuhisa/hatena_keyword/syou6162")
["Tsukuba.R:2:イントロダクション.txt","フュ−チャーリスト宣言.txt","我らクレイジー☆エンジニア主義.txt","頭がいい人、悪い人の仕事術.txt"].each{|f|
  path = "/Volumes/syou6162/Documents/" + File.basename(f,".txt") + ".html"
  system "ruby /tmp/aaa.rb -i \"#{f}\" -o \"#{path}\""
}

とかとするのがよさそう。

*1:今試しにインスコしてみたら入ったしwww