2013-08-19 9 views
7

Questa domanda sembra facile, ma non è così semplice come sembra.Ottieni tutte le immagini da una bacheca da un indirizzo web Pinterest

Breve riassunto di ciò che è sbagliato

Per un esempio, utilizzare questa scheda; http://pinterest.com/dodo/web-designui-and-mobile/

Esaminando il codice HTML per la scheda stessa (all'interno della div con la classe GridItems) nella parte superiore dei rendimenti pagina:

<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> 

Eppure in fondo alla pagina, dopo aver attivato l'infinito di scorrimento di un paio di volte, otteniamo questo come l'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> 

come si può vedere, alcuni dei contenitori per le immagini più in alto sulla pagina sono scomparsi, e non tutti i contenitori per le immagini caricare su di f prima caricando la pagina.


Quello che voglio fare

voglio essere in grado di creare uno script C# (o qualsiasi altro linguaggio lato server al momento) in grado di scaricare HTML completo della pagina (ad esempio, recuperare ogni immagine sulla pagina), e le immagini verranno quindi scaricate dai loro URL. Scaricare la pagina Web e utilizzare l'XPath appropriato è facile, ma la vera sfida è scaricare l'HTML completo per ogni immagine.

C'è un modo per emulare lo scorrimento fino alla fine della pagina o esiste un modo ancora più semplice per recuperare tutte le immagini? Immagino che Pinterest usi AJAX per cambiare l'HTML, c'è un modo in cui posso attivare a livello di codice gli eventi per ricevere tutto l'HTML? Grazie in anticipo per suggerimenti e soluzioni, e complimenti per aver letto anche questa domanda molto lunga se non ne hai nessuna!

pseudo codice

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 
    } 
} 
+0

Carica solo 25 perché carica il resto su richiesta tramite ajax quando si scorre verso il basso, ovvero "Scorrimento infinito". Immagino che dovresti emulare lo scrolling. O se hanno tirato fuori il dito, avrebbero già rilasciato la loro API. – mattytommo

+0

Non c'è modo di poter gestire ciò che viene esattamente quando viene chiamato l'evento AJAX? È un vero peccato per l'API –

+0

Hmm, non credo. Potrebbe essere meglio provare a farlo in JavaScript/Jquery, in questo modo è possibile ottenere tutti i collegamenti, quindi emulare lo scorrimento fino alla fine, quindi, dopo averlo ripetuto fino allo scorrimento, è possibile inviare un array di stringhe al server. – mattytommo

risposta

2

Ok, quindi penso che questo potrebbe essere (con alcune modifiche) quello che ti serve.

Avvertenze:

  1. Questo è PHP, C# non è (ma hai detto che eri interessato a qualsiasi linguaggio lato server).
  2. Questo codice si collega a endpoint di ricerca Pinterest (non ufficiali). Dovrai modificare $ data e $ search_res per riflettere gli endpoint appropriati (ad esempio BoardFeedResouce) per le tue attività. Nota: almeno per la ricerca, Pinterest utilizza attualmente due endpoint, uno per il caricamento iniziale della pagina e un altro per le azioni di scorrimento infinite. Ognuno ha la propria struttura param prevista.
  3. Pinterest non ha API pubbliche ufficiali, si aspettano che si interrompano ogni volta che cambiano qualcosa e senza preavviso.
  4. Potresti trovare pinterestapi.it più facile da implementare e accettabile per quello che stai facendo.
  5. Ho un codice demo/debug sotto la classe che non dovrebbe essere presente una volta ottenuti i dati desiderati e un limite di recupero della pagina predefinito che è possibile modificare.

