こんにちは、読者の皆様。
今日の記事では、Python用の
lxmlライブラリーを使用してHTMLページマークアップを解析する基本を紹介します。
つまり、
lxmlは、Pythonで
XMLおよび
HTMLマークアップを処理
するための高速で柔軟なライブラリです。 さらに、ドキュメント要素をツリーに分解する機能もあります。 記事では、実際にそのアプリケーションがどれほどシンプルであるかを示します。
ターゲット選択の解析
なぜなら 私はスポーツ、特に
BJJに積極的に関与しています。世界のMMAトーナメントのすべてのトーナメントで痛みを伴うレセプションの統計を見たいと思いました。
ドローンを検索すると、主要な国際的な総合格闘技トーナメントに関するすべての公式統計のある
サイトに移動しました。 唯一の障害は、私たちに関する情報が分析のために不快な形で提示されたことです。 これは、トーナメントの結果が別のページにあるという事実によるものです。 さらに、トーナメントの日付もその名前とともに、別のページの別のページに配置されます。
トーナメントに関するすべての情報を分析に適した1つのテーブルにまとめるために、以下に説明する
パーサーを作成することにしました。
パーサー操作アルゴリズム
まず、パーサーアルゴリズムを扱います。 次のようになります。
- 基礎として、すべてのトーナメントとその日付が記載されたテーブルを見てみましょう。
これで
住所 - このページのデータを次の列を使用してデータセットに入力します。
- トーナメント
- 説明リンク
- 日付
- 各セットエントリ(トーナメントごと)について、フィールド全体に移行します
[説明へのリンク] 、戦闘に関する情報 - トーナメントのすべての戦いに関する情報を記録します
- 戦闘に関する情報を含むデータセットに、トーナメントの日付を追加します
セット(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ライブラリーの
操作の基本を説明しようとしました。 この記事で指定されているコードは、最適性を主張していませんが、割り当てられたタスクを正しく実行します。
上記のプログラムからわかるように、ライブラリを操作するプロセスは非常に簡単で、必要なコードをすばやく作成するのに役立ちます。 上記の関数とメソッドに加えて、他にも必要なものがあります。