2013年8月2日金曜日

[HTML5]XMLHttpRequestでウェブスクレイピング

Chromeアプリでウェブスクレイピングするには、クロスドメインに対応した XMLHttpRequest Level2を使います。
Chromeアプリでと書きましたが、XMLHttpRequestはHTML5仕様のため、実際にはChromeアプリに限定されるわけではありません。ですから、今から紹介するサンプルは、HTML5標準のウェブスクレイピング技術として参考になるかと思います。
下のサンプルは、「ヤフー天気予報」へアクセスしてページのタイトルを取得するだけの簡単なものです。

ファイル構成

  • manifest.json
  • background.js
  • index.html
  • app.js
  • 128.png
Chrome Extensions や Packaged Apps の典型的なファイル構成です。

manifest.json
{
  "name": "Web Scraping Sample App",
  "description": "Sample app for Web Scraping.",
  "version": "0.1",
  "app": {
    "background": {
      "scripts": ["background.js"]
    }
  },
  "icons": { "128": "128.png" },
  "permissions": [
    "http://*/*"
  ]
}
permissionsでワイルドカードを使っていますが、本来はちゃんとURLを指定すべきです。今回のサンプルアプリでいうなら、"http://weather.yahoo.co.jp/weather/jp/13/4410.html"です。

background.js
chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('index.html', {
    'bounds': {
      'width': 600,
      'height': 500
    }
  });
});

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<button id="req_btn">Request</button><br/>
Response:<br/>
<pre id="res_div"></pre>
<script src="app.js"></script>
</body>
</html>

app.js
URL = 'http://weather.yahoo.co.jp/weather/jp/13/4410.html';
METHOD = 'GET';
ASYNC = true;

// ボタンとかのDOM取得
var req_btn = document.querySelector('#req_btn');
var res_div = document.querySelector('#res_div');

/*
 * onclick 'Request' button
 */
req_btn.onclick = function(e) {
  var xhr = new XMLHttpRequest();
  xhr.open(METHOD, URL, ASYNC);
  // DOMのParseには 'document' を指定する
  xhr.responseType = 'document';
  xhr.onload = function(e) {
    if (this.status === 200) {
      console.log(this.responseXML.title);
      res_div.innerText = this.responseXML.title;
    }
    
  };
  xhr.send();
}

/**
 * func for onerror
 */
function onErr(err) {
  console.log('Error: ' + err.code);
}

これを実行して、"Request"ボタンを押すと、下のようなログが表示されます。

東京(東京)の天気 - Yahoo!天気・災害 app.js:20

ちゃんとTITLEタグの値が取得できてますね。


何気に重要なクロスドメインのお話

ウェブスクレイピング技術を使用する上でクロスドメインのことを少しだけ理解しておく必要があると思います。WikipediaのXMLHttpRequestのページに分り易い解説がありました。

クロスドメイン

基本的には、XMLHttpRequestは同一ドメインとしか通信ができないが、XMLHttpRequest Level 2には、異なるドメインと通信する機能が追加になっており、Firefox 3.5以降、Google Chrome、Safari 4以降で利用可能である。また、Internet Explorer 8には、非標準の XDomainRequest があり、似たようなことが可能である。Opera は10.7現在、未実装。

クロスドメインを認めるには、サーバー側のHTTPレスポンスヘッダーに追加が必要であり、例えば、Access-Control-Allow-Origin: *と書くと全てのドメインからのアクセスが許可される。Access-Control-Allow-Origin は Internet Explorer を含め全てのクロスドメイン対応ブラウザで使える。W3Cの仕様は、Cross-Origin Resource Sharing にて規定されている。

また、Firefox では POST などで、text/plain など以外の Content-Type をクロスドメインで送信する場合、OPTIONS を使いプレフライトが行われる[5]。

今回何気なく使ってみた XMLHttpRequest ですが、クライアント(ブラウザ)が対応していることは当然のこと、さらに一般的には(Chrome Appsを除いては)サーバサイドでも対応している必要があります。対応しているサーバとは、HTTPレスポンスヘッダで Access-Control-Allow-Origin を吐き出すサーバのことです。何かあったときにハマってしまわないためにも、この辺はちゃんと理解しておくことが必要ですね。

ただしChrome Appsは特別

Chrome Apps(Packaged Apps, Extensions)の場合は、manifest.jsonのpermissionsにURLを指定するだけでクロスドメイン非対応なサーバとも普通にクロスドメイン通信できます。

参考にしたサイト

XMLHttpRequest の HTML パース処理 | MDN
Embed Content - Google Chrome
Cross-Origin XMLHttpRequest - Google Chrome

0 件のコメント:

コメントを投稿