2012年8月23日木曜日

[PHP][cURL] cURLでリダイレクトをたどるようにする

PHPはサイト解析するためにURLから情報を取得する方法がたくさんある。
その一つにcURLというのがある。
cURLではHTTPステータスコードが300系などで既に移転してしまったサイトなどは
リクエストのヘッダにLocationがあればリダイレクトしてたどっていく機能がある。

//URLを指定する
$url='google.co.jp';

//cURLを初期化して使用可能にする
$curl=curl_init();

//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//Locationをたどる
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,true);
//最大何回リダイレクトをたどるか
curl_setopt($curl,CURLOPT_MAXREDIRS,10);
//リダイレクトの際にヘッダのRefererを自動的に追加させる
curl_setopt($curl,CURLOPT_AUTOREFERER,true);

//URLにアクセスし、結果を表示させる
curl_exec($curl);

//cURLのリソースを解放する
curl_close($curl);

とすればよい。
意外と自分でリダイレクトをたどり続ける処理を書くのは一手間なので、
こういう細かな機能はありがたい。

ポイントとなる関数
  • curl_init ・・・ cURL セッションを初期化する
  • curl_setopt ・・・ cURL 転送用オプションを設定する
  • curl_exec ・・・ cURL セッションを実行する
  • curl_close ・・・ cURL セッションを閉じる

[PHP][cURL] cURLで結果を文字列として受け取る

PHPは数多くの関数やクラスがあり、URLから結果を取得することができるものもある。
その一つにcURLがある。
cURLはデフォルトでは取得した結果をそのままブラウザやコンソールなどに出力するが、
文字列として受け取りたい場合がある。
その際には

//URLを指定する
$url='www.google.co.jp';

//cURLを初期化して使用可能にする
$curl=curl_init();

//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//文字列で結果を返させる
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);

//URLにアクセスし、結果を文字列として返す
$result=curl_exec($curl);

//cURLのリソースを解放する
curl_close($curl);

となる。
この後にDOM解析したり、特定文字列を検知したりとすることになるだろう。

ポイントとなる関数
  • curl_init ・・・ cURL セッションを初期化する
  • curl_setopt ・・・ cURL 転送用オプションを設定する
  • curl_exec ・・・ cURL セッションを実行する
  • curl_close ・・・ cURL セッションを閉じる

[PHP][cURL] cURLでSSL(https)のCA証明書警告の回避や設定

PHPは柔軟な言語で特定のサイトを取得するのに様々な手法がある。
その中でcURLという選択をとる人は多い。
色々と便利なcURLだが、SSL(つまりhttps)で外部のサイトへアクセスした場合

SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

というエラーが出る場合がある。
これはcURLの–with-ca-bundleオプションで指定されたパスか特定パスにCA証明書がないと起こるエラーだ。
もちろん–with-ca-bundleオプションでCA証明書を指定してもいいのだが、
できない環境(レンタルサーバ)などもあるだろう。
これを回避するには

//URLを指定する
$url='https://www.google.co.jp/';

//cURLを初期化して使用可能にする
$curl=curl_init();

//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//CA証明書の検証をしない
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);

//URLにアクセスし、結果を表示させる
curl_exec($curl);

//cURLのリソースを解放する
curl_close($curl);

としてやればよい。
また、CA証明書を持っているなら

//URLを指定する
$url='https://www.google.co.jp/';
//CA証明書へのパス
$path='./cacert.pem';

//cURLを初期化して使用可能にする
$curl=curl_init();

//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//CA証明書ファイルを指定
curl_setopt($curl,CURLOPT_CAINFO,$path);

//URLにアクセスし、結果を表示させる
curl_exec($curl);

//cURLのリソースを解放する
curl_close($curl);

としてやればよい。

ポイントとなる関数
  • curl_init ・・・ cURL セッションを初期化する
  • curl_setopt ・・・ cURL 転送用オプションを設定する
  • curl_exec ・・・ cURL セッションを実行する
  • curl_close ・・・ cURL セッションを閉じる

[PHP][cURL] cURLでリクエストヘッダを表示させる

PHPでは特定のサイトにアクセスするのに様々な手法があるが、
その中で便利なものの一つがcURLだ。
そのcURLで相手サイトに想定したパラメータなどが渡ってなくて、
思った通りの結果がとれないときがある。
そのときにはリクエストヘッダを出力してみるといいだろう。

//URLを指定する
$url='www.google.co.jp';

//cURLを初期化して使用可能にする
$curl=curl_init();

