DartでWebページ取得

Dartを使ってWebページを取得、編集する方法をメモ書きしています。

※目次をクリックすると目次の下部にコンテンツが表示されます。

コマンドラインでWebページ取得
dartのコマンドラインアプリで、Webページ全体をテキストで取得して表示する簡単なサンプルを作成してみました。

import 'dart:io';
import 'dart:convert' show UTF8;

void main() {
  String result="";
  new HttpClient().getUrl(Uri.parse("http://localhost/index.html"))
    .then((HttpClientRequest request) {
      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        result = contents.toString();
        print(result);
      });
    });
}

HttpClientでリファラー、ユーザーエージェント、プロキシーの設定
リファラーとユーザーエージェントのリクエストヘッダーを設定し、Fiddlerなどのモニタツールでチェックするため、プロキシーの設定を行い、Webページを取得するサンプルを作成しました。
 
1)プロキシーの設定
 
HttpClient client = new HttpClient();
client.findProxy = (Uri uri) => “PROXY 127.0.0.1:8888;”;
 
2)リクエストヘッダーの設定

  client.getUrl(Uri.parse("http://localhost/index.html"))
    .then((HttpClientRequest request) {
      request.headers.set(HttpHeaders.USER_AGENT,
                       'Mozilla/5.0 (Windows NT 6.1; rv:28.0) Gecko/20100101 Firefox/28.0');
      request.headers.set(HttpHeaders.REFERER,
                       'http://localhost/bootstrap3/');

3)サンプルコード全体

import 'dart:io';
import 'dart:convert' show UTF8;

void main() {
  String result="";
  HttpClient client = new HttpClient();
  client.findProxy = (Uri uri) => "PROXY 127.0.0.1:8888;";
  client.getUrl(Uri.parse("http://localhost/index.html"))
    .then((HttpClientRequest request) {
      request.headers.set(HttpHeaders.USER_AGENT,
                       'Mozilla/5.0 (Windows NT 6.1; rv:28.0) Gecko/20100101 Firefox/28.0');
      request.headers.set(HttpHeaders.REFERER,
                       'http://localhost/bootstrap3/');

      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        result = contents.toString();
        print(result);
      });
    });
}

正規表現でtitleタグ抽出
取得したWebページから正規表現を使ってtitleタグを抽出するサンプルを作成します。
 
●RegExpクラスで正規表現、Matchクラスでリターン
 
タイトルタグは一つしかないので、firstMatchを使って最初にマッチしたものを取得します。
 
var reg = new RegExp(“<title>(.*)</title>”);
Match match = reg.firstMatch(web_page);
 
firstMatchメソッドのリターンは、Matchクラスとなります。パターンマッチングでグループ化した内容を抽出できるようになっていてgroup()メソッドを使って抽出します。マッチした内容の全体を取得する場合はgroup(0)を指定します。
 
String result = match.group(0);
print(result); //<title>(.*)</title>
result = match.group(1);
print(result); //(.*)
 
●サンプルコード全体

import 'dart:io';
import 'dart:convert' show UTF8;

void main() {
  String web_page = "";
  HttpClient client = new HttpClient();
  client.getUrl(Uri.parse("http://localhost/index.html"))
    .then((HttpClientRequest request) {
      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        web_page = contents.toString();
        var reg = new RegExp("<title>(.*)</title>");
        Match match = reg.firstMatch(web_page);
        String result = match.group(0);
        print(result);
        result = match.group(1);
        print(result);
      });
    });
}

正規表現でWebページ内のリンクタグを抽出
今回はページ内に複数あるリンクタグをすべて抽出し、URL部を表示するサンプルを作成しました。
 
●RegExpクラスで正規表現、Matchクラスでリターン
 
リンクタグは複数存在するので、allMatchesを使ってマッチしたすべてを表示します。
 
web_page = contents.toString();
var reg = new RegExp(““);
Iterable matches = reg.allMatches(web_page);
for (Match m in matches) {
 String match = m.group(1);
 print(match);
}
 
●サンプルコード全体

import 'dart:io';
import 'dart:convert' show UTF8;

void main() {
  String web_page = "";
  HttpClient client = new HttpClient();
  client.getUrl(Uri.parse("http://localhost/index.html"))
    .then((HttpClientRequest request) {
      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        web_page = contents.toString();
        var reg = new RegExp("");
        Iterable matches = reg.allMatches(web_page);
        for (Match m in matches) {
          String match = m.group(1);
          print(match);
        }
      });
    });
}

正規表現で不要な文字を置換して除去
Webページから正規表現で欲しい情報を抽出し、抽出した情報内の不要な文字を除去するサンプルを作成しました。
 
●取得対象の文字列
 
今回は、Webページのtdタグ内の数字情報を取得する場合を例にします。
 
<td>
 <p align=”right”>1,457</p>
</td>
 
tdタグ内には、改行、空白、pタグがあり、これは不要なので除去します。さらに数値内の三桁区切りのカンマもDB保存時に不要なので除去します。
 
●正規表現でtdタグ内の文字列を取得
 
改行を含む複数行の文字列を対象としていますが、\sだとうまくいかなかったので個別に改行コードと半角スペースを指定しました。
 
var reg = new RegExp(“<td>[ |\r\n|\n|\r]*(.*)[ |\r\n|\n|\r]*</td>”);
Iterable<Match> matches = reg.allMatches(web_page);
 
●不要な文字をreplaceAllメソッドで除去
 
replaceAllメソッドと正規表現を使って不要な文字を除去しました。

