• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Cheerio web scraping - jak zapisać do zmiennej tablicowej atrybuty src załadowanych zdjęć

+1 głos
34 wizyt
pytanie zadane 4 dni temu w JavaScript przez fyrr Użytkownik (910 p.)

cheerio, express.js


Witam
Chcę przechować w zmiennej premieres m.in. informacje dot. atrybutu src elementów zdjęć. Problem polega na tym, że na stronie domyślnie jest coś w rodzaju lazy loadingu i początkowo atrybutem src jest link do placeholdera, który zmienia się dopiero po zaczytaniu strony: 
https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png
W jaki sposób mogę dostać się do danych tego atrybutu dopiero, gdy zdjęcia się załadują? Mowa tu o podstronie: https://www.imdb.com/showtimes/location?ref_=inth_mny_sm&sort=release_date,desc&st_dt=2021-10-11&mode=showtimes_grid&page=1

1. crawler.js

import * as cheerio from "cheerio";
import request from "request";

const fetchPremieresData = () => {
  return new Promise((resolve, reject) => {
    request(
      "https://www.imdb.com/showtimes/location?ref_=inth_mny_sm&sort=release_date,desc&st_dt=2021-10-11&mode=showtimes_grid&page=1",
      (error, response, html) => {
        let premieres = [];
        if (!error && response.statusCode === 200) {
          const $ = cheerio.load(html);
          $("div.title").each((i, el) => {
            const title = $(el).text();
            premieres.push({ id: i, title });
          });
          $("img.loadlate").each((i, el) => {
            const cover = $(el).attr("src");
            premieres[i].cover = cover;
          });
          resolve(premieres);
          console.log(JSON.stringify(premieres));
        } else {
          reject(error);
          console.error(error);
        }
      }
    );
  });
};

export default fetchPremieresData;

2. server.js:

app.get("/premieres", (req, res) => {
  const premieresData = fetchPremieresData();
  try {
    const showPremieres = async () => {
      const data = await premieresData;
      res.send(data);
    };
    showPremieres();
  } catch (err) {
    console.error(err);
  }
});

3. response:

// http://localhost:1410/premieres

