56
65

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHP と Goutte ではじめる超絶簡単クローラー入門 ログイン編

Last updated at Posted at 2015-08-23

前回のPHP と Goutte ではじめる超絶簡単クローラー入門の記事が予想の35倍くらいストックされたので続編を。

今回は Cookie を引き回してログインしたページの情報をかっさr取得してみましょう。

題材はかの有名な SNS プラットフォーム、mixi なんていかがでしょうか。
API が公開されてない(たぶん)足あと一覧を攻略してみましょう。ニックネームでも集めてみましょうか。

はじめに

前回同様、composer つかってサクッと下地を作りましょう。

composer require fabpot/goutte
touch app.php
app.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Goutte\Client;

$cli = new Client();

このへんまで来たら準備完了ですね。
みなさんバッチリ mixi にログインしてるかと思うのでログアウトしておいてください。

ねらいをさだめる

ログアウトすると https://mixi.jp/ つまりトップページにリダイレクトされたかと思います。

スクリーンショット 2015-08-23 7.34.34.png

(LINE の友達募集コミュニティって時代を感じますね!)

たいせつなのはこちらです。
スクリーンショット 2015-08-23 7.34.15.png

ログインフォーム。

こちらにメールアドレスとパスワードを挿入して submit したいわけです。

app.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Goutte\Client;

$cli = new Client();
$top = $cli->request('GET', 'https://mixi.jp');
$loginForm = $top->filter('form')->form();
$loginForm['email']    = 'mojibakeo@example.com';
$loginForm['password'] = 'PASSSWOOOORDDD';
$cli->submit($loginForm);

フォームに狙いを定めて form() すると Symfony\Component\DomCrawler\Form オブジェクトが取得できるのであとは簡単。
email と password の値をセットして $cli->submit() の引数に渡すだけ。
これだけで $cli にログイン状態が有効な Cookie がセットされるので、こちらを使い回せばログインユーザーとしてアクセスできます。なにこれちょうべんり。

ためしに自分のニックネームを取得してみる

ログイン済みページ右上にあるこれが手っ取り早そうです。

スクリーンショット 2015-08-23 7.51.46.png

一番最初の .nickname がこの要素にあたるようなので以下のように書けますね。

app.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Goutte\Client;

$cli = getLoggedInClient();
$home = $cli->request('GET', 'http://mixi.jp/home.pl');
echo $home->filter('.nickname')->first()->text();

// 見やすさのためにログイン部分を function 化
function getLoggedInClient() {
    $topUrl = 'https://mixi.jp';
    $cli = new Client();
    $top = $cli->request('GET', $topUrl);
    $loginForm = $top->filter('form')->form();
    $loginForm['email']    = 'mojibakeo@example.com';
    $loginForm['password'] = 'PASSSWOOOORDDD';
    $cli->submit($loginForm);
    return $cli;
}

どうでしょう。ニックネームが表示されたのではないでしょうか。

足あとページにアクセス

そしてついに本題です。ニックネームなんぞ API 経由で取得するべきですが、足あとに関しては公開されていません(ぱっと見)。

スクリーンショット_2015-08-23_7_59_23.png

総アクセス数が切ないという衝撃事実 はさておき、このようなページ構成になっております。

Chrome Developer Tools で確認したところ、このような DOM 構造になっていました。
スクリーンショット_2015-08-23_8_01_20.png

足あと表示部分が .logListAre.visitorList で、ニックネームが .nickname なのでこのように絞り込めばさくっとニックネームを集められそうですね。

app.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Goutte\Client;

$cli = getLoggedInClient();
$visitor = $cli->request('GET', 'http://mixi.jp/list_visitor.pl');
$visitorList = $visitor->filter('.visitorList');

$visitUsers = [];
$visitorList->filter('.nickname')->each(function($nickname) use (&$visitUsers) {
    $user = trim($nickname->text());
    // $visitUsers に存在しないニックネームだけ追加
    if (!in_array($user, $visitUsers)) {
        $visitUsers[] = $user;
    }
});

print_r($visitUsers);

