ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

diffbotのようにコンテンツ抽出したい

Webコンテンツ抽出におけるvision-based手法とは、Webコンテンツのスクリーンショットの画像を用いて、ターゲットのコンテンツを自動的に抽出する手法です。ここでは、TextMapsというプロジェクトを見つけたので、その理論の概要と、デモの実行を行います。

モチベーション

Webコンテンツ抽出のvision-based手法を探していた目的は、diffbotという有料のコンテンツ抽出APIに似たことをしたいと思ったからです。diffbotの技術的詳細は公開されていませんが、Quoraでは以下の質問・回答があります:

What is the algorithm used by diffbot for extracting web data? - https://www.quora.com/What-is-the-algorithm-used-by-Diffbot-for-extracting-web-data

Our approach relies on computer-vision techniques (in conjunction with machine learning, NLP and markup inspection) as the primary engine in identifying the proper content to extract from a page. What this means: when analyzing a web document, our system renders the page fully, as it appears in a browser -- including images, CSS, even Ajax-delivered content -- and then analyzes its visual layout.

訳: 我々のアプローチはコンピュータビジョンテクニック(機械学習NLPマークアップを合わせた) をページから適切なコンテンツを抽出する主要なエンジンとして使っています。どういう意味か: ウェブドキュメントを解析するとき、システムはページ全体をブラウザに見える形態でレンダリングします(イメージ, CSS, Ajaxでさえも)。そして、その視覚的なレイアウトを解析します。

これを知り、視覚ベースのウェブコンテンツ抽出技術を探しましたが、有料の論文やらばかりでオープンソースはなかなか見つかりませんでした。TextMapsは、見つけた唯一のオープンソースです。

実は、TextMapsの詳細も、有料の論文でまとめられています。しかし、以下のプレゼン動画を見つけました:

https://youtu.be/hSIKsjrD46s https://youtu.be/hSIKsjrD46s

ここで使われている資料を以下で発見しました。 http://www.mlmu.cz/wp-content/uploads/2016/06/MLMU-Web-Page-Information-Extraction.pdf

TextMapsの理論

TextMapsは3つの入力が必要です。Webページのスクリーンショット、候補コンテンツのテキストマッピング、そして候補要素のポジションです。

テキストマッピングとは、ハッシュ化された候補テキストを圧縮された次元の画像に写像するための行列です。 Screenshot_2018-09-15_13-35-56.png

これは、特徴量に候補要素のポジション情報を効果的に利用するための特徴量設計だと考えることができます。候補要素とは、ルールベースで抽出されたWeb内のテキストのスクリーンショット内の位置情報(ピクセル)です。

これは、分類問題として捉えることができ、入力した候補要素がどのクラスの要素に属するかを意味します。例えば、商品情報をページから取得したい場合、クラスは「商品名」「商品画像」「商品価格」「取得しないコンテンツ」のどれかになります。

つまりネットワークは以下のようになります。 Screenshot_2018-09-15_13-35-07.png

デモを動かす

TextMapsは以下で公開されています。 https://github.com/gogartom/TextMaps

デモを動かすためには6つのステップを取ります。

  1. caffe-textmapsをビルドする。https://github.com/gogartom/caffe-textmaps
  2. phantomjsをダウンロードする。http://phantomjs.org/
  3. PYTHONPATHに、caffe-textmaps/python/caffeを追加する。
  4. PATHにphantonjsのバイナリを格納したパスを追加する。
  5. qtをインストール。
  6. python demo.py --url ターゲットURL を実行する。

実行結果

デモは、商品ページを対象とするので、アマゾンの商品ページを対象に試しました。

python demo.py --url http://a.co/d/8ELQYof

68747470733a2f2f692e696d6775722e636f6d2f3244646d4d4d472e676966.gif

矩形で囲われた部分が予測された部分です。

矩形位置からコンテンツを取得する

矩形位置からコンテンツを取得するためには、TextMapsがどのようにして入力データをダウンロードし、保存しているかを知る必要があります。

TextMapsは、download_page.jsというスクリプトでコンテンツを取得し、それを screenshot.jpegとdom.jsonという2つの出力に出します。

dom.jsonでは、すでに候補要素の位置とコンテンツの情報が格納されているため、矩形位置とdom.json内のポジションが対応しているものを探し、コンテンツを取得すれば良いことになります。

まず、demo.py内のshow関数を書き換え、予測された位置を保存するようにします。

def show(net, position_maps, temp_dir):
    pred_boxes = []
.
.
.
    for cls in range(1,4):
        ind = max_boxes[cls]
        print probs[ind]
    
        pred_box = boxes[ind,:]
        pred_boxes.append(pred_box)
.
.
.
    plt.show()
    with open(join(temp_dir, "pred_boxes.json", "w") as f:
        json.dump([p.tolist() for p in pred_boxes], f)
    return pred_boxes

次に、dom.jsonと予測位置(pred_boxes)を使って、深さ優先探索でコンテンツを探します。

with open("tmp/dom.json") as f:
    data = eval(f.read())

with open("tmp/pred_boxes.json") as f:
    preds = eval(f.read())

def search_content(parent, preds):
    results = []
    stack = [parent['childNodes']]
    while(True):
        if len(stack) == 0 or len(preds) == len(results):
            break
        children = stack.pop(0)
        for child in children:
            if "name" in child and child["name"] in ["#text", "IMG"]:
                for pred in preds:
                    coor_flag = True
                    for p,q in zip(child['position'], pred):
                        if int(p) != int(q):
                            coor_flag = False
                            break
                    if coor_flag:
                        results.append(child)
            if "childNodes" in child:
                stack.append(child['childNodes'])
    return results

results = search_content(parent)
print(results)

出力は以下です。

[{'name': '#text',
  'position': [1198, 331, 1242, 352],
  'type': 3,
  'value': '$9.60'},
 {'attrs': {'alt': '',
   'class': 'a-dynamic-image image-stretch-vertical frontImage',
   'data-a-dynamic-image': '{"https://images-na.ssl-images-amazon.com/images/I/41AWqUSdKTL._SY344_BO1,204,203,200_.jpg":[225,346],"https://images-na.ssl-images-amazon.com/images/I/41AWqUSdKTL._SX322_BO1,204,203,200_.jpg":[324,499]}',
   'id': 'imgBlkFront',
   'onload': "this.onload='';setCSMReq('af');if(typeof addlongPoleTag === 'function'){ addlongPoleTag('af','desktop-image-atf-marker');};setCSMReq('cf');",
   'src': 'https://images-na.ssl-images-amazon.com/images/I/41AWqUSdKTL._SX322_BO1,204,203,200_.jpg',
   'style': 'max-width: 225px; max-height: 346px; position: absolute; top: 0px; left: 0px;'},
  'computed_style': {'background-image': 'none',
   'content': '',
   'display': 'block',
   'opacity': '1',
   'visibility': 'visible',
   'z-index': 'auto'},
  'name': 'IMG',
  'position': [36, 316, 261, 662],
  'type': 1},
 {'name': '#text',
  'position': [328, 288, 765, 313],
  'type': 3,
  'value': 'Crazy Rich Asians (Crazy Rich Asians Trilogy)'}]

参考

[0] https://github.com/gogartom/TextMaps [1] http://www.mlmu.cz/wp-content/uploads/2016/06/MLMU-Web-Page-Information-Extraction.pdf [2] https://youtu.be/hSIKsjrD46s [3] https://www.quora.com/What-is-the-algorithm-used-by-Diffbot-for-extracting-web-data