次へ: 6 行の分割 上へ: AWK による HTML ファイルの整形 その 2 前へ: 4 タイトルと日付の取得 (PDF ファイル: awkwww2.pdf)


5 一覧部分の取得

次は、一覧部分の取得ですが、実はこちらは少し厄介です。

2 節で一覧の構造を少し紹介しましたが、 実際には少し違っていて、実は、各 <li> の行は別々の行にはなっておらず

  <ul>
  <li>...<br><li>...<br><li>...<br><li>...<br><li>...<br><br>
  <li>...<br><li>...<br><li>...<br><li>...<br><li>...<br><br>
  <li>...<br><li>...<br><li>...<br><li>...<br><li>...<br><br>
  .....
  </ul>
(...の部分がリンクのためのタグや見出し、日時などの部分)

のように、いくつかの行がつながって長い行を作っていて、 いくつかのまとまり (5 個程度) 毎に最後の <br> が 2 つつながっていて そこでやっと改行が入っています。 よって 1 行を取得しても、そこに複数の <li> 行が含まれるので、 それを分割する必要があります。

<ul></ul> は、 上では単独行で書きましたが必ずしもそうであるとは限らず、 それも他の行につながっている場合もあるようです (実際最終ページの </ul> は直前の行の最後につながっている場合があります)。

このブロック部分の判別は、 HTML ファイル内に <ul>$\sim$</ul> ブロックがここにしか現れないようなので、 簡単のため <ul> にマッチするかどうかで判別することにし、 </ul> が現われるまで getline で取得することにします。

なお、明示的な getline を使わず、 フラグを使うことで現在 <ul>$\sim$</ul> 内であるかどうかを判別する、 という方法もありますが、それについては [4] を参照してください。

AWK の疑似コードは以下のようになります。

  ##### 各行の取得 ##### 
  ($0 ~ /<ul>/){
      sub(/<ul>/,"")
      if($0 !~ /<li>/) getline
      do{
          # (1) 1 行を複数の <li> 行に分割して配列に保存
          # (2) それを整形して出力
          getline
      }while($0 !~ /<\/ul>/)
      if($0 ~ /<li>/){
          sub(/<\/ul>/,"")
          # (1) 1 行を複数の <li> 行に分割して配列に保存
          # (2) それを整形して出力
      }
  }

ところで、これだと同じ処理 (上の (1),(2)) を 2 箇所に書くことになりますが、 それを 1 箇所にまとめるには、ループの中の getline を最初に持ってきて、

  ($0 ~ /<ul>){
      sub(/<ul>/,"")
      do{
          getline
          # (1) 1 行を複数の <li> 行に分割して配列に保存
          # (2) それを整形して出力
      }while($0 !~ /<\/ul>/)
  }
とでもすれば済みそうですが、 これだと最初の行が <ul> のすぐ後ろから <li> が始まっている場合はそれを捨てることになってしまいますし、 最後が </ul> 単独の行である場合にも対応しておらず その行に対して (1),(2) を実行してしまいます。

(1),(2) の部分をループの中に 1 回だけしか書かないようにするには、 フラグなどを用いて次のようにする方法があります:

  ##### 各行の取得 その 2 ##### 
  ($0 ~ /<ul>){
      sub(/<ul>/,"")
      flag=0
      do{
          if(flag==1 || $0 !~ /<li>/) getline
          flag=1
          if($0 ~ /<\/ul>/ && $0 !~ /<li>/) break
          # (1) 1 行を複数の <li> 行に分割して配列に保存
          # (2) それを整形して出力
      }while($0 !~ /<\/ul>/)
  }
しかしこれだと、最初の if 文が意味があるのは <ul>$\sim$</ul> ブロックの最初の 1 行だけで、 その他の行に関しては if 文の判断は無駄になりますし、 2 つ目の if 文も最後の行にしか意味がないので、 その他の行に関しては if 文の判断は無駄です。

最初のコードと最後のコードは、(1),(2) のような部分を何度も書いていない、 という点では後者の方が綺麗に見えるかもしれませんが、 ループの中で無駄な処理を何度も繰り返すことになる、 という点では前者の方が無駄はなくてよいコード、ということになります。

どちらを採用するかは、ある程度は趣味の問題ですが、 とりあえずは最初のコードの方を採用することにします。

なお (2) の部分は、この時点で出力を行なうかわりに 全体を一つの配列に保存しておいて、END ブロックでまとめて出力する、 という方法もあります。 このようにすると、全体の項目数を知ることもできますし、 全体を逆順に出力することも可能になりますので、 今回もそのような方向で設計することにします。


次へ: 6 行の分割 上へ: AWK による HTML ファイルの整形 その 2 前へ: 4 タイトルと日付の取得
竹野茂治@新潟工科大学
2006年9月5日