实时抓取标记的 Instagram 照片

Posted

技术标签:

【中文标题】实时抓取标记的 Instagram 照片【英文标题】:Grabbing tagged instagram photos in real time 【发布时间】:2015-04-25 21:00:29 【问题描述】:

我正在尝试实时下载带有特定标签的照片。我发现实时 api 非常无用,所以我使用长轮询策略。下面是伪代码,其中包含一些微妙的错误。

newMediaCount = getMediaCount();
delta = newMediaCount - mediaCount;
if (delta > 0) 
    // if mediaCount changed by now, realDelta > delta, so realDelta - delta photos won't be grabbed and on next poll if mediaCount didn't change again realDelta - delta would be duplicated else ...
    // if photo posted from private account last photo will be duplicated as counter changes but nothing is added to recent
    recentMedia = getRecentMedia(delta);
    // persist recentMedia
    mediaCount = newMediaCount;

第二个问题可以用我猜的某种集合来解决。但首先真的很困扰我。我已将两个调用移至尽可能近的 instagram api,但这是否足够?

编辑

正如 Amir 建议的那样,我使用 min/max_tag_ids 重写了代码。但它仍然会跳过照片。我找不到比将图像保存在磁盘上一段时间并将结果与​​instagram.com/explore/tags/ 进行比较更好的测试方法。

public class LousyInstagramApiTest 

   @Test
    public void testFeedContinuity() throws Exception 
        Instagram instagram = new Instagram(Settings.getClientId());
        final String TAG_NAME = "portrait";
        String id = instagram.getRecentMediaTags(TAG_NAME).getPagination().getMinTagId();
        HashtagEndpoint endpoint = new HashtagEndpoint(instagram, TAG_NAME, id);

        for (int i = 0; i < 10; i++) 
            Thread.sleep(3000);
            endpoint.recentFeed().forEach(d -> 
                try 
                    URL url = new URL(d.getImages().getLowResolution().getImageUrl());
                    BufferedImage img = ImageIO.read(url);
                    ImageIO.write(img, "png", new File("D:\\tmp\\" + d.getId() + ".png"));
                 catch (Exception e) 
                    e.printStackTrace();
                
            );
        
    


class HashtagEndpoint 
    private final Instagram instagram;
    private final String hashtag;
    private String minTagId;

    public HashtagEndpoint(Instagram instagram, String hashtag, String minTagId) 
        this.instagram = instagram;
        this.hashtag = hashtag;
        this.minTagId = minTagId;
    

    public List<MediaFeedData> recentFeed() throws InstagramException 
        TagMediaFeed feed = instagram.getRecentMediaTags(hashtag, minTagId, null);
        List<MediaFeedData> dataList = feed.getData();
        if (dataList.size() == 0) return Collections.emptyList();

        String maxTagId = feed.getPagination().getNextMaxTagId();
        if (maxTagId != null && maxTagId.compareTo(minTagId) > 0) dataList.addAll(paginateFeed(maxTagId));
        Collections.reverse(dataList);
//        dataList.removeIf(d -> d.getId().compareTo(minTagId) < 0);

        minTagId = feed.getPagination().getMinTagId();
        return dataList;
    

    private Collection<? extends MediaFeedData> paginateFeed(String maxTagId) throws InstagramException 
        System.out.println("pagination required");

        List<MediaFeedData> dataList = new ArrayList<>();
        do 
            TagMediaFeed feed = instagram.getRecentMediaTags(hashtag, null, maxTagId);
            maxTagId = feed.getPagination().getNextMaxTagId();
            dataList.addAll(feed.getData());
         while (maxTagId.compareTo(minTagId) > 0);
        return dataList;
    


【问题讨论】:

为什么您发现 Realtime API 没用?我现在正在使用它,效果很好。 @Gonzalingui 因为它不会向您发送数据本身。要获取数据,您需要使用以上两种策略之一都行不通。而且你不能在我所在的服务器端使用它。 【参考方案1】:

使用Tag endpoints 获取带有所需标签的最近媒体,它会在其分页信息中返回min_tag_id,该信息与您调用时最近标记的媒体相关联。由于 API 还接受 min_tag_id 参数,因此您可以从上次查询中传递该数字,以便仅接收在您上次查询之后标记的那些媒体。

因此,根据您拥有的任何轮询机制,您只需调用 API 即可根据上次收到的min_tag_id 获取最新的媒体(如果有)。

您还需要传递一个大的count 参数并按照响应的分页来接收所有数据而不会在标记速度快于轮询速度时丢失任何数据。

更新: 根据您更新的代码:

public List<MediaFeedData> recentFeed() throws InstagramException 
    TagMediaFeed feed = instagram.getRecentMediaTags(hashtag, minTagId, null, 100000);
    List<MediaFeedData> dataList = feed.getData();
    if (dataList.size() == 0) return Collections.emptyList();

    // follow the pagination
    MediaFeed recentMediaNextPage = instagram.getRecentMediaNextPage(feed.getPagination());
    while (recentMediaNextPage.getPagination() != null) 
        dataList.addAll(recentMediaNextPage.getData());
        recentMediaNextPage = instagram.getRecentMediaNextPage(recentMediaNextPage.getPagination());
    

    Collections.reverse(dataList);

    minTagId = feed.getPagination().getMinTagId();
    return dataList;

【讨论】:

感谢您的回复,抱歉回复晚了。我正在测试这种方法并遇到了奇怪的结果,结果并不是我最初想的那样特定于min_tag_id。例如。此调用 api.instagram.com/v1/tags/partytools/media/… 不返回具有指定 id 的媒体,实际上它只返回 6 个最后的帖子。如果您省略 min_id 并提供 counter 或同时提供两者,它只会提供 6 个最后的条目。这是我使用 jInstagram 进行的测试:pastebin.com/EnPRwMLw。那里使用的简码来自第 9 张照片。 这种方法导致跳过一些照片。 您自己实现分页会使事情变得有点复杂。只需拨打min_tag_id 的电话并按照内置分页进行操作。请查看我的更新答案。 是的。虽然内置分页不打算以这种方式使用,因为它从来都不是null,并且当您第一次调用getRecentMediaNextPage 时,它会导致异常,因为nextUrl 为空。但是问题不在于分页。您可以将所有分页代码替换为if (feed.getPagination().getNextUrl() != null) System.out.println("pagination required");,运行工具,一次不需要分页但照片仍然会丢失...... 我很难相信每 10 个人在发布后的 1-3 秒内编辑照片,在标题中平均添加 30 个字。

以上是关于实时抓取标记的 Instagram 照片的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Instagram 实时发布数据中获取实际照片?

在Instagram上搜索我在照片上的照片

如何用 BeautifulSoup 抓取 Instagram

针对标记内容的新 Instagram API 限制的解决方法

我们如何为客户获取实时 Instagram 位置通知?

如何成为 Instagram 中任何客户的 Instagram 沙盒用户