目次


魅力的なPython: 私の最初のWebベース・フィルター・プロキシー

Txt2Htmlを使用してテキストをHTMLに変換する

Comments

このdeveloperWorks シリーズの記事を書くにあたり、私は、どういうフォーマットで書くのが最もよいのか、頭を悩ましました。ワード・プロセッサーのフォーマットは固有のものであり、フォーマット間の変換が面倒で完全に行えない傾向があます (また、これらのフォーマットを使用すると、オープン・ソースの精神に反して固有なツールに縛られてします)。HTML はかなり中立的です (そしておそらく、あなたがこの記事を読むのに使用している形式でしょう) が、付加されるタグがタイプミスを誘いがちです (あるいは、HTML 拡張エディターを使用する必要があります)。DocBook は、多くのターゲット・フォーマットに変換可能な興味深い XML フォーマットであり、科学記事 (または書籍) に適したセマンティクスを備えていますが、HTML と同じように、著述の際に神経を使わなければならないタグがたくさんあります。LaTeX は印刷の体裁が洗練されている点ではすばらしいのですが、やはりたくさんのタグが付いています。それに、このような記事の場合、印刷の体裁を良くする必要はありません。

文章の書きやすさの点では (また、特にプラットフォームとツールの中立性という点では)、プレーン ASCII を外すことはできません。ただしインターネット (特に Usenet) は、完全なプレーン・テキストではない、「スマート ASCII」文書 (参考文献を参照) という非公式の標準の開発を促してきました。「スマート ASCII」は、わずかな追加セマンティック・コンテンツおよびコンテキストを、テキスト表示中では「中立」に見えるような方法で追加するだけのものです。E メール、ニュースグループ・ポスト、FAQ、プロジェクトの README、およびその他の電子文書には、多くの場合、強調される語の前後のアスタリスク、表題を囲む下線、テキストの関係を示す垂直および水平の空白文字、選択的な ALLCAPS などの、いくつかの印刷/セマンティックの要素が含まれます。Project Gutenberg (参考文献を参照) は、多大な努力によって多くの知恵を投入し、フォーマットに関する独自の考え方をまとめ上げ、偉大な書物を長期間にわたって保存し、配布するためには、「スマート ASCII」が最適であるとの結論に達しました。私の書くものがこうした文学上の古典のような長い寿命を保つことはないでしょうが、「スマート ASCII」で書くことにし、他のフォーマットへの変換は、簡便な Python スクリプトによって自動化することにしました。

Txt2Html の概要

Txt2Html は、最初、その名前が示すように簡単なファイル・コンバーターとして出発しました。しかしインターネットにより、このツールに対していくつかの明らかな機能強化が促されました。「html 化」形式で表示したいと思うような文書の多くが、http: リンクまたは ftp: リンクの終端で生き残っていますので、このツールは、これらのリモート文書をすなおに (ダウンロード/変換/表示のサイクルを必要としないで) 処理できる必要があります。また、変換のターゲットは結局のところ HTML なのですから、一般にターゲットで行いたいことは、それを Web ブラウザーで表示するということになります。

こうしたことを取り入れて、Txt2Html は「Web ベースのフィルター・プロキシー」として登場してきました。「専門ぶり用語に完全準拠」という風変わりな形容もされています。これらの意見をまとめると、あるプログラムがユーザーに代わって Web ページ (またはその他のリソース) を読み取り、その内容をなんらかの方法で通知し、元のページよりも (少なくともある特定の目的との関係では)優れた 形でユーザーに提示する、という考えになります。このようなツールの好例として、Babelfish 変換サービス (参考文献を参照) があります。Babelfish に URL を通した後で表示される Web ページは、元のページとかなり良く似ていますが、理解できない言語の代わりに、ユーザーに読める言語で表示を行うという、ありがたい機能が備わっています。ある意味で、検索で見つけるページの概要をほとんど表示しないサーチ・エンジンは、同じことを行っています。しかしそれらのサーチ・エンジンは (設計上) ターゲット・ページのフォーマットと外観を大幅に変えたり、大幅に省略してしまったりします。Txt2Html は Babelfish に比べて恣意的な部分がはるかに少ないのですが、概念上は、ほとんど同じようなことをしています。その他の例については参考文献を参照してください。中には、ずいぶんユーモラスな例もあります。

