コンピュータ/ソフトウェア関連Tips

本文へ

LinkCheckerが出力したXMLファイルをパースしてHTMLを作成する

更新: 2013年06月19日(水) 23:13
公開: 2013年06月16日(日) 18:00

LinkCheckerが出力するHTMLファイルはErrorとWarningが一緒になっている。しかも、Warningの中には、「http-robots-denied」とか「url-content-duplicate」のように、自分には不要なWarningがある。また、外部サイト(自サイトではないサイト)がParent URL欄に表示されるときがある。「--no-warnings」オプションでWarningを抑制できるようなのだが、Warningの中には有益なものもある。例えば「Leading or trailing whitespace in URL」 。

そこで、XML形式で出力して、ErrorとWarningの2つのHTMLファイルを作成するパーサーを作ることにした。Warningは、自分にとって不要なものは除外するようにフィルタリング。

ソースはこんな感じ。(Rubyは自分のコーディングスタイルがまだ不安定。ちゃんと学べよ、俺)。Rubyのバージョンは2.0.0。XPathは初めて使ったのだが、いろいろ使い道がありそうだ。

XPathのattributeの使い方は、以下のサイトが参考になった。

http://himaratsu.hatenablog.com/entry/2013/04/27/002249

2013.6.17記: Rubyのコーディング規約をいくつか読んだけど、人目に晒すには早かった。はずかしー。

2013.6.19記: コードを修正。

#!/usr/local/bin/ruby
# coding: utf-8
# The above magic comment is not needed if you use ruby 2.0.0.

# Copyright (C) 2013 Fumio KAWAMATA
# This is a private work at home.

require 'rubygems'
require 'nokogiri'

def change_ext(filename, ext)
  filename.sub(/\.\w+$/, ext)
end

