从 Pinterest 网址获取图板的所有图像

2023-12-25

这个问题听起来很简单,但其实并不像听起来那么简单。

简要总结出了什么问题

例如,使用此板;http://pinterest.com/dodo/web-designui-and-mobile/ http://pinterest.com/dodo/web-designui-and-mobile/

检查板本身的 HTML(在div与班级GridItems)在页面顶部产生:

<div class="variableHeightLayout padItems GridItems Module centeredWithinWrapper" style="..">
    <!-- First div with a displayed board image -->
    <div class="item" style="top: 0px; left: 0px; visibility: visible;">..</div>
    ...
    <!-- Last div with a displayed board image -->
    <div class="item" style="top: 3343px; left: 1000px; visibility: visible;">..</div>
</div>

然而,在页面底部,激活无限滚动几次后,我们得到了 HTML:

<div class="variableHeightLayout padItems GridItems Module centeredWithinWrapper" style="..">
    <!-- First div with a displayed board image -->
    <div class="item" style="top: 12431px; left: 750px; visibility: visible;">..</div>
    ...
    <!-- Last div with a displayed board image -->
    <div class="item" style="top: 19944px; left: 750px; visibility: visible;">..</div>
</div>

正如您所看到的,页面上方的一些图像容器已经消失,并且并非所有图像容器都会在首次​​加载页面时加载。


我想做的事

我希望能够创建一个 C# 脚本(或目前的任何服务器端语言),该脚本可以下载页面的完整 HTML(即检索页面上的每个图像),然后从其 URL 下载图像。下载网页并使用适当的 XPath 很容易,但真正的挑战是下载每个图像的完整 HTML。

有没有一种方法可以模拟滚动到页面底部,或者有没有一种更简单的方法可以检索每个图像?我想象 Pinterest 使用 AJAX 来更改 HTML,有没有一种方法可以以编程方式触发事件来接收所有 HTML?预先感谢您的建议和解决方案,如果您没有任何建议和解决方案,请阅读这个很长的问题!

伪代码

using System;
using System.Net;
using HtmlAgilityPack;

private void Main() {
    string pinterestURL = "http://www.pinterest.com/...";
    string XPath = ".../img";

    HtmlDocument doc = new HtmlDocument();

    // Currently only downloads the first 25 images.
    doc.Load(strPinterestUrl);

    foreach(HtmlNode link in doc.DocumentElement.SelectNodes(strXPath))
    {
         image_links[] = link["src"];
         // Use image links
    }
}

好的,所以我认为这可能是您所需要的(经过一些修改)。

Caveats:

  1. 这是 PHP,而不是 C#(但您说过您对任何服务器端语言感兴趣)。
  2. 此代码挂钩(非官方)Pinterest 搜索端点。您需要更改 $data 和 $search_res 以反映您的任务的适当端点(例如 BoardFeedResouce)。注意:至少对于搜索,Pinterest 目前使用两个端点,一个用于初始页面加载,另一个用于无限滚动操作。每个都有自己的预期参数结构。
  3. Pinterest 没有官方的公共 API,预计每当他们更改任何内容时都会出现这种情况,并且不会发出警告。
  4. 您可能会发现 pinterestapi.co.uk 更容易实施并且适合您正在做的事情。
  5. 我在类下面有一些演示/调试代码,一旦您获得所需的数据,这些代码就不应该存在,并且您可能想要更改默认的页面获取限制。

兴趣点:

  1. 下划线_参数采用 JavaScript 格式的时间戳,即。类似于 Unix 时间,但添加了毫秒。它实际上并不用于分页。
  2. 分页使用bookmarks属性,因此您向不需要它的“新”端点发出第一个请求,然后获取bookmarks从结果中获取并在您的请求中使用它来获取下一个“页面”结果,采用bookmarks从这些结果中获取下一页,依此类推,直到用完结果或达到预设限制(或者达到服务器脚本执行时间的最大值)。我很想知道到底是什么bookmarks字段编码。我想,除了 PIN ID 或其他页面标记之外,还有一些有趣的秘密武器。
  3. 我跳过 html,而是处理 JSON,因为它(对我来说)比使用 DOM 操作解决方案或一堆正则表达式更容易。
<?php