Txt2Html の特徴は、多くの Web 指向による Python 使用法に共通した、いくつかのプログラミング手法を使用していることです。この記事では、これらの手法を紹介し、コーディング手法に関する若干のアドバイスを提供し、およびいくつかの Python モジュールを検討します。注: Txt2Html における実際のモジュールは、別の人が書いたモジュールの名前との競合を避けるために、dmTxt2Html と呼ぶことにします。

cgi モジュールの使用

Python の cgi モジュール (標準ディストリビューションに含まれます) は、Pythonで「Common Gateway Interface」アプリケーションを開発しているあらゆる人にとって、天からの贈り物です。これが無くても CGI を作成することは可能ですが、そうしたいと思う人はいないでしょう。

最も一般的には、CGI アプリケーションとの対話は HTML フォームで行います。ユーザーの仕様に従って処置を行うことを CGI に依頼するために、フォームに記入します。たとえば、Txt2Html ドキュメンテーションは、次の例を使用して HTML フォームを呼び出します (Txt2Html 自体によって生成される HTML フォームはこれよりもやや複雑で、変更されることがありますが、この例は、ユーザー自身の Web ページからでも完全に機能します)。

'Txt2Html' を呼び出す HTML フォーム
	<form method="get" action="http://gnosis.cx/cgi/txt2html.cgi">
	URL: <input type="text" name="source" size=40>
	<input type="submit" name="go" value="Display!">
	</form>

HTML フォームには多数の入力フィールドを組み込むことができ、それらのフィールドには、いくつかのタイプ (テキスト、チェック・ボックス、選択リスト、ラジオ・ボタンなど) のうちの 1 つを割り当てることができます。. HTML に関する優れた解説書は、初心者にとって、カスタム HTML フォームを作成するための参考になります。ここで注意すべきことは、各フィールドには名前属性があり、その名前が、後で CGI スクリプト内でそのフィールドを参照するために使用されるということです。また、フォームは "get" および "post" の 2 つのメソッド属性のうちの 1 つを備えることがある、ということも覚えておく価値があります。基本的な相違点は、"get" が URL 内に照会情報を組み込むこと、そしてこのメソッドのほうが、ユーザーにとって、後で再利用するために特定の照会を保管しやすいというです。一方、ユーザーに照会内容を保管させたくない場合には、"post" メソッドを使用してください。

上記のフォームで呼び出される Python スクリプトは、呼び出し側フォームのソートを容易にするために import cgi を行います。このモジュールが行うことの 1 つとして、"get" モジュールと "post" モジュールの相違に関する詳細を CGI スクリプトに分からないようにすることがあります。呼び出しが行われる時点では、CGI の作成者はこの相違を気にする必要がありません。CGI モジュールが行う主要な作業は、呼び出し側 HTML フォーム内のすべてのフィールドを辞書に似た方法で扱うことです。完全な Python 辞書というわけではありませんが、使いやすさの点では十分それに近いものです。

Python [cgi] モジュールの使用
import cgi, sys
cfg_dict = {'target': '<STDOUT>'}
sys.stderr = sys.stdout
form = cgi.FieldStorage()
if form.has_key('source'):
     cfg_dict['source'] = form['source'].value

上記の数行の中には、注意すべき詳細事項がいくつかあります。sys.stderr = sys.stdout の設定は、ちょっとしたテクニックです。こうすることにより、私たちのスクリプトがトラップされないエラーに遭遇した場合、トレースバックにより、クライアント・ブラウザーの表示に戻されます。これにより、CGI アプリケーションのデバッグに要する時間が大幅に短縮されます。しかし、これをユーザーに見せたくない場合があります (あるいは、問題の詳細を報告しようとするユーザーには見せたい場合もあります)。次に、HTML フォームの中の値を辞書に類似したフォーム・インスタンスに読み込みます。本当の Python 辞書と同じように、フォームには .has_key() メソッドがあります。しかし、本物の Python 辞書とは異なり、キー内の値を実際に取り出すためには、そのキーの .value 属性を調べる必要があります。

