• 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ęć

VPS Starter Arubacloud
+1 głos
141 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 (190,190 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 (190,190 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 (190,190 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 (190,190 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ź 110 wizyt
0 głosów
1 odpowiedź 97 wizyt
pytanie zadane 7 września 2020 w JavaScript przez Karol Loczeski Użytkownik (820 p.)
+1 głos
1 odpowiedź 655 wizyt

92,452 zapytań

141,262 odpowiedzi

319,077 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

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

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...