2014年9月26日金曜日

【PHP】wgetを使ってログイン後のページのHTMLを文字列として取得したい

PHPで次のようなことがやりたい。

■ブログの記事のように動的に生成されているページを静的HTMLファイルとして保存する。

■ダウンロードするのではなくサーバー上にHTMLファイルを複製する。

■該当のページは、システムにログインしている状態でアクセスした場合とログインしていない状態でアクセスした場合とで表示内容が異なるが、ログインした状態での表示内容を取得したい。

■サーバーの環境的にcURLの機能が使えない。

■複製するHTMLファイルは、リンクや画像へのパスなど、内容を一部書き換える。

---------------------------------------------

実装方針は以下のようになる。

■対象のURLに、ブラウザでアクセスするようなノリでプログラムからアクセスし、レンダリングされたHTMLを取得する。
 つまり、CMS(コンテンツマネジメントシステム=ワードプレスなどようなコンテンツ作成、管理システム / 今回はconcrete5)の動作を解析して、「このブロックはこのデータベーステーブルのこのコラムから値をとって」みたいな内部処理を再現するのではなく、システムの外側からアクセスした時の表示内容を取得する。

■「システムにログインする」というのをPHPプログラムで再現しないといけない。
 URLを読み込むだけならPHPの「file()」や「file_get_contents()」を使えばいいのだが、それらだとログインの挙動を再現できない。
 検索するとよく出てくるのはcURLを使っての実装だが(例: http://web-prog.com/php/curl-login-scraiping/ )、今回の環境ではこれは使えないということになった。
 そこで代わりに使うのは、Linuxのコマンドである「wget」をPHPの「exec()」メソッドで実行させるという方法である。

■wgetコマンドは対象のURLの内容を変数に取得するものではなくファイルに出力するものなので、出力されたファイルをさらに読み込みにいって変数に格納する。wgetで出力されたファイルは読み込んだ後は削除する。


---------------------------------------------

URLの内容を取得するのが難しかったのですが、それなりに上手くいったので、きちんと動いたソースコードの一部を転載用に書き換えて以下に載せます。
今回(仕事で取り組まねばならなかったシステム)は幸運にも、ログインフォームには動的に生成されるアクセストークンなどのめんどくさい値はなかったので、そこはすっきりいきました。

---------------------------------------------
サンプルプログラムソース
ファイルの保存文字コードはUTF8
---------------------------------------------
<?php

class ModelGetterContentFromUrl{
  //////////////////////////////////////////////
  /**
   * wgetを使い、システムへのログイン後のURLの内容を取得する
   *
   * @param  String 対象URL
   * @return String 取得したコンテンツ
   */
  public static function getContentFromUrlUsingWget($url){
    //引数チェック
    if(
      (isset($url) == false)or
      ($url == "")
    ){
      return;
    }//if

    /////////////////////////////////////
    //定数定義(サンプルプログラムなのでここに書いてますが)
    //本来ならコンフィグ用のファイルなどで行ってください

    //一時的な作業フォルダ、ファイル
    define("OUTPUT_DIR_WORK_TEMP" , $_SERVER["DOCUMENT_ROOT"]."/work_temp"); //作業用フォルダ。あらかじめ作成しておく必要がある。
    define("TEMP_FILE_COOKIE"     , OUTPUT_DIR_WORK_TEMP."/cookie.txt");     //クッキー保存用
    define("TEMP_FILE_HTML"       , OUTPUT_DIR_WORK_TEMP."/temp.html");      //wgetで作成されるHTMLファイル

    //ログインページURL
    define("URL_LOGIN_PAGE", "ログインページのURL");
    //ログイン処理を実行するスクリプトのURL(ログインページのフォームのアクションで指定されたURL)
    define("URL_LOGIN_PROCESS", "ログイン処理を実行するスクリプトのURL");

    //ログインID
    define("LOGIN_FORM_NAME_ID", "name"); //ログインフォームのID入力部分のname属性
    define("LOGIN_ID", "admin");          //ログインID
    //ログインパスワード
    define("LOGIN_FORM_NAME_PW", "password"); //ログインフォームのパスワード入力部分のname属性
    define("LOGIN_PW", "password");           //ログインパスワード


    /////////////////////////////////////
    //PHPプログラムをログインさせ、適切なクッキーファイルを作成し、
    //目当てのURLアクセス時にそのクッキーファイルを利用させる

    /////////////////////////////////////
    //処理の三段階の説明
    //1.ログインページにアクセスし、クッキーを取得・保存
    //2.form actionに値をPOSTし、ログイン完了ページへ
    //3.取得したいページへアクセスし、ソース取得

    //クッキー保存ファイルを作成
    touch(TEMP_FILE_COOKIE);

    /////////////////////////////////////
    //第1段階 クッキーを取得
    //該当URL : URL_LOGIN_PAGE
    $linux_command = ""
      ." wget "
      ." --save-cookies='".TEMP_FILE_COOKIE."' "  //クッキー保存指定
      ." --keep-session-cookies "                 //クッキーの内容を漏らさず保存しろという指定
      ." -p DirPath='".OUTPUT_DIR_WORK_TEMP."/' " //書き出しディレクトリ指定
      ." --delete-after=on "                      //ダウンロード後にファイル削除
      ." --no-cache "
      ." '".URL_LOGIN_PAGE."'"
    ;
    exec($linux_command);

    //※このログインページには、システムが動的に生成するアクセストークンなどの値が
    //  hiddenフィールドに存在しないものと想定している。
    //  もしシステムが動的に生成するアクセストークンなどのhiddenフィールドが
    //  ログインページにある場合、「--delete-after=on」を外し、書きだされたHTMLファイル
    //  の内容を解析し、アクセストークンのnameとvalueとを取得する必要がある。


    /////////////////////////////////////
    //第2段階 ログイン完了ページヘ
    //該当URL : URL_LOGIN_PROCESS
    $linux_command = ""
      ." wget "
      ." --load-cookies='".TEMP_FILE_COOKIE."' "  //クッキー読み込み指定
      ." --save-cookies='".TEMP_FILE_COOKIE."' "  //クッキー保存指定
      ." --keep-session-cookies "                 //クッキーの内容を漏らさず保存しろという指定
      ." -p DirPath='".OUTPUT_DIR_WORK_TEMP."/' " //書き出しディレクトリ指定
      ." --delete-after=on "                      //ダウンロード後にファイル削除
      ." --no-cache "
      ." --post-data '".LOGIN_FORM_NAME_ID."=".LOGIN_ID."&".LOGIN_FORM_NAME_PW."=".LOGIN_PW."' "
      ." '".URL_LOGIN_PROCESS."'"
    ;
    exec($linux_command);

    //※もしシステムが動的に生成するアクセストークンなどのhiddenフィールドが
    //  ログインページにある場合、「--post-data 'key=value&key=value'」の行に、
    //  「&アクセストークンのhiddenフィールドのname=アクセストークンの値」を追加


    /////////////////////////////////////
    //第3段階 取得したいページヘアクセスし、コンテンツを取得
    $linux_command = ""
      ." wget "
      ." --load-cookies='".TEMP_FILE_COOKIE."' " //クッキー読み込み指定
      ." --no-cache "                            //キャッシュさせずに常に最新のデータを取得する
      ." -O '".TEMP_FILE_HTML."' "               //書き出しディレクトリとファイル名との指定
      ." '".$url."' "                            //対象URL
    ;
    exec($linux_command);

    //出力したファイルからHTMLの内容を読み取る
    $html_string = file_get_contents(TEMP_FILE_HTML);

    //UTF8に文字コード変換
    mb_language("Japanese");
    $html_string = mb_convert_encoding($html_string, "UTF-8", "auto");

    //一時ファイルの削除
    unlink(TEMP_FILE_COOKIE);
    unlink(TEMP_FILE_HTML);

    //取得したHTMLを文字列に格納したものを戻す
    return $html_string;

  }//function
}//class

---------------------------------------------
/サンプルプログラムソース
---------------------------------------------

0 件のコメント:

コメントを投稿