ここからは、HTML フォーム内の値はすべてプレーン Python 変数になり、他の Python プログラムの場合と同様に扱えるようになります。

urllib モジュールの使用

他のほとんどの Python モジュールと同じように、urllib は 一連の複雑な事柄を丸ごと、明白で単純な方法で発生するようにします。urllib 内の urlopen() 関数は、リモート・リソースを (それが http: であっても、ftp: であっても、あるいは gopher: であっても)、まるでローカル・ファイルのように扱います。urlopen() を使用してリモート (疑似) ファイル・オブジェクトを取り込むと、ローカル (読み取り専用) ファイルのファイル・オブジェクトですべてのことが行えるようになります。

Python [urllib] モジュールの使用
from urllib import urlopen
import string
source = cfg_dict['source']
if source == '<STDIN>':
     fhin = sys.stdin
else:
     try:
          fhin = urlopen(source)
     except:
          ErrReport(source+' could not be opened!', cfg_dict)
          return
doc = ''
for line in fhin.readlines():   # Need to normalize line endings!
     doc = doc+string.rstrip(line)+'\n'

私が体験した小さな問題の 1 つに、リソースを生成したプラットフォームと自分のプラットフォームで使用されている行の終わり規則によっては、結果のテキストに奇妙なことが生じる、ということがあります (これは、urllib のバグのようです)。この問題の解決策は、上記のコード内で小さな .readlines() ループを実行することです。こうすることにより、ソース・リソースがどのようになっていたとしても (おそらく、それなりの理由があってのことでしょうが)、実行しているプラットフォームに適した行の終わり規則を含むストリングが得られます。

re モジュールの使用

正規表現について書くべきことは、この記事には収まりきらないほど、たくさんあります。このトピックについて広く読まれている参考資料を参考文献にリストしてあります。re モジュールは、ソース・テキスト内のさまざまなテキスト・パターンを識別するために、Txt2Html でかなり広く使用されています。少々複雑な例を示しておいても、無駄にはならないでしょう。

Python [re] モジュールの使用
import re
def
 
URLify(txt):
    txt = re.sub('((?:http|ftp|gopher|file)://(?:[^ \n\r<\)]+))(\s)',	 
                 '<a href="\\1">\\1</a>\\2', txt)
    return txt

URLify() は、その名前から想像できるようなことをしてくれる、気の利いた関数です。これは、「スマート ASCII」ファイル内に URL のようなものがある場合、HTML 出力にある同じ URL への実際のホット・リンクに変換されます。re.sub() がどのようなことをするのか、見ることにしましょう。最初に、最も広い意味におけるこの関数の目的は、「最初のパターン内にあるものを突き合わせ、それを、操作対象のストリングとして 3 番目の引数を使用して、2 番目のパターンによって置き換える」ことです。しかしこの説明では string.replace() とあまり異なるところはありません。

最初のパターンにはいくつかの要素が含まれます。まず、括弧に注目してください。最高レベルの括弧は 2 つのペアからなり、かなり込み入ったものが入った括弧のペアの後に (\s) が付いています。括弧のセットは、置換パターンの一部となる可能性のある「副次式」と突き合わされます。2 番目の副次式 (\s) は、「空白文字を突き合わせ、一致したものを参照できるようにする」ことを意味しています。そこで、最初の副次式を見ることにしましょう。

Python 正規表現は、いくつかの独自のトリックを備えています。こうしたトリックの 1 つとして、副次式の先頭の ?: 演算子があります。これは、「サブパターンを突き合わせるが、差し戻す参照にはその一致内容を含めない」ということを意味しています。それでは、その副次式を調べてみましょう。