Punti di interesse:

  1. La sottolineatura _ parametro assume un timestamp in formato JavaScript, vale a dire. come il tempo Unix ma ha aggiunto millisecondi. Non è effettivamente utilizzato per l'impaginazione.
  2. L'impaginazione utilizza la proprietà bookmarks, in modo da effettuare la prima richiesta al 'nuovo' endpoint che non lo richiede, quindi prendere il bookmarks dal risultato e utilizzarlo nella richiesta per ottenere la 'pagina' successiva di risultati, prendi lo bookmarks da quei risultati per recuperare la pagina successiva, e così via fino a quando non finisci i risultati o raggiungi il tuo limite preimpostato (o raggiungi il limite massimo di server per il tempo di esecuzione dello script). Sarei curioso di sapere esattamente cosa codifica il campo bookmarks. Mi piacerebbe pensare che ci sia un po 'di salsa segreta divertente oltre a un ID pin o un altro indicatore di pagina.
  3. Sto saltando l'html, invece di trattare con JSON, perché è più facile (per me) che usare una soluzione di manipolazione DOM, o un po 'di regex.
<?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"; 
    } 
    } 
} 

?> 

Fatemi sapere se si esegue in problemi ottenere questo adattato alle vostre particolari punti finali. Apols per qualsiasi negligenza nel codice, non è stato originariamente prodotto in produzione.

+1

Bene, questa domanda ha richiesto un po 'di tempo per rispondere, ma in pratica l'hai risolta - e fortunatamente ho quasi violato anche quei "segnalibri" sfuggenti. Per esempio, prendendo molte delle stringhe segnalibri e puttingthem attraverso un decoder Base64 dà: -> 18788523419059400: 25 | 77a8c15de91998d843301116b0345928753478fa9ac0b7da855a8eeccb9c1f84 -> 18788523419039267: 49 | 3686b33864aa96a215b28dd5e442afc06e6c76615a8adaae9f6f526432d47d12 che segue il formato: -> {pinID}: {itemNumber} | {random base16 stringa di 64 caratteri} Aiutami a rompere questa ultima parte e penso che lo avremo fatto! –

+0

Bello! Probabilmente non avrò la possibilità di scavare ulteriormente fino a quando non avrò una pausa tra i progetti alla fine di ottobre. Il mio istinto suggerirebbe qualche variazione sui timestamp/data, o forse un hash di qualche porzione di dati per il controllo degli errori, ma quelli sono pugnalate al buio. Rivisiterò quando avrò un momento :) – Skrivener

+0

Apprezzo l'aiuto ogni volta che puoi con questo, non ci sono molti sistemi di hashing esadecimali a 64 caratteri. Ho già provato a codificare -> {pinID}: {articolo #}, {pinID}: {articolo #} e {pinID} in SHA256 e non è stato fruttuoso. Il PHP che hai fornito funziona a prescindere, ma sarebbe bello se fosse completamente programmatico! Grazie ancora per il tuo aiuto in corso :) –

1

Un paio di persone hanno suggerito di usare JavaScript per emulare lo scorrimento.

Non penso che sia necessario emulare lo scrolling, penso che sia necessario trovare il formato degli URI chiamati tramite AJAX ogni volta che si verifica lo scorrimento, e quindi è possibile ottenere ogni "pagina" dei risultati in sequenza. È richiesto un po 'di ingegneria indietro.

Utilizzando la scheda di rete di Chrome ispettore posso vedere che una volta che raggiungo una certa distanza in basso nella pagina, questo URI si chiama:

http://pinterest.com/resource/BoardFeedResource/get/?source_url=%2Fdodo%2Fweb-designui-and-mobile%2F&data=%7B%22options%22%3A%7B%22board_id%22%3A%22158400180582875562%22%2C%22access%22%3A%5B%5D%2C%22bookmarks%22%3A%5B%22LT4xNTg0MDAxMTE4NjcxMTM2ODk6MjV8ZWJjODJjOWI4NTQ4NjU4ZDMyNzhmN2U3MGQyZGJhYTJhZjY2ODUzNTI4YTZhY2NlNmY0M2I1ODYwYjExZmQ3Yw%3D%3D%22%5D%7D%2C%22context%22%3A%7B%22app_version%22%3A%22fb43cdb%22%7D%2C%22module%22%3A%7B%22name%22%3A%22GridItems%22%2C%22options%22%3A%7B%22scrollable%22%3Atrue%2C%22show_grid_footer%22%3Atrue%2C%22centered%22%3Atrue%2C%22reflow_all%22%3Atrue%2C%22virtualize%22%3Atrue%2C%22item_options%22%3A%7B%22show_rich_title%22%3Afalse%2C%22squish_giraffe_pins%22%3Afalse%2C%22show_board%22%3Afalse%2C%22show_via%22%3Afalse%2C%22show_pinner%22%3Afalse%2C%22show_pinned_from%22%3Atrue%7D%2C%22layout%22%3A%22variable_height%22%7D%7D%2C%22append%22%3Atrue%2C%22error_strategy%22%3A1%7D&_=1377092055381