function getLoggedInClient() {
    $topUrl = 'https://mixi.jp';
    $cli = new Client();
    $top = $cli->request('GET', $topUrl);
    $loginForm = $top->filter('form')->form();
    $loginForm['email']    = 'mojibakeo@example.com';
    $loginForm['password'] = 'PASSSWOOOORDDD';
    $cli->submit($loginForm);
    $cli->getCookieJar()->all();
    sleep(2);
    return $cli;
}

こんな感じで足あとつけたひとのニックネーム一覧が取得できるはずです。

2ページ目(mixi プレミアムなユーザーならそれ以上)も取得したい

普通に for でまわして

// 抜粋
$visitor = $cli->request('GET', 'http://mixi.jp/list_visitor.pl?page=$i');

のような形でも実現できるのですが、これだと getLoggedInClient が2回も呼ばれてしまいます。
それはつまり1ページから取得したいだけなのに2ページ分のアクセス、10ページなら20アクセスになってしまいますね。
裏口からこそっと行ってるなりに、お行儀よくしたいところです。

Cookie 情報をキャッシュする

現時点でも相当に都合の良いオンナ感ある Goutte ですが、以下の一行だけで Cookie を取り出すことができます。

$cookies = $cli->getCookieJar()->all();

そして以下の1行で取り出した Cookie をセットすることができます。

$cli->getCookieJar()->updateFromSetCookie($cookies);

なんてつごうのいい子…(言いたいだけ)

Cookie を使いまわして省エネアクセスする

まずこれまで使ってた getLoggedInClient を超雑に Cookie 使い回し対応します。

function getLoggedInClient(&$cookies = null) {
    $cli = new Client();
    if (!is_null($cookies)) {
        $cli->getCookieJar()->updateFromSetCookie($cookies);
        return $cli;
    }
    $topUrl = 'https://mixi.jp';
    $top = $cli->request('GET', $topUrl);
    $loginForm = $top->filter('form')->form();
    $loginForm['email']    = 'mojibakeo@example.com';
    $loginForm['password'] = 'PASSSWOOOORDDD';
    $cli->submit($loginForm);
    $cookies = $cli->getCookieJar()->all();
    sleep(2);
    return $cli;
}
// 参照渡しやめろっていうのはごもっともで、普通にやるならクラス化して protected/private な $cookies プロパティを定義して使いたいですが本題からそれるので今回はこれで勘弁してください

これで関数側の準備ができましたね。
あとは1ページ目だけを取得するコードをちょろっと書き換えるだけです。

app.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Goutte\Client;

$cookies = null;
$visitUsers = [];
for ($i = 1, $last = 2; $i <= $last; $i++) {
    $cli = getLoggedInClient($cookies);
    $url = sprintf('http://mixi.jp/list_visitor.pl?page=%s', $i);
    $visitor = $cli->request('GET', $url);
    $visitorList = $visitor->filter('.visitorList');

    $visitorList->filter('.nickname')->each(function($nickname) use (&$visitUsers) {
        $user = trim($nickname->text());
        // $visitUsers に存在しないニックネームだけ追加
        if (!in_array($user, $visitUsers)) {
            $visitUsers[] = $user;
        }
    });
    sleep(2); // 高速連続アクセスとか迷惑なので sleep しましょう
}

print_r($visitUsers);

function getLoggedInClient(&$cookies = null) {
    $cli = new Client();
    if (!is_null($cookies)) {
        $cli->getCookieJar()->updateFromSetCookie($cookies);
        return $cli;
    }
    $topUrl = 'https://mixi.jp';
    $top = $cli->request('GET', $topUrl);
    $loginForm = $top->filter('form')->form();
    $loginForm['email']    = 'mojibakeo@example.com';
    $loginForm['password'] = 'PASSSWOOOORDDD';
    $cli->submit($loginForm);
    $cookies = $cli->getCookieJar()->all();
    sleep(2)
    return $cli;
}

mixi プレミアムなひとが一体何ページ目までアクセスできるのか知りませんが、$last の値を変えてあげれば好きなページ数までクロールできそうですね。
でも高速連続アクセスとか超迷惑なので sleep しましょう。お行儀よく。

さいごに

Facebook が好きです。でも、mixi の方がもっと好きです。

だからみんなもっと mixi つかおう(提案)

56
65
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
56
65

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?