Python + lxmlを使用した解析の基本

こんにちは、読者の皆様。

今日の記事では、Python用のlxmlライブラリーを使用してHTMLページマークアップを解析する基本を紹介します。

つまり、 lxmlは、PythonでXMLおよびHTMLマークアップを処理するための高速で柔軟なライブラリです。 さらに、ドキュメント要素をツリーに分解する機能もあります。 記事では、実際にそのアプリケーションがどれほどシンプルであるかを示します。





ターゲット選択の解析



なぜなら 私はスポーツ、特にBJJに積極的に関与しています。世界のMMAトーナメントのすべてのトーナメントで痛みを伴うレセプションの統計を見たいと思いました。

ドローンを検索すると、主要な国際的な総合格闘技トーナメントに関するすべての公式統計のあるサイトに移動しました。 唯一の障害は、私たちに関する情報が分析のために不快な形で提示されたことです。 これは、トーナメントの結果が別のページにあるという事実によるものです。 さらに、トーナメントの日付もその名前とともに、別のページの別のページに配置されます。

トーナメントに関するすべての情報を分析に適した1つのテーブルにまとめるために、以下に説明するパーサーを作成することにしました。



パーサー操作アルゴリズム



まず、パーサーアルゴリズムを扱います。 次のようになります。

  1. 基礎として、すべてのトーナメントとその日付が記載されたテーブルを見てみましょう。

    これで

    住所
  2. このページのデータを次の列を使用してデータセットに入力します。
  3. トーナメント
  4. 説明リンク
  5. 日付
  6. 各セットエントリ(トーナメントごと)について、フィールド全体に移行します

    [説明へのリンク] 、戦闘に関する情報
  7. トーナメントのすべての戦いに関する情報を記録します
  8. 戦闘に関する情報を含むデータセットに、トーナメントの日付を追加します

    セット(2)


アルゴリズムの準備ができており、その実装に進むことができます



lxmlを使い始める



動作させるには、 lxmlおよびpandasモジュールが必要です。 それらをプログラムにアップロードします。



import lxml.html as html from pandas import DataFrame
      
      





さらに解析しやすいように、メインドメインを別の変数に配置します。



 main_domain_stat = 'http://hosteddb.fightmetric.com'
      
      





次に、解析用のオブジェクトを取得しましょう。 これは、 parse()関数を使用して実行できます。



 page = html.parse('%s/events/index/date/desc/1/all' % (main_domain_stat))
      
      





次に、指定されたテーブルをHTMLエディターで開き、その構造を調べます。 最も興味深いのは、 events_table data_table row_is_link



クラスのブロックです。 必要なデータを含むテーブルが含まれています。 このブロックは次のように取得できます。



 e = page.getroot().\ find_class('events_table data_table row_is_link').\ pop()
      
      





このコードが何をするのか見てみましょう。

最初に、 getroot()関数を使用して、ドキュメントのルート要素を取得します(これは、ドキュメントでの以降の作業に必要です)。

次に、 find_class()関数を使用して、指定されたクラスを持つすべての要素を検索します。 関数の結果として、そのような要素のリストを取得します。 なぜなら ページのHTMLコードを視覚的に分析した結果、この基準に従って1つの要素のみが適切であることがわかり、 pop()関数を使用してリストから要素を抽出します。

ここで、以前に取得したdiv 'aからテーブルを取得する必要があります。 これを行うには、 getchildren()メソッドを使用します。このメソッドは、現在の要素の下線付きオブジェクトのリストを返します。 そして

そのようなオブジェクトは1つしかないため、リストからこのオブジェクトを抽出します。



 t = e.getchildren().pop()
      
      





これで、変数tには、必要な情報を含むテーブルが含まれます。 これで、2つの補助データフレームを受け取りそれらを組み合わせて、トーナメントのデータとその日付および結果へのリンクを受け取ります。

最初のセットには、トーナメントのすべての名前とサイト上のページへのリンクが含まれます。 これはiterlinks()イテレータを使用して簡単に実行できます。このイテレータはタグ(, ,

, )




(, ,

, )




(, ,

, )




指定された要素内。 実際、このタプルから、リンクのアドレスとそのテキストが必要です。

リンクテストは、対応する要素の.textプロパティにアクセスすることで取得できます 。 コードは次のようになります。



 events_tabl = DataFrame([{'EVENT':i[0].text, 'LINK':i[2]} for i in t.iterlinks()][5:])
      
      





熱心な読者は、サイクルで最初の5つのエントリを除外していることに気付くでしょう。 フィールドヘッダーなど、必要のない情報が含まれているため、それらを削除しました。

それで、リンクを取得しました。 これで、トーナメントの日付とともに2つのサブセットのデータを取得できます。 これは次のように実行できます。



 event_date = DataFrame([{'EVENT': evt.getchildren()[0].text_content(), 'DATE':evt.getchildren()[1].text_content()} for evt in t][2:])
      
      