se decodificare che, si vede che è per lo più JSON

http://pinterest.com/resource/BoardFeedResource/get/?source_url=/dodo/web-designui-and-mobile/&data= 
{ 
"options": { 
    "board_id": "158400180582875562", 
    "access": [], 
    "bookmarks": [ 
     "LT4xNTg0MDAxMTE4NjcxMTM2ODk6MjV8ZWJjODJjOWI4NTQ4NjU4ZDMyNzhmN2U3MGQyZGJhYTJhZjY2ODUzNTI4YTZhY2NlNmY0M2I1ODYwYjExZmQ3Yw==" 
    ] 
}, 
"context": { 
    "app_version": "fb43cdb" 
}, 
"module": { 
    "name": "GridItems", 
    "options": { 
     "scrollable": true, 
     "show_grid_footer": true, 
     "centered": true, 
     "reflow_all": true, 
     "virtualize": true, 
     "item_options": { 
      "show_rich_title": false, 
      "squish_giraffe_pins": false, 
      "show_board": false, 
      "show_via": false, 
      "show_pinner": false, 
      "show_pinned_from": true 
     }, 
     "layout": "variable_height" 
    } 
}, 
"append": true, 
"error_strategy": 1 
} 
&_=1377091719636 

Scorrere verso il basso fino a quando si ottiene una seconda richiesta, e noi vediamo questa

http://pinterest.com/resource/BoardFeedResource/get/?source_url=/dodo/web-designui-and-mobile/&data= 
{ 
    "options": { 
     "board_id": "158400180582875562", 
     "access": [], 
     "bookmarks": [ 
      "LT4xNTg0MDAxMTE4NjcwNTk1ODQ6NDl8ODFlMDUwYzVlYWQxNzVmYzdkMzI0YTJiOWJkYzUwOWFhZGFkM2M1MzhiNzA0ZDliZDIzYzE3NjkzNTg1ZTEyOQ==" 
     ] 
    }, 
    "context": { 
     "app_version": "fb43cdb" 
    }, 
    "module": { 
     "name": "GridItems", 
     "options": { 
      "scrollable": true, 
      "show_grid_footer": true, 
      "centered": true, 
      "reflow_all": true, 
      "virtualize": true, 
      "item_options": { 
       "show_rich_title": false, 
       "squish_giraffe_pins": false, 
       "show_board": false, 
       "show_via": false, 
       "show_pinner": false, 
       "show_pinned_from": true 
      }, 
      "layout": "variable_height" 
     } 
    }, 
    "append": true, 
    "error_strategy": 2 
} 
&_=1377092231234 

Come potete vedere, non è cambiato molto. Il Board_id è lo stesso. error_strategy ora è 2 e il & _ alla fine è diverso.

Il parametro & _ è la chiave qui. Scommetto che dice alla pagina dove iniziare la prossima serie di foto. Non riesco a trovare un riferimento ad esso in una delle risposte o nel codice HTML della pagina originale, ma deve trovarsi lì da qualche parte o essere generato da javascript sul lato client. In ogni caso, la pagina/browser deve sapere cosa chiedere in seguito, quindi questa informazione è qualcosa che dovresti essere in grado di ottenere.

+0