if(!class_exists('Skrivener_Pins')) {

  class Skrivener_Pins {

    /**
     * Constructor
     */
    public function __construct() {
    }

    /**
     * Pinterest search function. Uses Pinterest's "internal" page APIs, so likely to break if they change.
     * @author [@skrivener] Philip Tillsley
     * @param $search_str     The string used to search for matching pins.
     * @param $limit          Max number of pages to get, defaults to 2 to avoid excessively large queries. Use care when passing in a value.
     * @param $bookmarks_str  Used internally for recursive fetches.
     * @param $pages          Used internally to limit recursion.
     * @return array()        int['id'], obj['image'], str['pin_link'], str['orig_link'], bool['video_flag']
     * 
     * TODO:
        * 
        * 
     */
    public function get_tagged_pins($search_str, $limit = 1, $bookmarks_str = null, $page = 1) {

      // limit depth of recursion, ie. number of pages of 25 returned, otherwise we can hang on huge queries
      if( $page > $limit ) return false;

      // are we getting a next page of pins or not
      $next_page = false;
      if( isset($bookmarks_str) ) $next_page = true;

      // build url components
      if( !$next_page ) {

        // 1st time
        $search_res = 'BaseSearchResource'; // end point
        $path = '&module_path=' . urlencode('SearchInfoBar(query=' . $search_str . ', scope=boards)');
        $data = preg_replace("'[\n\r\s\t]'","",'{
          "options":{
            "scope":"pins",
            "show_scope_selector":true,
            "query":"' . $search_str . '"
          },
          "context":{
            "app_version":"2f83a7e"
          },
          "module":{
            "name":"SearchPage",
            "options":{
              "scope":"pins",
              "query":"' . $search_str . '"
            }
          },
          "append":false,
          "error_strategy":0
          }');
      } else {

        // this is a fetch for 'scrolling', what changes is the bookmarks reference, 
        // so pass the previous bookmarks value to this function and it is included
        // in query
        $search_res = 'SearchResource'; // different end point from 1st time search
        $path = '';
        $data = preg_replace("'[\n\r\s\t]'","",'{
          "options":{
            "query":"' . $search_str . '",
            "bookmarks":["' . $bookmarks_str . '"],
            "show_scope_selector":null,
            "scope":"pins"
          },
          "context":{
            "app_version":"2f83a7e"
          },
            "module":{
              "name":"GridItems",
            "options":{
              "scrollable":true,
              "show_grid_footer":true,
              "centered":true,
              "reflow_all":true,
              "virtualize":true,
              "item_options":{
                "show_pinner":true,
                "show_pinned_from":false,
                "show_board":true
              },
              "layout":"variable_height"
            }
          },
          "append":true,
          "error_strategy":2
        }');
      }
      $data = urlencode($data);
      $timestamp = time() * 1000; // unix time but in JS format (ie. has ms vs normal server time in secs), * 1000 to add ms (ie. 0ms)

      // build url
      $url = 'http://pinterest.com/resource/' . $search_res . '/get/?source_url=/search/pins/?q=' . $search_str
          . '&data=' . $data
          . $path
          . '&_=' . $timestamp;//'1378150472669';

      // setup curl
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Requested-With: XMLHttpRequest"));

      // get result
      $curl_result = curl_exec ($ch); // this echoes the output
      $curl_result = json_decode($curl_result);
      curl_close ($ch);

      // clear html to make var_dumps easier to see when debugging
      // $curl_result->module->html = '';

      // isolate the pin data, different end points have different data structures
      if(!$next_page) $pin_array = $curl_result->module->tree->children[1]->children[0]->children[0]->children;
      else $pin_array = $curl_result->module->tree->children;

      // map the pin data into desired format
      $pin_data_array = array();
      $bookmarks = null;
      if(is_array($pin_array)) {
        if(count($pin_array)) {

          foreach ($pin_array as $pin) {

            //setup data
            $image_id = $pin->options->pin_id;
            $image_data = ( isset($pin->data->images->originals) ) ? $pin->data->images->originals : $pin->data->images->orig;
            $pin_url = 'http://pinterest.com/pin/' . $image_id . '/';
            $original_url = $pin->data->link;
            $video = $pin->data->is_video;

            array_push($pin_data_array, array(
              "id"          => $image_id,
              "image"       => $image_data,
              "pin_link"    => $pin_url,
              "orig_link"   => $original_url,
              "video_flag"  => $video,
              ));
          }
          $bookmarks = reset($curl_result->module->tree->resource->options->bookmarks);

        } else {
          $pin_data_array = false;
        }
      }

      // recurse until we're done
      if( !($pin_data_array === false) && !is_null($bookmarks) ) {

        // more pins to get
        $more_pins = $this->get_tagged_pins($search_str, $limit, $bookmarks, ++$page);
        if( !($more_pins === false) ) $pin_data_array = array_merge($pin_data_array, $more_pins);
        return $pin_data_array;
      }

      // end of recursion
      return false;
    }

  } // end class Skrivener_Pins
} // end if



