2013-08-19 10 views
7

To pytanie brzmi łatwo, ale nie jest tak proste, jak się wydaje.Pobierz wszystkie obrazy z planszy z adresu strony internetowej Pinterest

Krótkie podsumowanie tego, co się dzieje

Dla przykładu, użyj tego forum; http://pinterest.com/dodo/web-designui-and-mobile/

Badanie HTML dla samej płycie (wewnątrz div z klasą GridItems) w górnej części plonów stronie:

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

Jeszcze na dole strony, po aktywacji nieskończone przewijanie kilka razy, mamy to jako 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> 

jak widać, niektóre pojemniki na obrazach wyżej na stronie zniknęły, a nie wszystkie pojemniki na obrazach załadować na f irst ładuje stronę.


Co chcę zrobić

Chcę móc utworzyć skrypt C# (lub dowolnego języka po stronie serwera w tej chwili), które można pobrać stronę w pełnej HTML (tj pobrać każdy obraz na stronie), a obrazy zostaną pobrane z ich adresów URL. Pobieranie strony internetowej i używanie odpowiedniego XPath jest łatwe, ale prawdziwym wyzwaniem jest pobranie pełnego kodu HTML dla każdego obrazu.

Czy istnieje sposób, w jaki mogę emulować przewijanie na dole strony, czy jest jeszcze łatwiejszy sposób na odzyskanie każdego obrazu? Wyobrażam sobie, że Pinterest używa AJAX do zmiany kodu HTML, czy jest sposób, w jaki mogę programowo wywołać zdarzenia, aby otrzymać cały HTML? Z góry dziękuję za sugestie i rozwiązania, a nawet za przeczytanie tego bardzo długiego pytania, jeśli go nie masz!

kod Pseudo

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

Załaduje się tylko 25, ponieważ ładuje resztę na żądanie za pośrednictwem ajax, kiedy przewiń do dołu, znany również jako "Infinite Scroll". Sądzę, że musiałbyś naśladować to przewijanie. A jeśli wyciągnęliby palec, dostali już swoje API. – mattytommo

+0

Czy nie ma sposobu, abym sobie poradził z tym, co dokładnie dzieje się podczas wywoływania zdarzenia AJAX? To wstyd o API –

+0

Hmm, nie sądzę. Lepiej możesz spróbować zrobić to w JavaScript/Jquery, w ten sposób możesz uzyskać wszystkie łącza, a następnie emulować przewijanie do końca, a następnie po powtórzeniu aż do zakończenia przewijania możesz wysłać tablicę ciągów na serwer. – mattytommo

Odpowiedz

2

Okej, więc myślę, że to może być (z kilkoma zmianami) to, czego potrzebujesz.

Ostrzeżenia:

  1. To jest PHP, C# nie (ale mówiłeś, że byli zainteresowani w każdym języku po stronie serwera).
  2. Ten kod jest podpięty do (nieoficjalnych) punktów końcowych wyszukiwania Pinterest. Będziesz musiał zmienić $ dane i $ search_res, aby odzwierciedlić odpowiednie punkty końcowe (np. BoardFeedResouce) dla twoich zadań. Uwaga: co najmniej do wyszukiwania, Pinterest obecnie używa dwóch punktów końcowych, jeden do początkowego załadowania strony, a drugi do nieskończonego przewijania. Każdy ma swoją oczekiwaną strukturę param.
  3. Pinterest nie ma oficjalnego publicznego interfejsu API, spodziewaj się, że ulegnie on awarii, gdy tylko coś zmieni, i bez ostrzeżenia.
  4. Możesz znaleźć pinterestapi.co.uk łatwiejsze do wdrożenia i akceptowalne dla tego, co robisz.
  5. Mam kod demo/debugowania pod klasą, którego nie powinno tam być, gdy otrzymujesz żądane dane, oraz domyślny limit pobierania strony, który możesz chcieć zmienić.

Ciekawostki:

  1. podkreślenia _ parametr bierze znacznik czasu w formacie JavaScript, tj. podobnie jak czas Unix, ale ma dodane milisekundy. W rzeczywistości nie jest on używany do stronicowania.
  2. Paginacja korzysta z właściwości bookmarks, więc najpierw wysyłasz żądanie do "nowego" punktu końcowego, który tego nie wymaga, a następnie odbierz bookmarks od wyniku i użyj go w żądaniu, aby uzyskać następną stronę " wyników, pobierz bookmarks z tych wyników, aby pobrać następną stronę i tak dalej, aż skończy się wyniki lub osiągnie ustawiony limit (lub osiągniesz maksymalny czas serwera dla czasu wykonania skryptu). Chciałbym wiedzieć, co dokładnie koduje pole bookmarks. Chciałbym myśleć, że jest jakiś zabawny sekretny sos poza zwykłym identyfikatorem PIN lub jakimś innym znacznikiem strony.
  3. Opuszczam html, zamiast zajmować się JSON-em, ponieważ jest to łatwiejsze (dla mnie) niż przy użyciu rozwiązania do manipulacji DOM lub wiązki wyrażeń regularnych.