[
  {
    "id": 0,
    "title": "Nędzarz i madame",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 1,
    "title": "Pitbull",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 2,
    "title": "To musi być miłość",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 3,
    "title": "Cuidado con lo que deseas",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 4,
    "title": "Pan de limón con semillas de amapola",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 5,
    "title": "Furioza",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 6,
    "title": "Eternals",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 7,
    "title": "Poroże",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },
  {
    "id": 8,
    "title": "Pogromcy duchów. Dziedzictwo",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },

...


 


 

 

1 odpowiedź

+1 głos
odpowiedź 4 dni temu przez ScriptyChris Mędrzec (168,320 p.)
wybrane 4 dni temu przez fyrr
 
Najlepsza

Z tego, co udało mi się zaobserwować wynika, że początkowo obrazki mają docelowe źródło umieszczone w atrybucie data-tconst, a atrybut src jest podmieniany, gdy wystąpi event bindloadlate - jest to jakiś customowy event triggerowany przez jQuery (np. w tym skrypcie).

function(a) {
    a(window).on("bindloadlate", function(b) {
        a("img.loadlate").appear(function() { // <-- podmiana [src] obrazka na event
              var b = a(this)
              , c = b.attr("loadlate");
            window.devicePixelRatio && window.devicePixelRatio > 1 && (b.attr("data-src-x2") ? c = b.attr("data-src-x2") : b.attr("data-src-x2lateload") && (replacementStr = b.attr("data-src-x2lateload"))),
            c && (b.attr("src", c),
            b.removeAttr("loadlate"));
            var d = b.attr("data-widget");
            d && "/" === d[0] && b.removeAttr("data-widget").parent().load(d)
        }),
        a("img.loadlate.hidden").removeClass("hidden")
    }),
    a(window).trigger("bindloadlate")
}(jQuery)

Można podpiąć się swoim event handlerem poprzez jQuery.on i tam rzeźbić, albo można podpiąć się MutationObserverem na każdy obrazek lub ich wspólnego parenta i obserwować zmianę atrybutu src każdego obrazka za pomocą parametru attributes.

komentarz 4 dni temu przez fyrr Użytkownik (910 p.)
Rozumiem, że jeśli chciałbym użyć MutationObserver to, gdy tylko zachodziłaby zmiana w parametrze attributes, musiałbym na nowo pobrać "aktualne" dane z src elementu i przypisać do zmiennej?
komentarz 4 dni temu przez ScriptyChris Mędrzec (168,320 p.)
To zależy, co chcesz zrobić z tymi danymi? Jeśli chcesz je przechować "na później", to tak, możesz je sobie przypisać do zmiennej z wyższego scope (poza callbackiem do mutation observera).
komentarz 4 dni temu przez fyrr Użytkownik (910 p.)

Chcę zrobić tak żebym w kluczu cover zawsze miał aktualną wartość atrybutu src elementu img. 

 

[
  {
    "id": 0,
    "title": "Nędzarz i madame",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png" 
  },
  {
    "id": 1,
    "title": "Pitbull",
    "cover": "https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png"
  },

itd...

 

komentarz 4 dni temu przez ScriptyChris Mędrzec (168,320 p.)

A to jest Twoja tablica, czy zmapowana lista mutacji dostarczona przez observer? Jeśli Twoja tablica, to wystarczy, że zaktualizujesz property cover z nową wartością. Kwestia tylko znalezienia odpowiedniego elementu w tablicy - możesz odnosić się do jakiegoś niezmiennego atrybutu obrazka, jak np. title i po nim szukać obiektu w tablicy.

komentarz 4 dni temu przez fyrr Użytkownik (910 p.)

To jest moja tablica o nazwie premieres. Na początku jest pusta. Tworzę ją sobie w pliku cravler.js i pushuję do niej wartości kluczy w taki sposób, pobierając dane ze żródła strony: 

 

 let premieres = [];
        if (!error && response.statusCode === 200) {
          const $ = cheerio.load(html);
          $("div.title").each((i, el) => {
            const title = $(el).text();
            premieres.push({ id: i, title });
          });
          $("img.loadlate").each((i, el) => {
            const cover = $(el).attr("src");
            premieres[i].cover = cover;
          });

Wszystko ładnie działa, tylko tak jak właśnie napisałeś -> muszę zaaktualizować wartość klucza cover, bo domyślnie atrybut src dla img przyjmuje wartość placeholdera na zdjęcie, a ja chcę właśnie mieć tam wartość docelową (czyli zdjęcie po załadowaniu). 

komentarz 4 dni temu przez ScriptyChris Mędrzec (168,320 p.)

Zatem, jak już sobie ogarniesz odczytywanie właściwego źródła obrazka (przy pomocy MutationObserver lub podpinając się pod event bindloadlate lub może znajdziesz inny sposób), to robisz coś w stylu:

const matchedImage = /* 
  tutaj podstawiasz obrazek, gdy ogarniesz, że załadowało się docelowe źródło
*/;

premieres[
 premieres.findIndex(( item ) => item.title === matchedImage.title)
].cover = matchedImage.src;

 

komentarz 4 dni temu przez fyrr Użytkownik (910 p.)
przywrócone 2 dni temu przez fyrr
Wielkie dzięki mistrzu ;)
1
komentarz 2 dni temu przez fyrr Użytkownik (910 p.)

@ScriptyChris, Nie trzeba było nawet ustawiać MutationObservera ;) Wystarczyło zmienić nazwę atrybutu z src na loadlate. Myślałem, że nie mam do niego dostępu, ale jednak był bardzo łatwy dostęp: 
 

'53': <ref *54> Node {
    type: 'tag',
    name: 'img',
    namespace: 'http://www.w3.org/1999/xhtml',
    attribs: [Object: null prototype] {
      alt: 'Zemlya Elzi',
      class: 'loadlate',
      loadlate: 'https://m.media-amazon.com/images/M/MV5BMDZhMThlZGQtMWVkNi00ZDJjLThkODItOGFjYTE1NjRhNzM2XkEyXkFqcGdeQXVyMjM5NTAwNzk@._V1_UY209_CR4,0,140,209_AL_.jpg',
      'data-tconst': 'tt11872836',
      height: '209',
      src: 'https://m.media-amazon.com/images/S/sash/4FyxwxECzL-U1J8.png',
      width: '140'
    },

Podmieniłem, więc tylko to: 
 

 const cover = $(el).attr("loadlate");
            premieres[i].cover = cover;

I śmiga! Jeszcze raz dzięki za wskazówki, bo dzięki Tobie to znalazłem ;) 

Podobne pytania

0 głosów
1 odpowiedź 69 wizyt
0 głosów
1 odpowiedź 50 wizyt
pytanie zadane 7 września 2020 w JavaScript przez Karol Loczeski Użytkownik (820 p.)
+1 głos
0 odpowiedzi 54 wizyt
Porady nie od parady
Forum posiada swój własny serwer Discord, dzięki któremu będziesz mógł po prostu pogadać z innymi Pasjonatami lub zapytać o jakiś problem. Podstrona z chatem znajduje się w menu pod ikoną człowieka w dymku.IRC

85,709 zapytań

134,503 odpowiedzi

298,532 komentarzy

56,629 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...