上記のコードでは、テーブルtのすべての行( trタグ)を調べます。 次に、各行について、子列( td要素)のリストを取得します。 そして、 text_contentメソッドを使用して、1列目と2列目に記録された情報を取得します。このメソッドは、この列のすべての子のテキストから文字列を返します。

text_contentメソッドの仕組みを理解するために、小さな例を示します。 このようなドキュメント構造<tr> <td> <span> text </ span> <span> text </ span>があるとします。 したがって、 text_contentメソッドは文字列textをtextに返し、 textメソッドは何も返さないか、単にtextを返します



データの2つのサブセットができたので、それらを最終セットに結合します。



 sum_event_link = events_tabl.set_index('EVENT').join(event_date.set_index('EVENT')).reset_index()
      
      





ここでは、最初にセットのインデックスを指定し、次にそれらを組み合わせて、最終セットのインデックスをリセットします。 これらの操作の詳細については、私の過去の記事をご覧ください。 安全のために、受信したデータフレームをテキストファイルにアンロードします。



 sum_event_link.to_csv('..\DataSets\ufc\list_ufc_events.csv',';',index=False)
      
      





UFCシングルイベントイベントハンドラー



便利な形式のトーナメントのリストを含むページをアンロードしました。 競争の結果ページを扱う時が来ました。 たとえば、最後のトーナメントに参加して、ページのHTMLコードを確認します。

必要な情報は、 data_table row_is_linkクラスの要素に含まれていることに気付くかもしれません。 一般に、解析プロセスは上記のプロセスと似ていますが、1つの例外があります。結果のテーブルは完全に正しいわけではありません。

その矛盾は、各戦闘機に対して個別の行が入力されることであり、これは分析には便利ではありません。 結果を解析するときにこの不便さを取り除くために、奇数行でのみイテレータを使用することにしました。 偶数は、現在の奇数行から計算されます。

したがって、一度に数行を処理し、それらを1行にラップします。 コードは次のようになります。



 all_fights = [] for i in sum_event_link.itertuples(): page_event = html.parse('%s/%s' % (main_domain_stat,active_event_link)) main_code = page_event.getroot() figth_event_tbl = main_code.find_class('data_table row_is_link').pop()[1:] for figther_num in xrange(len(figth_event_tbl)): if not figther_num % 2: all_fights.append( {'FIGHTER_WIN': figth_event_tbl[figther_num][2].text_content().lstrip().rstrip(), 'FIGHTER_LOSE': figth_event_tbl[figther_num+1][1].text_content().lstrip().rstrip(), 'METHOD': figth_event_tbl[figther_num][8].text_content().lstrip().rstrip(), 'METHOD_DESC': figth_event_tbl[figther_num+1][7].text_content().lstrip().rstrip(), 'ROUND': figth_event_tbl[figther_num][9].text_content().lstrip().rstrip(), 'TIME': figth_event_tbl[figther_num][10].text_content().lstrip().rstrip(), 'EVENT_NAME': i[1]} ) history_stat = DataFrame(all_fights)
      
      





試合ごとに、トーナメントの名前がさらに記録されることに気付くかもしれません。 これは、試合の日付を決定するために必要です。

次に、結果をファイルに保存します。



 history_stat.to_csv('..\DataSets\ufc\list_all_fights.csv',';',index=False)
      
      





結果を見てみましょう:



 history_stat.head()
      
      





EVENT_NAME FIGHTER_LOSE FIGHTER_WIN 方法 METHOD_DESC ラウンド 時間
0 UFCファイトナイト38:ショーグンvs. ヘンダーソン ロビーローラー ジョニー・ヘンドリックス U. DEC ナン 5 午前5時
1 UFCファイトナイト38:ショーグンvs. ヘンダーソン カルロス・コンディット タイロン・ウッドリー KO / TKO 膝の怪我 2 午前2時
2 UFCファイトナイト38:ショーグンvs. ヘンダーソン ディエゴ・サンチェス マイルズ審査員 U. DEC ナン 3 午前5時
3 UFCファイトナイト38:ショーグンvs. ヘンダーソン ジェイクシールド ヘクターロンバード U. DEC ナン 3 午前5時
4 UFCファイトナイト38:ショーグンvs. ヘンダーソン ニキータ・クリロフ オヴィンス・サン・プレウ SUB その他-チョーク 1 1:29


戦いの日付を引き出​​し、最終ファイルをアップロードするだけです。



 all_statistics = history_stat.set_index('EVENT_NAME').join(sum_event_link.set_index('EVENT').DATE) all_statistics.to_csv('..\DataSets\ufc\statistics_ufc.csv',';', index_label='EVENT')
      
      





おわりに



この記事では、XMLおよびHTMLマークアップの解析を目的としたlxmlライブラリーの操作の基本を説明しようとしました。 この記事で指定されているコードは、最適性を主張していませんが、割り当てられたタスクを正しく実行します。

上記のプログラムからわかるように、ライブラリを操作するプロセスは非常に簡単で、必要なコードをすばやく作成するのに役立ちます。 上記の関数とメソッドに加えて、他にも必要なものがあります。



All Articles