//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//リクエストヘッダ出力設定
curl_setopt($curl,CURLINFO_HEADER_OUT,true);

//URLにアクセスし、結果を表示させる
curl_exec($curl);

//リクエストヘッダ出力
echo curl_getinfo($curl,CURLINFO_HEADER_OUT);

//cURLのリソースを解放する
curl_close($curl);

とすることによってリクエストヘッダを出力させることができる。
ちなみに上記例では

GET / HTTP/1.1
Host: www.google.co.jp
Accept: */*


となっている。
ちなみに余談だが、HTTP1.1ではHostフィールドだけがリクエストヘッダで唯一の必須になっている。

ポイントとなる関数
  • curl_init ・・・ cURL セッションを初期化する
  • curl_setopt ・・・ cURL 転送用オプションを設定する
  • curl_exec ・・・ cURL セッションを実行する
  • curl_getinfo ・・・ 指定した伝送に関する情報を得る
  • curl_close ・・・ cURL セッションを閉じる

[PHP][cURL] cURLでクッキーを送受信する

PHPは特定のURLにアクセスしたりリクエストを送るのが非常に簡単だ。
とりわけcURLを使う方法は結構メジャーである。
cURLには通信のための様々なオプション設定ができるが、
クッキーを扱うためのオプションももちろんある。

相手からクッキーを受け取りたいときは

//URLを指定する
$url='www.google.co.jp';
//テンポラリファイルを作成する
$cookie=tempnam(sys_get_temp_dir(),'cookie_');

//cURLを初期化して使用可能にする
$curl=curl_init();

//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//クッキーを書き込むファイルを指定
curl_setopt($curl,CURLOPT_COOKIEJAR,$cookie);

//URLにアクセスし、結果を表示させる
curl_exec($curl);

//cURLのリソースを解放する
curl_close($curl);

//クッキーを表示する
echo file_get_contents($cookie);

//テンポラリファイルを削除
unlink($cookie);

となる。
ちなみにクッキーを受け取ったファイルの中身は

# Netscape HTTP Cookie File
# http://www.netscape.com/newsref/std/cookie_spec.html
# This file was generated by libcurl! Edit at your own risk.

ドメイン ドメインクッキーフラグ パス セキュアフラグ 有効期限 名前 値
ドメイン ドメインクッキーフラグ パス セキュアフラグ 有効期限 名前 値
ドメイン ドメインクッキーフラグ パス セキュアフラグ 有効期限 名前 値

となっている。
逆に相手にクッキーを送信したい場合は

//URLを指定する
$url='www.google.co.jp';
//クッキー情報の入ったファイルへのパス
$cookie='./cookie.txt';

//cURLを初期化して使用可能にする
$curl=curl_init();

//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//クッキーを読み込むファイルを指定
curl_setopt($curl,CURLOPT_COOKIEFILE,$cookie);

//URLにアクセスし、結果を表示させる
curl_exec($curl);

//cURLのリソースを解放する
curl_close($curl);

となる。
ちなみにpixivへの自動ログインを作るためには、
このクッキーの扱いをマスターするのが必須になってる。

ポイントとなる関数
  • curl_init ・・・ cURL セッションを初期化する
  • curl_setopt ・・・ cURL 転送用オプションを設定する
  • curl_exec ・・・ cURL セッションを実行する
  • curl_close ・・・ cURL セッションを閉じる

2012年5月17日木曜日

[PHP] 配列の前のキー、次のキーを取得する

配列で特定のキーの前のキー、もしくは次のキーを取得したい時がある。
キーさえわかってしまえば、前の値や次の値も簡単に取れる。
array_keysを使ってキーだけの配列を作り、
更にキーだけの配列に第二引数つきのarray_keysを使って取得するのもありだとは思うが、
配列が大きい時に、array_keysで更に配列を作ると効率が悪い。
そういう時は素直にforeachで調べるのがベター。

//link・・・http://mio-koduki.blogspot.jp/2012/05/php.html
//第一引数・・・調べたい配列
//第二引数・・・基準となるキー
//返り値・・・調べたい配列の、基準となるキーの前のキーが返る。なければfalse
function prev_key($array,$key)
{
    $tmp=false;
    foreach($array as $k=>$v)
    {
        if($k==$key)
        {
            return $tmp;
        }
        $tmp=$k;
    }
    return false;
}

//link・・・http://mio-koduki.blogspot.jp/2012/05/php.html
//第一引数・・・調べたい配列
//第二引数・・・基準となるキー
//返り値・・・調べたい配列の、基準となるキーの次のキーが返る。なければfalse
function next_key($array,$key)
{
    $tmp=false;
    foreach($array as $k=>$v)
    {
        if($k==$key)
        {
            $tmp=$k;
        }
        elseif($tmp!==false)
        {
            return $k;
        }
    }
    return false;
}

記述的にはarray_keysなどを使ったほうがスマートに見えるが、
処理速度やメモリなどを考慮するとやはりforeachで処理するのがベターだと思う。

2012年5月9日水曜日

[PHP] http_build_urlを実装する

PECLにhttp_build_urlというURLを組み立ててくれる便利な関数があるが、
レンタルサーバや、相手のサーバの都合などでPECLを導入できないことがある。
となると、自分で実装するしかない。
http_build_urlの使い方はPHPマニュアルに乗ってるので参考にするといいかもしれない。

//link・・・http://mio-koduki.blogspot.jp/2012/05/php-httpbuildurl.html

//PECLのhttp_build_urlがある可能性があるためチェックする
if(!function_exists('http_build_url'))
{
    //フラグの定数を設定
    define('HTTP_URL_REPLACE',1);
    define('HTTP_URL_JOIN_PATH',2);
    define('HTTP_URL_JOIN_QUERY',4);
    define('HTTP_URL_STRIP_USER',8);
    define('HTTP_URL_STRIP_PASS',16);
    define('HTTP_URL_STRIP_AUTH',24);
    define('HTTP_URL_STRIP_PORT',32);
    define('HTTP_URL_STRIP_PATH',64);
    define('HTTP_URL_STRIP_QUERY',128);
    define('HTTP_URL_STRIP_FRAGMENT',256);
    define('HTTP_URL_STRIP_ALL',504);

    function http_build_url($url,$parts=array(),$flags=HTTP_URL_REPLACE,&$new_url=array())
    {
        //置き換えるキー
        $key=array('user','pass','port','path','query','fragment');
        //urlをパースする
        $new_url=parse_url($url);
        //スキーマとホストが設定されていれば置き換える
        if(isset($parts['scheme']))
        {
            $new_url['scheme']=$parts['scheme'];
        }
        if(isset($parts['host']))
        {
            $new_url['host']=$parts['host'];
        }
        //フラグにHTTP_URL_REPLACEがあれば置き換える
        if($flags&HTTP_URL_REPLACE)
        {
            foreach($key as $v)
            {
                if(isset($parts[$v]))
                {
                    $new_url[$v]=$parts[$v];
                }
            }
        }
        else
        {
            //フラグにHTTP_URL_JOIN_PATHがあり新しいパスがあれば新しいパスをつなげる
            if(isset($parts['path'])&&$flags&HTTP_URL_JOIN_PATH)
            {
                if(isset($new_url['path']))
                {
                    $new_url['path']=rtrim(preg_replace('#'.preg_quote(basename($new_url['path']),'#').'$#','',$new_url['path']),'/').'/'.ltrim($parts['path'],'/');
                }
                else
                {
                    $new_url['path']=$parts['path'];
                }
            }
            //フラグにHTTP_URL_JOIN_QUERYがあり新しいクエリがあれば新しいクエリをつなげる
            if(isset($parts['query'])&&$flags&HTTP_URL_JOIN_QUERY)
            {
                if(isset($new_url['query']))
                {
                    $new_url['query'].='&'.$parts['query'];
                }
                else
                {
                    $new_url['query']=$parts['query'];
                }
            }
        }
        //ストリップフラグの判定をし、設定されていれば消す
        foreach($key as $v)
        {
            if($flags&constant('HTTP_URL_STRIP_'.strtoupper($v)))
            {
                unset($new_url[$v]);
            }
        }
        //パーツを繋げて返す
        return (isset($new_url['scheme'])?$new_url['scheme'].'://':'').(isset($new_url['user'])?$new_url['user'].(isset($new_url['pass'])?':'.$new_url['pass']:'').'@':'').(isset($new_url['host'])?$new_url['host']:'').(isset($new_url['port'])?':'.$new_url['port']:'').(isset($new_url['path'])?$new_url['path']:'').(isset($new_url['query'])?'?'.$new_url['query']:'').(isset($new_url['fragment'])?'#'.$new_url['fragment']:'');
    }
}

という感じになる。
しょっちゅう使うものではないにしても、
たまにあると便利な時がある。

ポイントとなる関数
  • http_build_url ・・・ URL を組み立てる
  • parse_url ・・・ URL を解釈し、その構成要素を返す