def write_header(outdir, prefix, mode, created)
  out_name="#{outdir}/#{prefix}_#{mode}.html"
  open(out_name, "w") do |f|
    f.print <<EOT
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ja">
<head>
<meta http-equiv="content-style-type" content="text/css">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="description" content="Broken link check results" />
<title>Broken link check results</title>
<style type="text/css">
<!--
  body { background-color: #fff7e5;}
  td.center { text-align: center; }
  td.url { background-color: #dcd5cf; }
  td.warning { background-color: navajowhite; }
  td.ok { background-color: palegreen; }
  td.error { background-color: lightpink; }
  table { border-collapse:collapse; ext-alingn: left;}
  tr { vertical-align:top;}
  th { padding: 2px;border: 1px solid black; background-color : NavajoWhite;}
  td { padding: 2px;border: 1px solid black; background-color : white;
       font-family: Arial,sans-serif; }
  p { font-family: Arial,sans-serif; }
  h1 { text-align: center;}
-->
</style>
</head>
<body>
<h1>Broken link check results</h1>
EOT
    f.print "<p>This page is parsed from the result of the LinkChecker.</p>\n"
    f.print "<p>Start checking at #{created}</p>\n"
    f.close
  end
end

def append_footer(outdir, prefix, mode, parent_url_pattern)
  out_name="#{outdir}/#{prefix}_#{mode}.html"
  open(out_name, "a") do |f|
    f.print "<p>It is not displayed that the Parent URL is not match as /#{parent_url_pattern}/ (Regular expression).</p>\n"
    f.print <<EOT
<p>several warnings such as "http-robots-denied" and "url-content-duplicate" are ignored</p>
</body>
</html>
EOT
    f.close
  end
end

unless ARGV.size == 3
  print %(Error: "File name", "output directory" and "Parent URL filter" are required.\n)
  exit(1)
end

xmlfile=ARGV[0]
outdir=ARGV[1]
parent_url_pattern=ARGV[2]

unless File.exist?(xmlfile)
  print "Error: #{xmlfile} is not existed.\n"
  exit(2)
end

unless File.exist?(outdir)
  print "Error: #{outdir} is not existed.\n"
  exit(3)
end

prefix=change_ext(File.basename(xmlfile), "")
doc=Nokogiri::XML(File.open(xmlfile))
if doc.xpath('count(/linkchecker)') == 0
  print "Error: #{xmlfile} is not a linkcheker XML file.\n"
  exit(4)
end

created=doc.xpath("/linkchecker").attribute("created").value;
write_header(outdir, prefix, "error", created)
write_header(outdir, prefix, "warning", created)

doc.xpath("//urldata").each do |node|
  node.xpath("url") == nil ? url="" : url=node.xpath("url").text
  node.xpath("name") ==nil ? name="" : name=node.xpath("name").text

  if node.xpath("parent") == nil
    parent_url=""
    parent_column=""
    parent_line=""
  else
    parent_url=node.xpath("parent").text
    parent_column=node.xpath("parent").attribute("column").value
    parent_line=node.xpath("parent").attribute("line").value
    if /#{parent_url_pattern}/ =~ parent_url
      significant_parent_url=true
    else
      significant_parent_url=false
    end
  end

  node.xpath("realurl") == nil ? relurl="" : realurl=node.xpath("realurl").text
  node.xpath("dlsize") ==nil ? dlsize="" : dlsize=node.xpath("dlsize").text
  node.xpath("checktime") ==nil ? checktime="" : checktime=node.xpath("checktime").text
  node.xpath("valid").attribute("result") ==nil ? result= "" : result=node.xpath("valid").attribute("result").value
  infos=""
  unless node.xpath("infos") == nil
    count=node.xpath('count(infos/info)')
    if count>0
      range=1..count
      range.each do |i|
        unless infos == ""
          infos="#{infos}<br>"
        end
        formula="infos/info[#{i.to_s}]"
        infos=infos+node.xpath(formula).text
      end
    end
  end

  significant_warnings=0
  warnings=""
  unless node.xpath("infos") == nil
    count=node.xpath('count(warnings/warning)')
    if count>0
      range=1..count
      range.each do |i|
        unless warnings == ""
          warnings="#{warnings}<br>"
        end
        formula="warnings/warning[#{i.to_s}]"
        tag=node.xpath(formula).attribute("tag").value
        if (tag != "http-robots-denied") and (tag != "url-content-duplicate")
          significant_warnings=significant_warnings+1
          warnings=warnings+node.xpath(formula).text
        end
      end
    end
  end

  if node.xpath("valid").text == "0"
    mode="error"
    td_class="error"
  else
    mode="warning"
    td_class="warning"
  end

  if (significant_warnings>0) and (significant_parent_url==true)
    out_name="#{outdir}/#{prefix}_#{mode}.html"
    open(out_name, "a") do |f|
      f.print "<table>\n"
      f.print %(<tr><td class="url">URL</td><td class="url">#{url}"</td></tr>\n)
      f.print "<tr><td>Name</td><td>#{name}</td></tr>\n"
      f.print %(<tr><td>Parent URL</td><td><a href="#{parent_url}">#{parent_url}</a>, line: #{parent_line}, Column: #{parent_column}</td></tr>\n)
      f.print %(<tr><td>Real URL</td><td><a href="#{realurl}">#{realurl}</a></td></tr>\n)
      f.print "<tr><td>Size</td><td>#{dlsize}</td></tr>\n"
      f.print "<tr><td>Check time</td><td>#{checktime}</td></tr>\n"
      f.print "<tr><td>Info</td><td>#{infos}</td></tr>\n"
      f.print "<tr><td>Warning</td><td>#{warnings}</td></tr>\n"
      f.print %(<tr><td class="#{td_class}">Result</td><td class="#{td_class}">#{result}</td></tr>\n)
      f.print "</table>\n"
      f.print "<br>\n"
      f.close
    end
  end
end

append_footer(outdir, prefix, "error", parent_url_pattern)
append_footer(outdir, prefix, "warning", parent_url_pattern)

exit(0)

同一カテゴリーの記事

Tags: Ruby

© Someone in the TERRA

編集