((?:http|ftp|gopher|file)://(?:[^ \n\r<\)]+)).

最初に、この副次式自体が 2 つの子副次式からなり、中間部に、どの子副次式の一部でもないものが含まれていることに注意してください。ただし、それぞれの子が?: で始まっています。これは、それらの子が突き合わされるが、参照の対象としては考慮されない、ということを意味します。こうした「非参照」子副次式は、「httpまたは ftpまたは ... のような外見のものを突き合わせる」ことを指示しています。次に、:// という短いストリングがあります。これは、それとまったく同じ外観のものを突き合わせることを意味します (簡単でしょう?)。最後に、2 番目の子副次式があります。これは、「参照するな」と言っている演算子を除き、大括弧内のものと正符号からなります。

正規表現内では、大括弧は「その大括弧内のすべての文字を突き合わせる」ことを意味しています。ただし、先頭文字が脱字記号 (^) である場合には、この意味が逆になり、「次の文字にない ものを突き合わせる」ことを意味します。そこで、スペース、CR、LF、"<"、または ")"以外 のものを探すことになります (正規表現にとって特別な意味がある文字は、その前に "\" を付けることによって「エスケープ」できる、ということにも注意してください)。最後の正符号は、「最後のものを 1 つまたは複数個突き合わせる」ことを意味します (アスタリスクは、「ゼロ以上突き合わせる」ことを意味し、疑問符 は「ゼロまたは 1 つ突き合わせる」ことを意味します)。

この正規表現については、述べることがたくさんありますが、何回か検討するうちに、これが URL のあるべき姿であることが分かるはずです。

次は置き換え部分です。これは、もっと単純です。\\1 および\\2 (あるいは、必要があれば\\3\\4、その他) が、ここで説明される「差し戻す参照」です。\\1 (または \\2) は、突き合わせ式の最初 (または2 番目) の副次式によって突き合わせされたパターンを表します。置き換え部分のうちのそれ以外のすべての部分は、見てのとおり、HTML コードとして簡単に認識できる文字です。少し分かりにくいのは、私たちがわざわざ \\2 を突き合わせていることです。上で見ると、単なる空白文字です。「どうしてわざわざそんなことを? どうしてリテラルとしてスペースを挿入しないの?」と疑問に思うことでしょう。もっともな疑問です。それに、HTML の場合には、このようなことは本当には必要 でないのです。しかし美学的な見地からすると、HTML 出力は、できるかぎり、HTML マークアップ以前のソース・テキスト・ファイルと同じ形のままにするほうが良いのです。特に、行の切れ目は行の切れ目のまま、またスペースはスペースのまま (そして、タブはタブのまま) 維持することにしましょう。


ダウンロード可能なリソース


関連トピック

  • Txt2Html を入手または使用するためには、著者の Web サイト で探してください。(デフォルトで) プロキシーされるすべてのページの先頭に付いているナビゲーション・バーに、すべてのソース・ファイルをダウンロードするためのリンクが含まれています。
  • この記事を「スマート ASCII」テキストとして表示できます。
  • 「Project Gutenberg」のホーム・ページを参照してください。
  • Ka-Ping Yee のリストおよび「メディエーター (mediators)」についての論考 (および、「プロキシー」という用語ではその概念が表せないと彼が考えている理由) を参照してください。
  • 「Babelfish」のホーム・ページ を調べて、ある言語から別の言語に Web ページを変換してみてください。
  • 「Malkovich」 Web ベースのフィルター・プロキシーを参照してください (これについては、説明のしようがありません。ご自分で確かめてください)。.
  • Anonymizer を調べてください。これは、個人の ID に関する情報 (cookie だけでなく IP アドレス、ブラウザーのバージョン、OS、またはその他の、ID と相関している可能性のある情報) が漏れないようにして Web をブラウズしたいと考えている人にとって、ほんとうに役に立つフィルター・プロキシーです。
  • Mastering Regular Expressions by Jeffery E. F. Friedl (O'Reilly, 1997) は、正規表現に関する、かなり標準的で信頼のおける参考書です。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux
ArticleID=230455
ArticleTitle=魅力的なPython: 私の最初のWebベース・フィルター・プロキシー
publish-date=07012000