/**
 * Debug/Demo Code
 * delete or comment this section for production
 */

// output headers to control how the content displays
// header("Content-Type: application/json");
header("Content-Type: text/plain");
// header("Content-Type: text/html");

// define search term
// $tag = "vader";
$tag = "haemolytic";
// $tag = "qjkjgjerbjjkrekhjk";

if(class_exists('Skrivener_Pins')) {

  // instantiate the class
  $pin_handler = new Skrivener_Pins();

  // get pins, pinterest returns 25 per batch, function pages through this recursively, pass in limit to 
  // override default limit on number of pages to retrieve, avoid high limits (eg. limit of 20 * 25 pins/page = 500 pins to pull 
  // and 20 separate calls to Pinterest)
  $pins1 = $pin_handler->get_tagged_pins($tag, 2);

  // display the pins for demo purposes
  echo '<h1>Images on Pinterest mentioning "' . $tag . '"</h1>' . "\n";
  if( $pins1 != false ) {
    echo '<p><em>' . count($pins1) . ' images found.</em></p>' . "\n";
    skrivener_dump_images($pins1, 5);
  } else {
    echo '<p><em>No images found.</em></p>' . "\n";
  }
}

// demo function, dumps images in array to html img tags, can pass limit to only display part of array
function skrivener_dump_images($pin_array, $limit = false) {
  if(is_array($pin_array)) {
    if($limit) $pin_array = array_slice($pin_array, -($limit));
    foreach ($pin_array as $pin) {
      echo '<img src="' . $pin['image']->url . '" width="' . $pin['image']->width . '" height="' . $pin['image']->height . '" >' . "\n";
    }
  }
}

?>