Grazie mille per questa risposta - è informativo, ma non abbastanza informativo. Ho davvero scavato duro, ed è un peccato che sono così perplesso, visto che anch'io ho trovato questo copione JSON e mi sono chiesto cosa stesse succedendo. Notare anche che il valore dei "segnalibri" cambia - un altro mistero. Ho messo una taglia di 50 rep per una risposta che può dirmi quale parte esatta di JSON causa questi aggiornamenti e come attivarla. Anch'io credo che le informazioni JSON da sole dovrebbero consentirmi di richiedere questi URL, restituire l'HTML e identificare le immagini e quindi codificare l'URL dei caratteri JSON fino alla fine del bordo. –

0

È possibile attivare l'endpoint JSON facendo una richiesta con questa intestazione: X-Requested-With:XMLHttpRequest

Prova questa in comando nella console:

curl -H "X-Requested-With:XMLHttpRequest" "http://pinterest.com/resource/CategoryFeedResource/get/?source_url=%2Fall%2Fgeek%2F&data=%7B%22options%22%3A%7B%22feed%22%3A%22geek%22%2C%22scope%22%3Anull%2C%22bookmarks%22%3A%5B%22Pz8xMzc3NjU4MjEyLjc0Xy0xfDE1ZjczYzc4YzNlNDg3M2YyNDQ4NGU1ZTczMmM0ZTQyYzBjMWFiMWNhYjRhMDRhYjg2MTYwMGVkNWQ0ZDg1MTY%3D%22%5D%2C%22is_category_feed%22%3Atrue%7D%2C%22context%22%3A%7B%22app_version%22%3A%22addc92b%22%7D%2C%22module%22%3A%7B%22name%22%3A%22GridItems%22%2C%22options%22%3A%7B%22scrollable%22%3Atrue%2C%22show_grid_footer%22%3Atrue%2C%22centered%22%3Atrue%2C%22reflow_all%22%3Atrue%2C%22virtualize%22%3Atrue%2C%22item_options%22%3A%7B%22show_pinner%22%3Atrue%2C%22show_pinned_from%22%3Afalse%2C%22show_board%22%3Atrue%2C%22show_via%22%3Afalse%7D%2C%22layout%22%3A%22variable_height%22%7D%7D%2C%22append%22%3Atrue%2C%22error_strategy%22%3A2%7D&module_path=App()%3EHeader()%3EDropdownButton()%3EDropdown()%3ECategoriesMenu(resource%3D%5Bobject+Object%5D%2C+name%3DCategoriesMenu%2C+resource%3DCategoriesResource(browsable%3Dtrue))&_=1377658213300" | python -mjson.tool 

potrete vedere i dati dei pin nel JSON emesso. Dovresti essere in grado di analizzarlo e catturare le immagini successive di cui hai bisogno.

Per quanto riguarda questo bit: &_=1377658213300. Immagino che questo sia l'id dell'ultimo pin dell'elenco precedente. Dovresti essere in grado di sostituirlo ad ogni chiamata con l'ultimo pin della risposta precedente.

0
#!/usr/bin/env bash 
## 
## File: getpins.bsh 
## 
## Copyrighted by +A.M.Danischewski 2016+ (c) 
## This program may be reutilized without limits, provided this 
## notice remain intact. 

## If this breaks one day, then just fire up firefox Developer Tools and check the network traffic to 
## capture "copy as curl" of the calls to the search page (filter with BaseSearchResource), then the 
## call to feed more data (filter with SearchResource). 
## 
## Do a search on whatever you want remove the cookie header, and add -o ret2.html -D h2.txt -c c1.txt, 
## then search replace the search terms as SEARCHTOKEN1 and SEARCHTOKEN2. 
## 
## Description this script facilitates alternate browsers, by caching images/pins 
## from pinterest. This script is hardwired for two search terms. First create a directory 
## to where you want the images to go, then cd there. 
## Usage: 
## $> cd /big/drive/auto_gyros 
## $> getpins.bsh "sleek autogyros" 
## 
## Expect around 900 images to land wherever you select, so make sure you have space! =) 
## 

declare -r ORIG_IMGS="pin_orig_imgs.txt" 
declare -r TMP_IMGS="pin_imgs.txt" 
declare -r UA_HEADER="User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:19.$(($RANDOM%10))) Gecko/20100101 Firefox/19.0" 

