How can I get a user's media from Instagram wi

2019-01-02 19:13发布

I'm trying to put a user's recent Instagram media on a sidebar. I'm trying to use the Instagram API to fetch the media.

http://instagram.com/developer/endpoints/users/

The documentation says to GET https://api.instagram.com/v1/users/<user-id>/media/recent/, but it says to pass an OAuth access token. An access token represents the authorization to act on behalf of a user. I don't want users to log into Instagram to see this on the sidebar. They shouldn't even need to have an Instagram account.

For instance, I can go to http://instagram.com/thebrainscoop without being logged into Instagram and see photos. I want to do that through the API.

In the Instagram API, non-user-authenticated requests pass a client_id instead of an access_token. If I try that, though, I get:

{
  "meta":{
    "error_type":"OAuthParameterException",
    "code":400,
    "error_message":"\"access_token\" URL parameter missing. This OAuth request requires an \"access_token\" URL parameter."
  }
}

So, is this not possible? Is there no way to fetch a user's latest (public) media without asking a user to log into an Instagram account through OAuth first?

18条回答
像晚风撩人
2楼-- · 2019-01-02 19:32

This is late, but worthwhile if it helps someone as I did not see it in Instagram's documentation.

To perform GET on https://api.instagram.com/v1/users/<user-id>/media/recent/ (at present time of writing) you actually do not need OAuth access token.

You can perform https://api.instagram.com/v1/users/[USER ID]/media/recent/?client_id=[CLIENT ID]

[CLIENT ID] would be valid client id registered in app through manage clients (not related to user whatsoever). You can get [USER ID] from username by performing GET users search request: https://api.instagram.com/v1/users/search?q=[USERNAME]&client_id=[CLIENT ID]

查看更多
十年一品温如言
3楼-- · 2019-01-02 19:32

11.11.2017
Since Instagram changed the way they provide this data, none of above methods work nowadays. Here is the new way to get user's media:
GET https://instagram.com/graphql/query/?query_id=17888483320059182&variables={"id":"1951415043","first":20,"after":null}
Where:
query_id - permanent value: 17888483320059182 (note it might be changed in future).
id - id of the user. It may come with list of users. To get the list of users you can use following request: GET https://www.instagram.com/web/search/topsearch/?context=blended&query=YOUR_QUERY
first - amount of items to get.
after - id of the last item if you want to get items from that id.

查看更多
无与为乐者.
4楼-- · 2019-01-02 19:33

Thanks to Instagram's ever changing (and horrifically designed) API schema most of the above will no longer work as of April 2018.

Here is the latest path to access individual post data if you are querying their API directly using the https://www.instagram.com/username/?__a=1 method.

Assuming your returned JSON data is $data you can loop through each result using the following path examples :

foreach ($data->graphql->user->edge_owner_to_timeline_media->edges as $item) {

    $content_id = $item->node->id; 
    $date_posted = $item-node->taken_at_timestamp;
    $comments = $item->node->edge_media_to_comment->count;
    $likes = $item->node->edge_liked_by->count;
    $image = $item->node->display_url;
    $content = $item->node->edge_media_to_caption->edges[0]->node->text;
    // etc etc ....
}

The main things in this recent change were graphql and edge_owner_to_timeline_media.

Looks like they are going to be killing this API access off for non 'business' customers in DEC 2018 so make the most of it while you can.

Hope it helps somebody ;)

查看更多
一个人的天荒地老
5楼-- · 2019-01-02 19:33

You can use this API to retrieve public info of the instagram user:
http://pegaqui-api.sa-east-1.elasticbeanstalk.com/instagram?username=thebrainscoop&limit=2

If you don't set the limit parameter, the posts are limited at 12 by default

This api was made in SpringBoot with HtmlUnit as you can see in the code:

import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

@RestController
public class InstagramController {

