Allegro Seller Info 0.1.8

tech • 717 słów • 4 minuty czytania

Nastąpiły kolejne zmiany na stronach serwisu Allegro.pl, więc przyszła pora na szybkie poprawki w moim skrypcie.

Wczoraj zauważyłem brak informacji dodawanych przez skrypt na listach aukcji. Po szybkim wybadaniu sprawy okazało się, że nieszczęsny obiekt __listing_StoreState_base używany do wykrywania strony z listą zniknął z głównej przestrzeni nazw. Na szczęście, podobnie jak to miało miejsce wcześniej na stronach przedmiotów (czego dotyczyła poprawka w wersji 0.1.7) bez problemu można było oprzeć się na strukturze strony i prostym selektorem załatwić sprawę.

Poprzednio używany w dalszej części skryptu element z klasą opbox-listing--base nadal istnieje i zawiera w sobie listę ofert. Dostał teraz dedykowany identyfikator, ale wciąż występuje tylko na stronach z listami, więc aktualne wykrywanie takich podstron serwisu wygląda tak:

// strona z lista aukcji
const listing = document.getElementById('opbox-listing--base');
if (listing) { }

Taka ciekawostka, ten nieszczęsny obiekt bazowy, który do tej pory leżał gdzieś w kodzie JS strony tak naprawdę nie zniknął definitywnie. Przebrandowano go na jakiś JSON-owy kod i zapewne nadal jest wykorzystywany przy tworzeniu i manipulowaniu bazową listą aukcji (tą widoczną) po bezpośrednim załadowaniu strony.

To w sumie nie jest zbyt istotne, ale zmiana ta w pewnych okolicznościach wpływa na błędne zachowanie skryptu. Objawia się to tym, że czasami po załadowaniu strony skrypt nie dodaje informacji o lokalizacji. Dzieje się to z powodu braku reactowych “internalsów” (z których to dane czerpie skrypt) w elementach listy w czasie wykonania skryptu. Ewidentnie czai się tu jakiś wyścig przy wykonywaniu kodu skryptu z tym ze strony.

Zapewne związane jest to z tym JSON-em, który teraz musi zostać sparsowany i przemielony, a to dokonuje się jakoś asynchronicznie po załadowaniu strony. Udało mi się na szybko zdebugować kilka fragmentów kodu i ustalić, że po tym “przemieleniu” kontener zawierający listę ofert dostaje pole z _reactRootContainer z jakimiś tam obiektami. Fakt ten wykorzystałem do odświeżenia listy:

function UpdateListBase() {
	if (listing._reactRootContainer)
		UpdateList();
	else
		setTimeout(UpdateListBase, 100);
}

UpdateListBase();

Teraz zamiast bezpośredniego wywołania funkcji UpdateList na koniec działania skryptu w kontekście strony listującej aukcje używany jest helper UpdateListBase. Sprawdza on czy już nadszedł czas na “odświeżenie” i “ulepszenie” listy. A jeśli nie to ponawia swoje wykonanie za 100 milisekund. Tym prostym hackiem udało się wygrać wyścig…

Przy okazji nowej wersji postanowiłem naprawić też inny, nieco stary problem, który zauważałem już jakiś czas temu, ale nie miałem okazji go ogarnąć. Czasami wykonanie kodu niekoniecznie kończy się wyświetleniem informacji o lokalizacji, ale psuje wygląd strony, co można zobaczyć na poniższym obrazku.

Wykluczając tezę, że to może “nie mój problem”, ani jakieś akcje z kodem allegro, CSS-ami, czy innymi zcacheowanymi danymi, natrafiłem na mój genialny mechanizm wykrywania przebudowania części informacyjnej aukcji1.

var mutationObserver = new MutationObserver(function (mutations) {
	var delEvt = mutations.findIndex(m => m.removedNodes.length != 0) != -1;
	if (delEvt && itemNode.getElementsByClassName(cssName).length == 0)
		UpdateOffer();
});
mutationObserver.observe(itemNode, { childList: true, subtree: true });

Kod strony przebudowuje w run-time niektóre elementy opisowe aukcji, akurat tam gdzie skrypt dodaje swoje wstawki. Do tej pory odświeżanie wyglądało mniej więcej tak, że najpierw usuwane były zbędne elementy (w tym wstrzyknięty kod) i dodawane nowe. Obecnie po zajrzeniu do DOM-a po zreprodukowaniu problemu zauważyłem coś dziwnego.

Jak pokazuje powyższy obrazek, główny kontener przechowujący dodane elementy został potraktowany jako jeden z elementów strony i go nie usunięto, a wykorzystano i zmodyfikowano. To wszystko tłumaczy dlaczego dodane elementy zniknęły ze strony i nie wykryto tego faktu przez obserwatora, a także czemu wygląd się rozjechał2.

Po kilku eksperymentach ustaliłem, że przy użyciu innej nazwy lub struktury, wstrzyknięty element nie zostanie omyłkowo wykorzystany przez kod strony. Ostatecznie zdecydowałem się na zamianę kontenera <div class="asiXXX"/> z losową klasą stylującą na dedykowany element z losową nazwą tagu <asiXXX/>. To zapewni, że w przyszłości nie powinien powtórzyć się podobny scenariusz i nie powinno być żadnej potencjalnej interakcji z kodem strony.

Nie będę tutaj wspominał o drobnych szczegółach, takich jak poprawki w CSS-ach i stylach, które to pozwalają na lepsze wkomponowanie się dodanych elementów z resztą strony.

Aktualna wersja dostępna w moim repozytorium UserScripts. Standardowo aktualizacja powinna nastąpić automatycznie, chyba że używany jest jakiś silnik UserJS nie wspierający takiej możliwości. Wtedy pozostaje ręczna zabawa.


Przypisy

  1. Jak ktoś śledzi rozwój skryptu lub przeglądał źródła to pewnie orientuje się, że od wersji 0.1.5 wykorzystywany jest MutationObserver do wykrywania pewnym zdarzeń na części drzewa DOM, a dokładnie usunięcia elementów wstrzykniętych przez skrypt. ↩︎

  2. Wynika to z braku odpowiednich klas stylów w kontenerze i zastosowaniu CSS-a ze wstrzykiwanego kodu. ↩︎

Komentarze (0)

Dodaj komentarz

/dozwolony markdown/

/nie zostanie opublikowany/