如果您在使其适应您的特定终点时遇到问题,请告诉我。 Apols 对于代码中的任何草率之处,它最初并没有投入生产。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 Pinterest 网址获取图板的所有图像 的相关文章

  • 如何滚动到div内的元素?

    我有一个滚动的div我想在点击它时发生一个事件 它会强制执行此操作div滚动以查看内部元素 我写的JavasCript是这样的 document getElementById chr scrollIntoView true 但这会在滚动时滚
  • css 计数器在 Internet Explorer 中无法工作以获取隐藏内容 - 如何修复?

    我们想要一些编号列表 并发现了这个很酷的计数器 您可以在 css 中使用它来让浏览器为您计算数字 ol instructions counter reset instructions section ol instructions gt l
  • Flux + React.js - 操作中的回调是好还是坏?

    让我解释一下我最近遇到的问题 我有 React js Flux 驱动的应用程序 有一个列表显示文章数量 注意 应用程序中有多个不同的列表 和文章详情查看在里面 但每个列表只有一个 API 端点 它返回文章数组 为了显示我需要的详细信息fin
  • 使水平滚动条始终可见,即使底部不在视图中

    我将用一个片段来开始这个问题 该片段几乎显示了我想要完成的任务 wrapper overflow hidden display flex sidebar min width 200px background 333 color FFF co
  • 如何访问另一个 mobx 商店中的 mobx 商店?

    假设以下结构 stores RouterStore js UserStore js index js each of Store jsfiles 是一个 mobx 存储类 包含 observable and action index js只
  • 如何将 Browserify 与外部依赖项一起使用?

    我正在尝试慢慢地将 Browserify 引入我的网站 但我不想重写所有 js 也不希望 jquery 和其他库的重复实例与我的 Browserify 版本捆绑在一起 如果我构建将 jquery 列为外部依赖项的模块 那么如何将其指向我的全
  • C++ 到 C# 事件处理

    所以我有我的C WinForm 应用程序 我从中调用我的C CLI MFC dll图书馆 但也有一些events在我的 C 库上 甚至此事件也发生在该库的本机 非 CLI 部分 我需要从我的 C 应用程序调用一些代码 并获取一些有关此事件的
  • ALTER TABLE ... ADD CONSTRAINT 失败时将事务回滚到保存点

    有没有办法在事务中添加检查约束and如果失败回滚到以前的保存点 而不是回滚整个事务 就我而言 当 ALTER TABLE ADD CONSTRAINT 命令失败时 事务无法回滚到保存点 尝试这样做会引发 InvalidOperationEx
  • 有关于 PHP 中的 V8JS 的文档吗?

    有没有关于V8JS的文档 我是否只需要标准 PHP 或一些扩展即可使用 V8JS 我将非常感谢有关 PHP 中的 V8JS 的任何信息 要求 PHP 5 3 3 和 V8 库和标头安装在正确的路径中 Install http www php
  • 从 NumPy 数组到 Mat 的 C++ 转换 (OpenCV)

    我正在围绕 ArUco 增强现实库 基于 OpenCV 编写一个薄包装器 我试图构建的界面非常简单 Python 将图像传递给 C 代码 C 代码检测标记并将其位置和其他信息作为字典元组返回给 Python 但是 我不知道如何在 Pytho
  • 在哪里可以下载没有 Visual Studio 2010 的 C# 4.0 编译器?

    我知道 CTP VS 2010 映像 但我可以只下载 NET Framework 4 0 和 C 编译器吗 AFAIK VS 2010 CTP 仅作为 VM 映像提供 我不相信 Microsoft 发布了 VS 的安装程序 其中一个绝对不适
  • React Native - 跨屏幕传递数据

    我遇到了一些麻烦react native应用程序 我不知道如何跨屏幕传递数据 我意识到还有其他类似的问题在 SO 上得到了回答 但是这些解决方案对我来说不起作用 我正在使用StackNavigator 这是我的设置App js file e
  • gwt 中的垂直居中

    如何使用垂直面板在 gwt 中垂直居中 或者请告诉我有什么方法可以进行垂直居中 如果你想直接使用VerticalPanel从代码中 您需要使用setVerticalAlignment HasVerticalAlignment ALIGN M
  • Django 与谷歌图表

    我试图让谷歌图表显示在我的页面上 但我不知道如何将值从 django 视图传递到 javascript 以便我可以绘制图表 姜戈代码 array Year Sales Expenses 2004 1000 400 2005 1170 460
  • 将 char 绑定到枚举类型

    我有一段与此非常相似的代码 class someclass public enum Section START MID END vector section Full void ex for int i 0 i section
  • 如何在c linux中收听特定接口上的广播?

    我目前可以通过执行以下操作来收听我编写的简单广播服务器 仅广播 hello int fd socket PF INET SOCK DGRAM 0 struct sockaddr in addr memset addr 0 sizeof ad
  • 使用 C# 动态创建按钮并按预定义的顺序放置它们

    NET 4 5 C 创建 Windows 窗体 我想动态创建和添加按钮并为其分配单击事件 但希望它们以特定的方式动态放置 就像图像一样 我的问题是如何以上述方式动态放置按钮 即 4x4 格式 一行 4 个按钮 4 列 但行数不受限制 是否可
  • 如何提高环复杂度?

    对于具有大量决策语句 包括 if while for 语句 的方法 循环复杂度会很高 那么我们该如何改进呢 我正在处理一个大项目 我应该减少 CC gt 10 的方法的 CC 并且有很多方法都存在这个问题 下面我将列出一些例如我遇到的问题的
  • 如何更改订阅值?使用 rxJS

    我正在创建一个计时器 需要你的帮助 我刚刚学习 Angular 和 rxJS 对此我有一些疑问 我正在创建一个具有启动 停止 暂停 重置功能的计时器 并且 btn Reset 必须将我的计时器 暂停 到 300 毫秒 怎么做 D 我的启动定
  • 如何调试 Gulp 任务?

    如何调试我的中定义的 gulp 任务gulpfile js使用诸如 Google Chrome 调试器之类的调试器逐行单步执行任务的代码 对于 Node js 6 3 版本 您可以使用 inspect flag https nodejs o

随机推荐