## Say Hello to the main page and get a cookie. 
declare PINCMD1=$(cat << EOF 
curl -o ret1.html -D h1.txt -c c1.txt -H 'Host: www.pinterest.com' -H '${UA_HEADER}' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Connection: keep-alive' 'https://www.pinterest.com/' 
EOF 
) 
## Start a search for our dear search terms. 
declare PINCMD2=$(cat << EOF 
curl -H 'X-APP-VERSION: ea7a93a' -o ret2.html -D h2.txt -c c1.txt -H 'Host: www.pinterest.com' -H '${UA_HEADER}' -H 'Accept: application/json, text/javascript, */*; q=0.01' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'X-Pinterest-AppState: active' -H 'X-NEW-APP: 1' -H 'X-Requested-With: XMLHttpRequest' -H 'Referer: https://www.pinterest.com' -H 'Connection: keep-alive' 'https://www.pinterest.com/resource/BaseSearchResource/get/?source_url=%2Fsearch%2Fpins%2F%3Fq%3DSEARCHTOKEN1%2520SEARCHTOKEN2%26rs%3Dtyped%260%3DSEARCHTOKEN1%257Ctyped%261%3DSEARCHTOKEN2%257Ctyped&data=%7B%22options%22%3A%7B%22restrict%22%3Anull%2C%22scope%22%3A%22pins%22%2C%22constraint_string%22%3Anull%2C%22show_scope_selector%22%3Atrue%2C%22query%22%3A%22SEARCHTOKEN1+SEARCHTOKEN2%22%7D%2C%22context%22%3A%7B%7D%2C%22module%22%3A%7B%22name%22%3A%22SearchPage%22%2C%22options%22%3A%7B%22restrict%22%3Anull%2C%22scope%22%3A%22pins%22%2C%22constraint_string%22%3Anull%2C%22show_scope_selector%22%3Atrue%2C%22query%22%3A%22SEARCHTOKEN1+SEARCHTOKEN2%22%7D%7D%2C%22render_type%22%3A1%2C%22error_strategy%22%3A0%7D&module_path=App%3EHeader%3ESearchForm%3ETypeaheadField(support_guided_search%3Dtrue%2C+resource_name%3DAdvancedTypeaheadResource%2C+tags%3Dautocomplete%2C+class_name%3DbuttonOnRight%2C+prefetch_on_focus%3Dtrue%2C+support_advanced_typeahead%3Dnull%2C+hide_tokens_on_focus%3Dundefined%2C+search_on_focus%3Dtrue%2C+placeholder%3DSearch%2C+show_remove_all%3Dtrue%2C+enable_recent_queries%3Dtrue%2C+name%3Dq%2C+view_type%3Dguided%2C+value%3D%22%22%2C+input_log_element_type%3D227%2C+populate_on_result_highlight%3Dtrue%2C+search_delay%3D0%2C+is_multiobject_search%3Dtrue%2C+type%3Dtokenized%2C+enable_overlay%3Dtrue)&_=1454779874891' 
EOF 
) 
## Load further images. 
declare PINCMD3=$(cat << EOF 
curl -H 'X-APP-VERSION: ea7a93a' -D h3.txt -c c1.txt -H 'Host: www.pinterest.com' -H '${UA_HEADER}' -H 'Accept: application/json, text/javascript, */*; q=0.01' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'X-Pinterest-AppState: active' -H 'X-NEW-APP: 1' -H 'X-Requested-With: XMLHttpRequest' -H 'Referer: https://www.pinterest.com' -H 'Connection: keep-alive' 'https://www.pinterest.com/resource/SearchResource/get/?source_url=%2Fsearch%2Fpins%2F%3Fq%3DSEARCHTOKEN1%2520SEARCHTOKEN2%26rs%3Dtyped%260%3DSEARCHTOKEN1%257Ctyped%261%3DSEARCHTOKEN2%257Ctyped&data=%7B%22options%22%3A%7B%22layout%22%3Anull%2C%22places%22%3Afalse%2C%22constraint_string%22%3Anull%2C%22show_scope_selector%22%3Atrue%2C%22query%22%3A%22SEARCHTOKEN1+SEARCHTOKEN2%22%2C%22scope%22%3A%22pins%22%2C%22bookmarks%22%3A%5B%22_NEW_BOOK_MARK_%22%5D%7D%2C%22context%22%3A%7B%7D%7D&module_path=App%3EHeader%3ESearchForm%3ETypeaheadField(support_guided_search%3Dtrue%2C+resource_name%3DAdvancedTypeaheadResource%2C+tags%3Dautocomplete%2C+class_name%3DbuttonOnRight%2C+prefetch_on_focus%3Dtrue%2C+support_advanced_typeahead%3Dnull%2C+hide_tokens_on_focus%3Dundefined%2C+search_on_focus%3Dtrue%2C+placeholder%3DSearch%2C+show_remove_all%3Dtrue%2C+enable_recent_queries%3Dtrue%2C+name%3Dq%2C+view_type%3Dguided%2C+value%3D%22%22%2C+input_log_element_type%3D227%2C+populate_on_result_highlight%3Dtrue%2C+search_delay%3D0%2C+is_multiobject_search%3Dtrue%2C+type%3Dtokenized%2C+enable_overlay%3Dtrue)&_=1454779874911' 
EOF 
) 
## Exactly 2 search terms in a single string are expected, you can hack it up if 
## you want something else. 
declare SEARCHTOKEN1=$(echo "${1}" | cut -d " " -f1) 
declare SEARCHTOKEN2=$(echo "${1}" | cut -d " " -f2) 