    @RequestMapping(value = "/instagram", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<String> getInstagram(@RequestParam(name = "username") String username, @RequestParam(name = "limit", required = false) Long limit) {
        String html;
        WebClient webClient = new WebClient();
        try {
            webClient.getOptions().setCssEnabled(false);
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setThrowExceptionOnScriptError(false);
            Page page = webClient.getPage("https://www.instagram.com/" + username);
            WebResponse response = page.getWebResponse();
            html = response.getContentAsString();
        } catch (Exception ex) {
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        String prefix = "static/bundles/base/ProfilePageContainer.js";
        String sufix = "\"";
        String script = html.substring(html.indexOf(prefix));
        script = script.substring(0, script.indexOf(sufix));
        try {
            Page page = webClient.getPage("https://www.instagram.com/" + script);
            WebResponse response = page.getWebResponse();
            script = response.getContentAsString();
        } catch (Exception ex) {
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        prefix = "r.pagination},queryId:\"";
        String queryHash = script.substring(script.indexOf(prefix) + prefix.length());
        queryHash = queryHash.substring(0, queryHash.indexOf(sufix));
        prefix = "<script type=\"text/javascript\">window._sharedData = ";
        sufix = ";</script>";
        html = html.substring(html.indexOf(prefix) + prefix.length());
        html = html.substring(0, html.indexOf(sufix));
        JsonObject json = new JsonParser().parse(html).getAsJsonObject();
        JsonObject entryData = json.get("entry_data").getAsJsonObject();
        JsonObject profilePage = entryData.get("ProfilePage").getAsJsonArray().get(0).getAsJsonObject();
        JsonObject graphql = profilePage.get("graphql").getAsJsonObject();
        JsonObject user = graphql.get("user").getAsJsonObject();
        JsonObject response = new JsonObject();
        response.addProperty("id", user.get("id").getAsString());
        response.addProperty("username", user.get("username").getAsString());
        response.addProperty("fullName", user.get("full_name").getAsString());
        response.addProperty("followedBy", user.get("edge_followed_by").getAsJsonObject().get("count").getAsLong());
        response.addProperty("following", user.get("edge_follow").getAsJsonObject().get("count").getAsLong());
        response.addProperty("isBusinessAccount", user.get("is_business_account").getAsBoolean());
        response.addProperty("photoUrl", user.get("profile_pic_url").getAsString());
        response.addProperty("photoUrlHD", user.get("profile_pic_url_hd").getAsString());
        JsonObject edgeOwnerToTimelineMedia = user.get("edge_owner_to_timeline_media").getAsJsonObject();
        JsonArray posts = new JsonArray();
        try {
            loadPosts(webClient, queryHash, json.get("rhx_gis").getAsString(), user.get("id").getAsString(), posts, edgeOwnerToTimelineMedia, limit == null ? 12 : limit);
        } catch (Exception ex) {
            return new ResponseEntity<>(HttpStatus.TOO_MANY_REQUESTS);
        }
        response.add("posts", posts);
        return ResponseEntity.ok(response.toString());
    }

    private void loadPosts(WebClient webClient, String queryHash, String rhxGis, String userId, JsonArray posts, JsonObject edgeOwnerToTimelineMedia, Long limit) throws IOException {
        JsonArray edges = edgeOwnerToTimelineMedia.get("edges").getAsJsonArray();
        for (JsonElement elem : edges) {
            if (limit != null && posts.size() == limit) {
                return;
            }
            JsonObject node = elem.getAsJsonObject().get("node").getAsJsonObject();
            if (node.get("is_video").getAsBoolean()) {
                continue;
            }
            JsonObject post = new JsonObject();
            post.addProperty("id", node.get("id").getAsString());
            post.addProperty("shortcode", node.get("shortcode").getAsString());
            JsonArray captionEdges = node.get("edge_media_to_caption").getAsJsonObject().get("edges").getAsJsonArray();
            if (captionEdges.size() > 0) {
                JsonObject captionNode = captionEdges.get(0).getAsJsonObject().get("node").getAsJsonObject();
                post.addProperty("caption", captionNode.get("text").getAsString());
            } else {
                post.add("caption", null);
            }
            post.addProperty("photoUrl", node.get("display_url").getAsString());
            JsonObject dimensions = node.get("dimensions").getAsJsonObject();
            post.addProperty("photoWidth", dimensions.get("width").getAsLong());
            post.addProperty("photoHeight", dimensions.get("height").getAsLong());
            JsonArray thumbnailResources = node.get("thumbnail_resources").getAsJsonArray();
            JsonArray thumbnails = new JsonArray();
            for (JsonElement elem2 : thumbnailResources) {
                JsonObject obj = elem2.getAsJsonObject();
                JsonObject thumbnail = new JsonObject();
                thumbnail.addProperty("photoUrl", obj.get("src").getAsString());
                thumbnail.addProperty("photoWidth", obj.get("config_width").getAsLong());
                thumbnail.addProperty("photoHeight", obj.get("config_height").getAsLong());
                thumbnails.add(thumbnail);
            }
            post.add("thumbnails", thumbnails);
            posts.add(post);
        }
        JsonObject pageInfo = edgeOwnerToTimelineMedia.get("page_info").getAsJsonObject();
        if (!pageInfo.get("has_next_page").getAsBoolean()) {
            return;
        }
        String endCursor = pageInfo.get("end_cursor").getAsString();
        StringBuilder xInstagramGis = new StringBuilder();
        String variables = "{\"id\":\"" + userId + "\",\"first\":12,\"after\":\"" + endCursor + "\"}";
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            String key = rhxGis + ":" + variables;
            byte[] messageDigest = md5.digest(key.getBytes());
            BigInteger no = new BigInteger(1, messageDigest);
            xInstagramGis.append(no.toString(16));
            while (xInstagramGis.length() < 32) {
                xInstagramGis.insert(0, "0");
            }
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        }
        webClient.addRequestHeader("x-instagram-gis", xInstagramGis.toString());
        String url = "https://www.instagram.com/graphql/query/?query_hash=" + queryHash + "&variables=" + URLEncoder.encode(variables, "UTF-8");
        Page page = webClient.getPage(url);
        WebResponse response = page.getWebResponse();
        String content = response.getContentAsString();
        JsonObject json = new JsonParser().parse(content).getAsJsonObject();
        loadPosts(webClient, queryHash, rhxGis, userId, posts, json.get("data").getAsJsonObject().get("user").getAsJsonObject().get("edge_owner_to_timeline_media").getAsJsonObject(), limit);
    }

}


It's an example of response:

{
  "id": "290482318",
  "username": "thebrainscoop",
  "fullName": "Official Fan Page",
  "followedBy": 1023,
  "following": 6,
  "isBusinessAccount": false,
  "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/447ffd0262082f373acf3d467435f130/5C709C77/t51.2885-19/11351770_612904665516559_678168252_a.jpg",
  "photoUrlHD": "https://scontent-gru2-1.cdninstagram.com/vp/447ffd0262082f373acf3d467435f130/5C709C77/t51.2885-19/11351770_612904665516559_678168252_a.jpg",
  "posts": [
    {
      "id": "1430331382090378714",
      "shortcode": "BPZjtBUly3a",
      "caption": "If I have any active followers anymore; hello! I'm Brianna, and I created this account when I was just 12 years old to show my love for The Brain Scoop. I'm now nearly finished high school, and just rediscovered it. I just wanted to see if anyone is still active on here, and also correct some of my past mistakes - being a child at the time, I didn't realise I had to credit artists for their work, so I'm going to try to correct that post haste. Also; the font in my bio is horrendous. Why'd I think that was a good idea? Anyway, this is a beautiful artwork of the long-tailed pangolin by @chelsealinaeve . Check her out!",
      "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/ab823331376ca46136457f4654bf2880/5CAD48E4/t51.2885-15/e35/16110915_400942200241213_3503127351280009216_n.jpg",
      "photoWidth": 640,
      "photoHeight": 457,
      "thumbnails": [
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/43b195566d0ef2ad5f4663ff76d62d23/5C76D756/t51.2885-15/e35/c91.0.457.457/s150x150/16110915_400942200241213_3503127351280009216_n.jpg",
          "photoWidth": 150,
          "photoHeight": 150
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/ae39043a7ac050c56d741d8b4355c185/5C93971C/t51.2885-15/e35/c91.0.457.457/s240x240/16110915_400942200241213_3503127351280009216_n.jpg",
          "photoWidth": 240,
          "photoHeight": 240
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/ae7a22d09e3ef98d0a6bbf31d621a3b7/5CACBBA6/t51.2885-15/e35/c91.0.457.457/s320x320/16110915_400942200241213_3503127351280009216_n.jpg",
          "photoWidth": 320,
          "photoHeight": 320
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/1439dc72b70e7c0c0a3afcc30970bb13/5C8E2923/t51.2885-15/e35/c91.0.457.457/16110915_400942200241213_3503127351280009216_n.jpg",
          "photoWidth": 480,
          "photoHeight": 480
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/1439dc72b70e7c0c0a3afcc30970bb13/5C8E2923/t51.2885-15/e35/c91.0.457.457/16110915_400942200241213_3503127351280009216_n.jpg",
          "photoWidth": 640,
          "photoHeight": 640
        }
      ]
    },
    {
      "id": "442527661838057235",
      "shortcode": "YkLJBXJD8T",
      "caption": null,
      "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/dc94b38da679826b9ac94ccd2bcc4928/5C7CDF93/t51.2885-15/e15/11327349_860747310663863_2105199307_n.jpg",
      "photoWidth": 612,
      "photoHeight": 612,
      "thumbnails": [
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/c1153c6513c44a6463d897e14b2d8f06/5CB13ADD/t51.2885-15/e15/s150x150/11327349_860747310663863_2105199307_n.jpg",
          "photoWidth": 150,
          "photoHeight": 150
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/47e60ec8bca5a1382cd9ac562439d48c/5CAE6A82/t51.2885-15/e15/s240x240/11327349_860747310663863_2105199307_n.jpg",
          "photoWidth": 240,
          "photoHeight": 240
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/da0ee5b666ab40e4adc1119e2edca014/5CADCB59/t51.2885-15/e15/s320x320/11327349_860747310663863_2105199307_n.jpg",
          "photoWidth": 320,
          "photoHeight": 320
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/02ee23571322ea8d0992e81e72f80ef2/5C741048/t51.2885-15/e15/s480x480/11327349_860747310663863_2105199307_n.jpg",
          "photoWidth": 480,
          "photoHeight": 480
        },
        {
          "photoUrl": "https://scontent-gru2-1.cdninstagram.com/vp/dc94b38da679826b9ac94ccd2bcc4928/5C7CDF93/t51.2885-15/e15/11327349_860747310663863_2105199307_n.jpg",
          "photoWidth": 640,
          "photoHeight": 640
        }
      ]
    }
  ]
}
查看更多
与君花间醉酒
6楼-- · 2019-01-02 19:36

