有馬総一郎のブログ

(彼氏の事情)

アメブロ(ameblo.jp)の記事をMarkdown記法のテキストファイルに変換する

以前触れたように、自分はamebaownd.comではなく、octopressに引っ越すことにしたので記事をMarkdown記法に変換する必要があった。

Movable TypeからGitHub+Octopressに乗り換える手順 - それはBooksというちょうど良さそうなブログがあり、また、Movable Type の記事を Markdown に変換するソースも公開されていたので、参考にさせてもらった。

しかし、Windows環境だとrubyの設定が間違っているせいで、変換できない記事がいくつか出る(Ubuntuだと全部変換、成功)。しかも、何故かCGI.unescapeHTMLで実体参照を元の文字列に変換するはずが、実体参照のままになってしまう。初めはそれでもいいと、vimの vim-qfreplaceを使って置換してみたが、どうも記号の置換のためか?エラーが出る。

そもそも、Movable Typeから更にMarkdownに変換するのもなーということで、自分なりに作成したのが、以下の変換PowerShell Scriptである。pandocという変換ツールを使っているので、それをインストールする必要がある。1

htmlToMarkdown.ps1link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
cd "C:\Users\system76\Documents\arimasou16"
# アカウント名の設定
$username = "arimasou16"
# 変換したmarkdownを格納するフォルダの作成
if (Test-Path .\markdown) {
  Remove-Item .\markdown -Recurse -Force
}
New-Item ".\markdown" -itemType Directory -Force
# 一時ファイルを格納するフォルダの作成
if (Test-Path .\tmp) {
  Remove-Item .\tmp -Recurse -Force
}
New-Item ".\tmp" -itemType Directory -Force
$files = Get-ChildItem -File *.html
$index = 0
$imegeidx = 0
foreach($file in $files) {
  # ヘッダー情報
  # タイトル
  $title = (Get-Content -Encoding UTF8 $file).foreach{ if ($_ -match "(?<=data-unique-entry-title=`")(.+?)(?=`" data-unique-ameba-id=)") { $matches[1] }}
  # カテゴリー
  $category = (Get-Content -Encoding UTF8 $file).foreach{ if ($_ -match "(?<=`" rel=`"tag`">)(.+?)(?=<\/a><\/span>)") { $matches[1] }}
  # 記事の日付
  $date = (Get-Content -Encoding UTF8 $file).foreach{ if ($_ -match "(?<=pubdate=`"pubdate`">)(\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2})(:\d{2})(?=<\/time><\/span>)") { $matches[1] + "-" + $matches[2] + "-" + $matches[3] + " " + $matches[4] }}
  # ファイル名
  $octopressfile = $date.Substring(0, 10) + "-" + (++$index).toString("00000") + ".md"
  # 本文だけを出力するファイル
  $bodyfile = ".\tmp\" + $date.Substring(0, 10) + "-" + ($index).toString("00000") + ".txt"
  # 本文ファイルをmarkdown形式に変換するファイル名
  $mdfile = ".\tmp\" + $date.Substring(0, 10) + "-" + ($index).toString("00000") + "_md.txt"
  $year = $date.Substring(0, 4)
  #ボディ情報
  $content = (Get-Content -Encoding UTF8 $file) -as [String[]]
  $isBody = $false
  $body = ""
  foreach ($line in $content) {
      # 本文終了か判定
      if ($line -match "^<!--entryBottom-->$") {
          $isBody = $false
          # 本文の最終行にある</div>、そして、上の空行を削除
          $body = $body.Remove($body.Length -7, 7)
      }
      if ($isBody) {
          # 空白行でなければ
          if (-not ($line -match "^\s*$")) {
              $regex = [regex]("(?<=src=`")(http://stat.ameba.jp/user_images/\d{8}/\d{2}/\w+/\w{2}/\w{2}/\w/)(\w{9}_)(\w{19}\.(jpg|png|gif))(?!.*class=`"articleImage`")")
              # オリジナル画像のurlを作成
              $urls = (Get-Content -Encoding UTF8 $file).foreach{ $regex.Matches($_) | ForEach { $_.Groups[1].Value + "o" + $_.Groups[3].Value }}
              foreach($url in $urls) {
                  $imagefilename = $date.Substring(0, 10) + "-" + (++$imegeidx).toString("00000") + $url.Substring($url.Length -4, 4)
                  $line = $line -replace "(?<=src=`")(http://stat.ameba.jp/user_images/\d{8}/\d{2}/\w+/\w{2}/\w{2}/\w/)(\w{9}_)(\w{19}\.(jpg|png|gif))(\??[^`"]*)(?!.*class=`"articleImage`")", ("/images/$year/" + $imagefilename)
                  $line = $line -replace "(id=`"\w{12}`" class=`"detailOn`" href=`")(http://ameblo.jp/$username/image-\d{11}-\d{11}\.html)(\??[^`"]*)", ("href=`"/images/$year/" + $imagefilename)
              }
              $regex = [regex]("(?<=src=`")(http://stat.ameba.jp/user_images/\d{8}/\d{2}/\w+/\w{2}/\w{2}/\w/o\w{19}\.(jpg|png|gif))")
              # 縮小されていない画像のurlを作成
              $urls = (Get-Content -Encoding UTF8 $file).foreach{ $regex.Matches($_) | ForEach { $_.Groups[1].Value }}
              foreach($url in $urls) {
                  $imagefilename = $date.Substring(0, 10) + "-" + (++$imegeidx).toString("00000") + $url.Substring($url.Length -4, 4)
                  $line = $line -replace "(?<=src=`")(http://stat.ameba.jp/user_images/\d{8}/\d{2}/\w+/\w{2}/\w{2}/\w/o\w{19}\.(jpg|png|gif))(\??[^`"]*)", ("/images/$year/" + $imagefilename)
                  $line = $line -replace "(id=`"\w{12}`" class=`"detailOn`" href=`")(http://ameblo.jp/$username/image-\d{11}-\d{11}\.html)(\??[^`"]*)", ("href=`"/images/$year/" + $imagefilename)
              }
              # 画像の大きさ指定を削除
              $line = $line -replace "height=`"[0-9]{1,4}`""
              $line = $line -replace "width=`"[0-9]{1,4}`""
              $body += $line + "`n"
          }
      }
      # 本文開始か判定
      if ($line -match "^<div class=`"articleText`">$") {
          $isBody = $true
      }
  }
  echo $body | Set-Content $bodyfile -Encoding UTF8
  # pandocを使ってのmarkdown変換
  & pandoc.exe -f html -t markdown $bodyfile -o $mdfile
  # テキスト書き込み(PowerShellは文字コードUTF-16がデフォルトなので)
  $enc = New-Object System.Text.UTF8Encoding($False)
  try{
      # markdown形式utf8として最終的に出力するファイル名
      $writefile = $file.DirectoryName + "\markdown\" + $octopressfile
      $stream_w = New-Object system.IO.Streamwriter("$writefile", $false, $enc)
      $stream_w.Write("---" + "`n")
      $stream_w.Write("layout: post" + "`n")
      $stream_w.Write("title: `"$title`"" + "`n")
      $stream_w.Write("DATE: $date" + "`n")
      $stream_w.Write("comments: true" + "`n")
      $stream_w.Write("categories: $category" + "`n")
      $stream_w.Write("tags: $category" + "`n")
      $stream_w.Write("author: $username " + "`n")
      $stream_w.Write("---" + "`n")
      $lineNo = 0
      $charCnt = 0
      $add = $false
      $content = (Get-Content -Encoding UTF8 $mdfile) -as [String[]]
      foreach ($bodyLine in $content) {
          # 変換
          $bodyLine = $bodyLine -replace "(</?div[^>]*>)|\{style[^}]*\}|{#\w{13}}|{#\w{12}|\.detailOn}|\\`$"
          $bodyLine = $bodyLine -replace "&gt;", ">"
          $bodyLine = $bodyLine -replace "&lt;", "<"
          $bodyLine = $bodyLine + "`n"
          $lineNo++
          $charCnt += $bodyLine.Length
          if (($lineNo -ge 2) -and ($charCnt -ge 80) -and (!$add) -and ($bodyLine -match "^\s+$")) {
              # 続きを挿入
              $stream_w.Write("<!-- more -->`n")
              $add = $true
          }
          $stream_w.Write($bodyLine)
      }
  } finally {
      $stream_w.close()
  }
}
Remove-Item .\tmp -Recurse -Force

上記スクリプトを使って、移行した結果が今のブログである。 pandocがまず、tableタグの変換に対応してなく、削除すべき属性値が残っていたりするが、まあ、これ以上完璧を求めても効率が悪いと思って、画像部分の修正をして以降は修正していない。

当初は、以下のように複数行モードMultiLineを使って、"articleText"から<!--entryBottom-->までを一気に引き抜こうとしたけど、それだと一行にまとまってしまうので止めた。

$body = [regex]::matches((Get-Content -Encoding UTF8 $file), "(`"articleText`">\s)(.*)(<!--entryBottom-->)", 'MultiLine') | % { $_.Groups[2].Value }

作成中は色々書き留めようと思っていたこともあったけど、時間が経ちすぎて、もうどうでも良くなってしまった。取り敢えず、これでブログの移行ネタの準備はできた。


  1. ダウンロードはこちら→Release pandoc 1.19.1 · jgm/pandoc

Comments