<?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"; 
    } 
    } 
} 

?> 

Daj mi znać, jeśli napotkasz problemy coraz to dostosowane do poszczególnych punktów końcowych. Apole za wszelkie niechlujstwa w kodzie, początkowo nie doszło do produkcji.

+1

Cóż, to pytanie zajęło trochę czasu, aby odpowiedzieć, ale w zasadzie go rozwiązałeś - i na szczęście prawie złamałem też te nieuchwytne "zakładki". Na przykład, biorąc kilka strun zakładek i puttingthem poprzez dekoder base64 daje: -> 18788523419059400: 25 | 77a8c15de91998d843301116b0345928753478fa9ac0b7da855a8eeccb9c1f84 -> 18788523419039267: 49 | 3686b33864aa96a215b28dd5e442afc06e6c76615a8adaae9f6f526432d47d12 Które następujący format: -> {pinID} {Nummer artykułu} | {random base16 ciąg 64 znaków} Pomóż mi złamać tę ostatnią część i myślę, że to zrobimy! –

+0

Nice! Prawdopodobnie nie będę miał szansy na dalsze kopanie, dopóki nie dostanę przerwy między projektami pod koniec października. Mój instynkt podpowiadałby jakąś odmianę znaczników czasu/daty, a może mieszankę jakiejś części danych do sprawdzania błędów, ale są to pchnięcia w ciemności. Powrócę, gdy tylko nadejdzie chwila :) – Skrivener

+0

Doceniam pomoc, kiedy tylko możesz, nie ma zbyt wielu 64-znakowych systemów szastania heksadecymalnego. Próbowałem już kodować -> {pinID}: {element #}, {pinID}: {element #} i {pinID} w SHA256, co nie było owocne. Podany przez ciebie PHP działa niezależnie, ale byłoby miło, gdyby to było w pełni programistyczne! Jeszcze raz dziękuję za bieżącą pomoc :) –

1

Kilka osób sugerowało użyciu javascript, aby emulować przewijanie.

Nie wydaje mi się, że trzeba emulować przewijanie w ogóle, myślę, że po prostu trzeba znaleźć format identyfikatorów URI wywoływanych przez AJAX, gdy pojawia się przewijanie, a następnie można uzyskać kolejno każdą "stronę" wyników. Wymagana jest niewielka inżynieria wsteczna.

Stosując kartę sieciową Chrome inspektora widzę, że kiedyś osiągnie pewną odległość w dół strony, to URI nazywa się:

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

gdybyśmy dekodować że widzimy, że to głównie 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 

Przewiń w dół, aż otrzymamy drugi wniosek, i widzimy to

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 

Jak widać, niewiele się zmieniło. Nazwa Board_id jest taka sama. error_strategy ma teraz 2, a & _ na końcu jest inna.

Kluczem jest tutaj parametr & _. Założę się, że informuje stronę, od której należy rozpocząć następny zestaw zdjęć. Nie mogę znaleźć odniesienia do niego w żadnej z odpowiedzi lub oryginalnego kodu HTML strony, ale musi on być gdzieś tam lub zostać wygenerowany przez javascript po stronie klienta. Tak czy inaczej, strona/przeglądarka musi wiedzieć, o co poprosić w przyszłości, więc ta informacja jest czymś, do czego powinieneś mieć dostęp.

+0

Dziękuję bardzo za tę odpowiedź - jest to informacja, ale nie wystarczająco informatywna. Naprawdę ciężko kopałem i to niefortunne, że jestem tak zakłopotany, ponieważ ja też natknąłem się na ten skrypt JSON i zastanawiałem się, co się dzieje. Zauważ też, że wartość "zakładek" zmienia się - kolejna zagadka. Złożyłem nagrodę w wysokości 50 kopii za odpowiedź, która może mi powiedzieć, jaka dokładnie część JSON powoduje te aktualizacje i jak je uruchomić. Ja również uważam, że sama informacja JSON powinna pozwolić mi na żądanie tych adresów URL, zwrócenie kodu HTML i identyfikację obrazów, a następnie zakodowanie adresu URL znaków JSON, aż do końca tablicy. –

0

można wywołać punkt końcowy json składając wniosek z tego nagłówka: X-Requested-With:XMLHttpRequest

Spróbuj to w komendzie w konsoli:

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 

Zobaczysz dane PIN w podawanych JSON. Powinieneś być w stanie przetworzyć go i pobrać następne obrazy, które potrzebujesz.

Co do tego fragmentu: &_=1377658213300. Spekuluję, że jest to identyfikator ostatniego pinu z poprzedniej listy. Powinieneś być w stanie zastąpić to przy każdym wywołaniu ostatnim pinem z poprzedniej odpowiedzi.

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 
Powiązane problemy