Iterable<Match> matches = reg.allMatches(web_page);
for (Match m in matches) {
  String match = m.group(1);
  match = match.replaceAll(new RegExp('<p align="right">|</p>|,'),"");
  print(match);
}

●サンプルコード全体

import 'dart:io';
import 'dart:convert' show UTF8;

void main() {
  String web_page = "";
  HttpClient client = new HttpClient();
  client.getUrl(Uri.parse("http://localhost/index.html"))
    .then((HttpClientRequest request) {
      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        web_page = contents.toString();
        var reg = new RegExp("<td>[ |\r\n|\n|\r]*(.*)[ |\r\n|\n|\r]*</td>");
        Iterable<Match> matches = reg.allMatches(web_page);
        for (Match m in matches) {
          String match = m.group(1);
          match = match.replaceAll(new RegExp('<p align="right">|</p>|,'),"");
          print(match);
        }
      });
    });
}

Webサーバーアプリ
Dartで簡単なWebサーバーアプリのサンプルを作成し、動作確認しました。

●サンプルコード

import 'dart:io';
void main() {
  HttpServer.bind('127.0.0.1', 8080)
  .then((server) {
    server.listen((HttpRequest request) {
      request.response.write('This is Dart Server');
      request.response.close();
    });
  })
  .catchError((e) => print('Error :: $e'));
}

●動作確認

1)下記クライアントアプリ作成

import 'dart:io';
import 'dart:convert' show UTF8;

void main() {
  String result="";
  new HttpClient().getUrl(Uri.parse("http://localhost:8080/index.html"))
    .then((HttpClientRequest request) {
      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        result = contents.toString();
        print(result);
      });
    });
}

2)サーバーアプリをコマンドラインで実行

3)クライアントアプリをコマンドラインで実行

‘This is Dart Server’と表示される事を確認

POSTリクエストのクライアント、サーバーアプリのサンプルを作成
HTTPのPOSTリクエストを送信するクライアント、POSTリクエストを処理するサーバーアプリのサンプルを作成してみました。
 

(1)クライアントアプリ


 
①JSONフォーマットで送信
 
ContentTypeを下記のように指定し、’application/json’にします。
request.headers.contentType = ContentType.JSON;
 
(サンプルコード)

import 'dart:io';
import 'dart:convert';

void main() {
  Map post_data = {
    "id": "0101",
    "name": "tom"
  };
  String result="";
  HttpClient client = new HttpClient();
  client.postUrl(Uri.parse("http://localhost:8080/index.html"))
    .then((HttpClientRequest request) {
      request.headers.contentType = ContentType.JSON;
      request.write(JSON.encode(post_data));
      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        print(contents);
      });
    });
}

②x-www-form-urlencodedフォーマットで送信
 
(サンプルコード)

import 'dart:io';
import 'dart:convert';

void main() {
  String post_data = "id=0101&name=tom";
  String result="";
  HttpClient client = new HttpClient();
  client.postUrl(Uri.parse("http://localhost:8080/index.html"))
    .then((HttpClientRequest request) {
      request.headers.set(HttpHeaders.CONTENT_TYPE,
                       'application/x-www-form-urlencoded');
      request.write(JSON.encode(post_data));
      return request.close();
    })
    .then((HttpClientResponse response){
      response.transform(UTF8.decoder).listen((contents) {
        print(contents);
      });
    });
}

(2)サーバー側アプリ

 
・JSONフォーマットの場合は、JSON.decodeメソッドで処理しています。
 
・x-www-form-urlencodedフォーマットの場合は、Uri.splitQueryStringメソッドでクエリー文字列を処理しています。
 
・CORSを許可するために下記ヘッダー設定を行っています。
req.response.headers.add(‘Access-Control-Allow-Origin’, ‘*’);
req.response.headers.add(‘Access-Control-Allow-Methods’, ‘POST, OPTIONS’);
req.response.headers.add(‘Access-Control-Allow-Headers’,’Origin, X-Requested-With, Content-Type, Accept’);
 
(サンプルコード)

import 'dart:io';
import 'dart:convert';

void main() {
  Map data;
  HttpServer.bind('127.0.0.1', 8080)
  .then((server) {
    server.listen((HttpRequest req) {
      ContentType contentType = req.headers.contentType;
      BytesBuilder builder = new BytesBuilder();
      if (req.method == 'POST' && contentType != null) {
        req.listen((buffer) {
          builder.add(buffer);
        }, onDone: (){
          String bodyString = UTF8.decode(builder.takeBytes());
          if (contentType.mimeType == 'application/json') {
            data = JSON.decode(bodyString);
          } else if (contentType.mimeType == 'application/x-www-form-urlencoded') {
            data = Uri.splitQueryString(bodyString);
          } else {
            data = {"error": "Unsupported request"};
          }
          req.response.headers.add('Access-Control-Allow-Origin', '*');
          req.response.headers.add('Access-Control-Allow-Methods', 'POST, OPTIONS');
          req.response.headers.add('Access-Control-Allow-Headers','Origin, X-Requested-With, Content-Type, Accept');
          req.response.statusCode = HttpStatus.OK;
          req.response.write(data);
          req.response.close();
        });
      } else {
        req.response.statusCode = HttpStatus.METHOD_NOT_ALLOWED;
        req.response.write("Unsupported request: ${req.method}.");
        req.response.close();
      }
    });
  })
  .catchError((e) => print('Error :: $e'));
}

関連記事の目次

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください