昔のブログは Hatena Blog でホスティングしていたのですが、 一念発起してこちらの Jekyll ベースのブログにコンバートしてインポートしました。

Hatena Blog のエクスポート時のフォーマットは MovableType 形式 なので、 それを一括で変換してインポートする感じに適当スクリプトを作りました。

予め reverse_markdown gem をインストールしておく必要があります。

コード

# frozen_string_literal: true

require 'erb'
require 'reverse_markdown'
require 'time'
require 'uri'

contents_all = File.readlines('xxx.hatenablog.com.export.txt')
puts contents_all.length

chunked = contents_all.chunk_while do |i, _|
  !(i =~ /^--------\Z/)
end

puts chunked.to_a.size

class Article
  # @return [Array]
  attr_accessor :raw
  # @return [Hash]
  attr_accessor :meta
  # @return [Array]
  attr_accessor :body
  # @return [Array]
  attr_accessor :comment
  # @return [String]
  attr_accessor :body_markdown

  def initialize(arr)
    @raw = arr
    @meta = {}
    @body = []
    @comment = []

    @raw.pop # rm term

    parse!
    convert_body!
  end

  def parse!
    state = :meta
    original = @raw.dup
    while true
      line = original.shift
      break unless line

      case state
      when :meta
        if line =~ /-----/
          state = :body
          next
        end
        line =~ /([A-Z ]+): (.+)?/
        @meta[$1] ||= []
        @meta[$1] << $2
      when :body
        if line =~ /-----/
          state = :comment
          next
        end

        @body << line
      when :comment
        @comment << line
      end
    end
  end

  def convert_body!
    b = @body.dup
    b.shift # rm BODY: line
    @body_markdown = ReverseMarkdown.convert(b.join, github_flavored: true)
  end
end

articles = chunked.map do |chunk|
  Article.new(chunk)
end

class Publisher
  attr_accessor :article
  attr_accessor :title
  attr_accessor :publish_datetime
  attr_accessor :categories
  attr_accessor :body
  attr_accessor :content
  attr_accessor :filename

  # @param [Article] article
  def initialize(article)
    @article = article

    @title = article.meta['TITLE']
    @publish_datetime = article.meta['DATE']
    @categories = article.meta['CATEGORY']
    @body = article.body_markdown

    normalize!
  end

  def publish
    template = <<EOS
---
layout: post
title:  "<%= @title %>"
date:   <%= @publish_datetime.to_s %>
categories: [<%= @categories.join(', ') %>]
---

<%= @body %>
EOS

    b = binding
    @content = ERB.new(template).result(b)
    @filename = generate_filename
  end

  def normalize!
    @title = @title[0] || '無題'
    @publish_datetime = Time.strptime(@publish_datetime[0], '%m/%d/%Y %H:%M:%S')
    @categories = @categories || ['post']
  end

  def generate_filename
    ymd = @publish_datetime.strftime("%Y-%m-%d")
    title = @title.lstrip[0, 16].gsub(/[\/.:]/, '_')
    "#{ymd}-#{URI.escape(title)}.markdown"
  end
end

publishes = articles.map do |article|
  p = Publisher.new(article)
  p.publish
  p
end

publishes.each do |pub|
  puts pub.filename

  File.open('_posts/' + pub.filename, 'w') do |f|
    f.write(pub.content)
  end
end

とりあえずざっくりでインポートはできるのですが、 元データもそんなに安定してなかったりなので結構手作業で直しちゃう感じである程度整えたところで手仕舞いとしました。

冗長な感じだったり puts あるねぇな感じだったりするのは手探りでやってたままのコードだからです、ご容赦ください! よかったらお使いくださいませ〜。

ライセンス

クリエイティブ・コモンズ 表示 - 非営利 4.0 国際 ライセンス

おわり