PINCMD3=$(sed "s/SEARCHTOKEN1/${SEARCHTOKEN1}/g" <<< "${PINCMD3}") 
PINCMD3=$(sed "s/SEARCHTOKEN2/${SEARCHTOKEN2}/g" <<< "${PINCMD3}") 
PINCMD2=$(sed "s/SEARCHTOKEN1/${SEARCHTOKEN1}/g" <<< "${PINCMD2}") 
PINCMD2=$(sed "s/SEARCHTOKEN2/${SEARCHTOKEN2}/g" <<< "${PINCMD2}") 

function lspinimgs() { grep -o "\"url\": \"http[s]*://[^\"]*.pinimg.com[^\"]*.jpg\"" "${1}" | cut -d " " -f2 | tr -d "\""; } 
function mkpinorig() { sed "s#\(^http.*\)\(com/\)\([^/]*\)\(/.*jpg\$\)#\1\2originals\4#g" "${1}" > "${2}"; }  
function getpinbm() { grep -o "bookmarks\": [^ ]* " "${1}" | sed "s/^book.*\[\"//g;s/\"\].*\$//g" | sort | uniq | grep -v "-end-"; } 
function changepinbm() { PINCMD3=$(sed "s/\(^.*\)\(bookmarks%22%3A%5B%22\)\(.*\)\(%22%5D.*\$\)/\1\2${1}\4/g" <<< "${PINCMD3}"); } 
function cleanup() { rm ret*html c1.txt "${TMP_IMGS}" h{1..3}.txt "${ORIG_IMGS}"; } 

function main() { 
eval "${PINCMD1}" 
eval "${PINCMD2}" 
for ((i=3,lasti=2; i<10000; i++,lasti++)); do 
pinbm=$(getpinbm "ret${lasti}.html") 
[[ -z "${pinbm}" ]] && break 
changepinbm "${pinbm}" 
eval "${PINCMD3}" > "ret${i}.html" 
done 
for a in *.html; do lspinimgs "${a}" >> "${TMP_IMGS}"; done 
mkpinorig "${TMP_IMGS}" "${ORIG_IMGS}" 
IFS=$(echo -en "\n\b") && for a in $(sort "${ORIG_IMGS}" | uniq); do 
wget --tries=3 -E -e robots=off -nc --random-wait --content-disposition --no-check-certificate -p --restrict-file-names=windows,lowercase,ascii --header "${UA_HEADER}" -nd "$a" 
done 
cleanup 
} 

main 
exit 0