HTMLパーサーの Hpricot を使って、リンク <a href="hoge.html"> や 画像 <img src="hoge.png"> や Flash <embed src="hoge.swf"> から相対パスを抽出して、絶対パスへ置き換えるという方法。

直接、Hpricot や nokogiri で置換しようかと思ったが、元のHTMLの構造を崩しそうなのでそれはヤメ。
⇒ 参考: ヅラッシュ! - Ruby + Hpricot で HTML ファイル内の URL を置き換える

HTML内に存在する相対パスはダブルクォートで囲まれているという前提で処理することにした。(シングルクォートの可能性…)

処理の流れ:
1. hpricot で相対パスを抽出
2. 相対パスに対応する絶対パスを生成
3. 相対パスの前後にダブルクォートを付ける
4. HTML内からダブルクォート付き相対パスをダブルクォート付き絶対パスに置換


#!/usr/bin/env ruby
$KCODE='u'
 
require 'hpricot'
 
# 相対パスを絶対パスへ変換する
# [param]  base_path (String) 絶対パスの基準となるパス文字列
# [param]  src_path  (String) 変換対象のパス文字列
# [return] (String) 絶対パス文字列
def replace_path(base_path, src_path)
  begin
    # 変換しなくていいパターン
    no_replace_patterns = [ '^http://', '^https://', '^/', '^#' ]
    no_replace_patterns.each{|pat|
      reg = Regexp.new(pat)
      return src_path if reg =~ src_path
    }
    # 変換するパターン
    return File.expand_path(src_path, base_path)
  rescue
    # 主に <a herf="hoge"> などの typo でエラーが発生する
    $stderr.puts "ERROR: src_path=#{src_path}, base_path=#{base_path}"
    $stderr.puts $!.inspect
    raise $!
  end
end
 
# HTMLテキストからパス表現を抽出する
# [param]  doc (Hpricot::Doc)
# [return] (Array[String]) パス文字列の配列
def extract_paths(doc)
  paths = []
  begin
    (doc/:a).each{|elem|
      # <a name="hoge"> は変換しない
      # <a herf="hoge"> や <a hrf="hoge"> とかの
      # typo を発見したらエラーが出るように
      if !elem[:name] || elem[:href]
        paths << elem[:href]
      end
    }
    (doc/:img).each{|elem|
      paths << elem[:src]
    }
    (doc/:embed).each{|elem|
      paths << elem[:src]
    }
    (doc/:param).each{|elem|
      if elem[:name] == 'movie'
        paths << elem[:value]
      end
    }
  rescue
    $stderr.puts $!.inspect
    raise $!
  end
  return paths
end
 
# HTMLテキスト内の相対パスを絶対パスへ変換する
# [param]  html (String) 変換対象のHTML文字列
# [param]  base_path (String) 絶対パスの基準となるパス文字列
# [return] (String) 変換後のHTML文字列
def replace_html(html, base_path)
  dst_html = html
  src_paths = extract_paths(Hpricot(html))
  src_paths.each{|src_path|
    dst_path = replace_path(base_path, src_path)
    # HTML内のパスはダブルクォートで囲まれているという前提で
    dst_html.gsub!("\"#{src_path}\"", "\"#{dst_path}\"")
  }
  return dst_html
end
 
# だいたいこんな感じでデータを渡す
base_path = '/zurazure2/'
src_html = File.open('srcdata.html').read()
dst_html = replace_html(src_html, base_path)
puts dst_html

tags: ruby hpricot

Posted by NI-Lab. (@nilab)