Help us understand the problem. What is going on with this article?

PHPでInstagramの特定ユーザの写真をスクレイピングする2018/3/20版(認証不要)

More than 1 year has passed since last update.

はじめに

Instagramのusernameもしくはuser_idを指定して写真をスクレイピングできないか考えてサイトにある情報を参考に作成したもの。

今日1日で作ったので検証も甘いです。関数名も見直すかもしれません。コーディングも未熟ですみません。

また筆者は個人的にこのコーディングを育てていくつもりだが、あくまで参考情報としてみて頂きたい。

説明

query_hashとは

query_hashはリクエストのアカウントを特定するものと思われる。
instagramの画面をスクロールすると古い投稿の画像が表示されるが、javascript等でクライアントサイドで都度、画像の情報一覧を取得している動きをしている。
その際に、リクエストを行っているアカウントが分かるようにquery_hashをWEBアクセス時にGET値として付与させる必要があるようだ。2018/03/20時点

query_hashを調べる方法については割愛(ブラウザのデバック機能などで調べられる)

少し前まではquery_idだったみたい。昔作成したアカウントであれば、query_idをもっており、query_idの付与でも取得できるようだ。筆者のアカウントは今年作成した為かquery_hashしかわからない。

user_idとusername

qiitaとかgithubなどに投稿されているプログラムのソースをみると、たまにuser_idとusernameの呼び名(変数名)が区別がされていないことがあったが、instagramの仕組上ではちゃんと区別しておく必要がある。

user_idは数字だけで表現されており、おそらく変更できない。
usernameは英数字アンダースコアで表現されており、変更可能。(URLに利用されている名前)

usernameを変更されると、プログラムでusernameで埋め込んでいる場合同じアカウントの情報を取得できなくなる。

クラスと実行例

実行例

$query_hash = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

$ins = new Insta();
$ins->setQuery_hash($query_hash);

$username = $ins->get_username_by_user_id('XXXXXXXXXXXXX');
var_dump($username);

$user_id = $ins->get_user_id_by_username("XXXXX_XXXXXX_official");
var_dump($user_id);

while($ins->is_cursorend){
    foreach ($ins->media_by_user as $media){

        $typename = $media["node"]["__typename"];
        if($typename =="GraphImage"){
            var_dump($media["node"]["id"]);
            var_dump($media["node"]["shortcode"]);
        } else if($typename =="GraphSidecar"){
            $ins->get_media_detail_by_shortcode($media["node"]["shortcode"]);
            foreach($ins->media_childrens as $media_children){
                var_dump($media_children["node"]["id"]);
                var_dump($media_children["node"]["shortcode"]);
            }
        }
    }
    $ins->get_media_id_user_feed($user_id,$ins->end_cursor,1);
}

$ins->is_cursorend = true;

クラス

class Insta{

    const api_user_detail = "https://i.instagram.com/api/v1/users/%s/info/";
    const url_user_detail = "https://www.instagram.com/%s/?__a=1";
    const url_media_detail = "https://www.instagram.com/p/%s/?__a=1";

    public $media_by_user = array();
    public $media_childrens = array();
    public $end_cursor = null;
    public $is_cursorend = true;
    private $query_hash = false;


    function setQuery_hash($query_hash){
        $this->query_hash = $query_hash;
    }


    function get_media_id_user_feed($user_id,$after,$first =12){

        if($this->query_hash == null){
            return null;
        }

        $variables = Array(
            "id" => $user_id,
            "first" => $first,
        );

        if($after != null){
            $variables["after"] = $after;
        }

        $url = "https://www.instagram.com/graphql/query/?".
                                 "query_hash=" .$this->query_hash."&".
                                 http_build_query(array("variables"  => json_encode($variables)));

        $r =self::rest("GET",$url);
        $all_data = json_decode($r,true);

        if(array_key_exists("data",$all_data)){
            if(count($all_data["data"]["user"]["edge_owner_to_timeline_media"]["edges"]) != 0){
                $this->media_by_user = $all_data["data"]["user"]["edge_owner_to_timeline_media"]["edges"];
                $this->end_cursor =$all_data["data"]["user"]["edge_owner_to_timeline_media"]["page_info"]["end_cursor"];
            } else {
                $this->media_by_user = array();
                $this->end_cursor = false;
                $this->is_cursorend = false;
            }

        }
    }


    function get_username_by_user_id($user_id){

        $url_info = sprintf(self::api_user_detail, $user_id);

        $r =self::rest("GET",$url_info);

        $all_data = json_decode($r,true);
        $username = $all_data["user"]["username"];

        return $username;
    }


    function get_user_id_by_username($username){
        $url = sprintf(self::url_user_detail, $username);
        $r =self::rest("GET",$url);

        $all_data = json_decode($r,true);

        $user_id = $all_data["graphql"]["user"]["id"];

        return $user_id;
    }


    function get_media_detail_by_shortcode($shortcode){
        $media_url = sprintf(self::url_media_detail, $shortcode);
        $r =self::rest("GET",$media_url);

        $all_data = json_decode($r,true);
        $username = $all_data['graphql']['shortcode_media']['owner']['username'];

        $this->media_childrens = $all_data["graphql"]["shortcode_media"]["edge_sidecar_to_children"]["edges"];
    }


        function rest($method,$uri){

            if(!isset($handle)) $handle = curl_init();

            $option_defaults = array(
                CURLOPT_HEADER => false,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 2
            ); 

            $options = array(
                CURLOPT_URL => $uri,
                CURLOPT_CUSTOMREQUEST => $method,
            ); 

            curl_setopt_array($handle,($options + $option_defaults));


            $responce =  curl_exec($handle);
            //var_dump(curl_errno($handle));
            //var_dump(curl_error($handle));
            return $responce;
        }

}

関数名

string get_username_by_user_id (string $user_id)
user_idからusernameを取得

string get_user_id_by_username (string $username)
usernameからuser_idを取得

array get_media_id_user_feed (string \$user_id, string \$after [,int \$first])
\$user_idには数字だけのidを使う。(ピンとこない方は説明を参照)
\$afterには一度写真情報を取得した際のjsonにend_cursorの文字列を次の取得で渡すことで、前回取得した内容を除く次の古い写真情報を取得できるようになる。なお、nullにして取得することで最新の写真を取得できる。
\$firestは一度に取得できる投稿の数。最大いくつまでとれるかは未検証。

array media_by_user
get_media_id_user_feedで取得した写真情報をarray形式で参照できる

get_media_detail_by_shortcode(string \$shortcode)
$shortcodeには写真毎に割り当てされているコードを指定する

array media_childrens
get_media_detail_by_shortcodeで取得した写真情報(子供)をarray形式で参照できる

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした