Python で XML を解析 (ストリーミング) するためのノンブロッキング メソッド
-
12-09-2019 - |
質問
ソケット経由で受信する XML ドキュメントを解析してオンザフライで反応する必要があります (つまり、部分ツリーを解析する)。私が望んでいるのは、ノンブロッキングの方法で、より多くのデータが入ってくるのを待っている間に(スレッド化せずに)他のことをできるようにすることです。
iterparse のようなものは、読み取りバッファーが空のときに反復を終了できれば理想的です。例:
context = iterparse(imaginary_socket_file_wrapper)
while 1:
for event, elem in context:
process_elem(elem)
# iteration of context finishes when socket has no more data
do_other_stuff()
time.sleep(0.1)
SAX も選択肢になると思いますが、私のニーズには iterparse の方が簡単だと思います。何か案は?
アップデート:
スレッドを使用するのは問題ありませんが、回避したいと考えていたレベルの複雑さが生じます。ノンブロッキング呼び出しがそのための良い方法であると考えていましたが、XML の解析が複雑になることがわかりました。
解決
iterparse ソースに飛び込むことで、解決策が得られました。XML ツリーをオンザフライで構築し、終了タグの後の要素を処理する簡単な例を次に示します。
import xml.etree.ElementTree as etree
parser = etree.XMLTreeBuilder()
def end_tag_event(tag):
node = self.parser._end(tag)
print node
parser._parser.EndElementHandler = end_tag_event
def data_received(data):
parser.feed(data)
私の場合、ツイストからデータを供給することになりましたが、ノンブロッキングソケットでも動作するはずです。
他のヒント
これには、ノンブロッキング ネットワーク I/O とストリーム指向の XML パーサーという 2 つのコンポーネントがあると思います。
前者の場合は、ノンブロッキングのネットワーク フレームワークを選択するか、独自のソリューションを導入する必要があります。Twisted は確かに機能するでしょうが、私個人としては、制御フレームワークの反転を頭の中で理解するのは難しいと感じています。パーサーにフィードするために、コールバックで多くの状態を追跡する必要があるでしょう。このため、私は見つける傾向があります イベントレット プログラムするのが少し簡単で、この状況にはうまく適合すると思います。
基本的に、コードを記述できるようになります。 かのように ブロッキング ソケット呼び出し (通常のループやジェネレーターなど好きなものを使用) を使用していましたが、I/O 操作が行われるときに自動的に協調的なイールドを実行する別のコルーチン (「グリーンレット」) にそれを生成できる点が異なります。ブロックすることで、他のコルーチンの実行が可能になります。
これにより、コードが通常のブロッキング呼び出しのように構造化されているため、ストリーム指向のパーサーを使用することが再び簡単になります。これは、ソケットや他の I/O (たとえばパーサーなど) を直接処理しない多くのライブラリは、ノンブロッキングになるように特別に変更する必要がないことも意味します。ブロックされた場合、Eventlet はコルーチンを生成します。
確かにイベントレットは わずかに 魔法ですが、Twisted よりも学習曲線がはるかに簡単で、フレームワークに合わせてロジックを「裏返し」にする必要がないため、より単純なコードが得られることがわかりました。
スレッドを使用しない場合は、イベント ループを使用して、非ブロッキング ソケットをポーリングできます。
asyncore
は、そのようなもののための標準ライブラリモジュールです。 ツイスト は の Python 用の async ライブラリですが、複雑で、おそらくニーズにとっては少し重いと思われます。
あるいは、 multiprocessing
は非スレッドスレッドの代替手段ですが、2.6 を実行していないと思います。
いずれにせよ、スレッドや追加のプロセスを使用するか、同様に複雑な非同期マジックを編み込む必要があると思います。