Update

This method don't work anymore

JSFiddle

Javascript:

$(document).ready(function(){

    var username = "leomessi";
    var max_num_items = 5;

    var jqxhr = $.ajax( "https://www.instagram.com/"+username+"/?__a=1" ).done(function() {
        //alert( "success" );
    }).fail(function() {
        //alert( "error" );
    }).always(function(data) {
        //alert( "complete" )
        items = data.graphql.user.edge_owner_to_timeline_media.edges;
        $.each(items, function(n, item) {
            if( (n+1) <= max_num_items )
            {
                var data_li = "<li><a target='_blank' href='https://www.instagram.com/p/"+item.node.shortcode+"'><img src='" + item.node.thumbnail_src + "'/></a></li>";
                $("ul.instagram").append(data_li);
            }
        });

    });

});

HTML:

<ul class="instagram">
</ul>

CSS:

ul.instagram {
    list-style: none;
}

ul.instagram li {
  float: left;
}

ul.instagram li img {
    height: 100px;
}
查看更多
怪性笑人.
7楼-- · 2019-01-02 19:37

As of last week, Instagram disabled /media/ urls, I implemented a workaround, which works pretty well for now.

To solve everyone's problems in this thread, I wrote this: https://github.com/whizzzkid/instagram-reverse-proxy

It provides all of instagram's public data using the following endpoints:

Get user media:

https://igapi.ga/<username>/media
e.g.: https://igapi.ga/whizzzkid/media 

Get user media with limit count:

https://igapi.ga/<username>/media?count=N // 1 < N < 20
e.g.: https://igapi.ga/whizzzkid/media?count=5

Use JSONP:

https://igapi.ga/<username>/media?callback=foo
e.g.: https://igapi.ga/whizzzkid/media?callback=bar

The proxy API also appends next page and previous page URLs to the response so you do not need to calculate that at your end.

Hope you guys like it!

Thanks to @350D for spotting this :)

查看更多
登录 后发表回答