• 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
73 wizyt
pytanie zadane 23 listopada 2021 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ź 23 listopada 2021 przez ScriptyChris Mędrzec (181,080 p.)
wybrane 23 listopada 2021 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 23 listopada 2021 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 23 listopada 2021 przez ScriptyChris Mędrzec (181,080 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 23 listopada 2021 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 23 listopada 2021 przez ScriptyChris Mędrzec (181,080 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 23 listopada 2021 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 23 listopada 2021 przez ScriptyChris Mędrzec (181,080 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 23 listopada 2021 przez fyrr Użytkownik (910 p.)
przywrócone 25 listopada 2021 przez fyrr
Wielkie dzięki mistrzu ;)
1
komentarz 25 listopada 2021 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ź 78 wizyt
0 głosów
1 odpowiedź 56 wizyt
pytanie zadane 7 września 2020 w JavaScript przez Karol Loczeski Użytkownik (820 p.)
+1 głos
0 odpowiedzi 83 wizyt

88,686 zapytań

137,299 odpowiedzi

306,685 komentarzy

58,876 pasjonatów

Motyw:

Akcja Pajacyk

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

Sklep oferujący ćwiczenia JavaScript, PHP, rozmowy rekrutacyjne dla programistów i inne materiały

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

...