Jak programista szuka mieszkania?

Page Title Shape 1
Page Title Shape 2
Page Title Shape 3

Gdy nadchodzi ten czas, gdy potrzebujesz zmienić mieszkanie, zaczyna się polowanie. Tym pięknym, ale prymitywnym rymem zapraszam Cię do dzisiejszego wpisu. Dziś chciałbym pokazać Ci jak wykorzystać web scraping, na praktycznym przykładzie. Zatem jak programista szuka mieszkania?

Musimy umówić się, że od teraz Twoim celem jest poszukać sobie idealne m4. Nie chcesz codziennie po parę godzin przeglądać ofert na portalach ogłoszeniowych. Chcesz zminimalizować czas potrzebny na tę operację. Ofert szukasz jedynie na olx.pl. To uproszczenie znacznie usprawni pisanie kodu w tym artykule 🙂

Web scraping

Dla tych, którzy nie wiedzą, czym jest omawiane dzisiaj zagadnienie, tłumaczę. Jest to nic innego jak automatyczne pozyskiwanie danych z zewnętrznych stron www. Innymi słowy, automatyzujemy proces wchodzenia na strony www i wyciągania z nich interesujących nas informacji. W dzisiejszym przykładzie proces, jaki będziemy automatyzować, wygląda następująco:

  • wejdź na stronę olx.pl
  • ustaw odpowiednie filtry (szukam tylko mieszkania z Krakowa bez konkretnej ceny, metrażu etc.)
  • pobierz interesującą Cię listę ofert
  • sprawdź, czy mamy jakieś nowe oferty
  • wyślij do mnie email z listą ofert, jeśli coś się zmieniło.

Technologia

Skoro mamy już ustalony proces, wiemy mniej więcej, co chcemy osiągnąć. Teraz należy podjąć decyzję, jaką technologię możemy wykorzystać, w celu realizacji zadania. W moim przypadku dostępne są dwie opcje: php lub javascript. Oczywiście, jeśli znasz np. Javę czy .NET to zapewne również są dostępne gotowe biblioteki do tego typu operacji.

Szybki rzut monetą i dziś padło na node.js. Zaskoczony? JavaScript staje się równie wszechstronny, jak i uniwersalny. Nie obędzie się oczywiście bez dodatkowej biblioteki. Dziś z pomocą przybywają puppeteer, lodash oraz nodemailer.

Puppeteer to biblioteka, która dostarcza nam high-level API do kontroli nad Chromium oraz Chrome. Dzięki niej możemy kontrolować te dwie przeglądarki z poziomu kodu JavaScript.

Loadash daje nam dostęp do funkcji, które ułatwią nam życie. W tym wypadku skorzystamy z niej, aby ustalić unikalne wartości w tablicy obiektów.

Nodemailer pozwala nam wysyłać maile bezpośrednio za pomocą JavaScript. Polecam też wykorzystać aplikacje mailtrap.io, która pozwoli Ci przetestować wysyłkę maili.

Let’s code it

/*
* Cały kod źródłowy udostępniłem na moim githubie.
* Link do repozytorium znajdziesz na końcu tego wpisu.
*/

W pierwszej kolejności instalujemy potrzebne zależności.

npm i puppeteer lodash nodemailer --save

Zaczynamy od utworzenia pliku /src/modules/Scraper.js, który będzie odpowiedzialny za pobranie danych ze strony i zwrócenie nam odpowiednich informacji do dalszego przetwarzania.

Uwaga! Nasz kod musi działać z zachowaniem odpowiedniej kolejności procesów. Nie możemy zacząć przeszukiwać strony internetowej bez wcześniejszego pobrania jej zawartości. Do tego celu wykorzystamy async/await, czyli uproszczony zapis promise’ów.

const puppeteer = require('puppeteer');
const Config = require('./Config');

module.exports = async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto(Config.pageURL);
    const fetchedOffers = await page.evaluate(() => {
        const offers = Array.from(document.querySelectorAll(".offer-wrapper"));
        const offersTitles = offers.map( offer => {            
            return {
                title: offer.querySelector('strong').innerText,
                url: offer.querySelector('.link').getAttribute('href'),
                price: offer.querySelector('.price strong').innerText,
                location: offer.querySelector('.breadcrumb').innerText
            };
        });
        return offersTitles;
    });        
    await browser.close();
    return fetchedOffers;
};

Definiujemy pupeteer, a następnie tworzymy instancję odpowiedzialną za całą przeglądarkę oraz instancje otwartej strony www. Jak widzisz w poniższym kodzie dopiero na instancji strony uruchamiamy metodę .goto(), która służy do uruchomienia konkretnego adresu w przeglądarce.

Wywołanie metody .goto() powoduje, że instancja strony – zmienna page – będzie miała dostęp do zawartości HTML adresu, który podaliśmy w argumencie tej funkcji. Natomiast widoczna metoda .evaluate() pozwala nam uruchomić naszego callback’a, który będzie zawierał context pobranej strony www.

Oznacza to, że piszemy kod callback’a tak, jakby był on uruchomiony na konkretnej stronie www. Jeśli nadal jest to dla Ciebie mgliste, to wyobraź sobie, że kod z poniższej funkcji strzałkowej (eng. arrow function) wklejasz w konsolę na otwartej stronie https://www.olx.pl/nieruchomosci/mieszkania/krakow/

Co zrobiliśmy powyżej? Wyszukaliśmy wszystkie oferty dostępne na stronie olx.pl za pomocą selektora i metody .querySelectorAll(). Następnie za pomocą funkcji .map() z każdej oferty wyciągnęliśmy jej nazwę, cenę, lokalizację oraz adres url do oferty.

Przygotowane dane zwracamy, aby móc je porównać z globalną tablicą. Robimy to już w pliku /src/App.js. Tutaj ustalamy funkcję, która będzie uruchamiana raz na 10 minut. Będzie ona uruchamiała Scraper’a i decydowała, czy wysłać do nas maila. Oto jak wygląda kod:

const Scraper = require('./modules/Scraper');
const Mail = require('./modules/Mail');
const Config = require('./modules/Config');
const _ = require('lodash');
let offers = [];

function getOffers(){
    Scraper().then(newOffers => {                
        const beforeScraperOffersLength = offers.length;        
        //Filtrujemy unikalne obiekty na podstawie adresu url z olx.pl
		offers = _.uniqBy([...offers, ...newOffers], 'url');
		//Jeśli pojawiły się nowe oferty
        if(offers.length > beforeScraperOffersLength){
            //Wysyłamy maila        
            Mail(offers);
        }
    });
}

setInterval(getOffers, Config.refreshTime);

Jak widzisz wykorzystuję funkcję Mail(), którą przygotowałem wcześniej. Służy nam ona do wysyłki maila w odpowiednim formacie. Jako argument przyjmuje jedynie tablicę obiektów z ofertami. Więcej informacji znajdziesz w kodzie:

const Config = require('./Config');

module.exports = newOffers => {
    const nodemailer = require('nodemailer');
    const transporter = nodemailer.createTransport(Config.mailConnection);
    //Generuj treść HTML
    const html = generateMailHtml(newOffers);
    //Wysyłamy maila
    transporter.sendMail({...Config.mailMessage, html});
}

//Funkcja generuje listę HTML
function generateMailHtml(newOffers){
    return '<ul>' + newOffers.map(renderListElement) + '</ul>';
}

//Funkcja generuje element listy
function renderListElement(offer) {
    return '<li><a href="' + offer.url + '">' + offer.title + '</a> - ' + offer.location + ' za ' + offer.price + '</li>';
}

UWAGA! Zauważ, że wszystkie zmienne konfiguracyjne znajdują się w /src/modules/Config.js

module.exports = {
    refreshTime: 600000,
    pageURL: 'https://www.olx.pl/nieruchomosci/mieszkania/krakow/',
    mailConnection: {
        host: "smtp.mailtrap.io",
        port: 2525,
        auth: {
            user: "XX",
            pass: "XX"
        }
    },
    mailMessage: {
        from: 'wojtek@cybercode.pl',
        to: 'wojtek@cybercode.pl',
        subject: 'Nowe oferty z olx'
    }
};

Podsumowanie

Tak w dużym uproszczeniu wygląda przeszukiwanie i gromadzenie danych ze stron zewnętrznych. Można oczywiście rozszerzyć go o przeglądanie stron, do których linki znajdziemy na aktualnie przeglądanej stronie itd.

Daj znać w komentarzu, czy ten artykuł był dla Ciebie wartościowy. Możesz go również zalajkować na Facebooku. Twój feedback jest dla mnie bardzo ważny.

Link do repozytorium: https://github.com/octocodeio/olx-scraper

Previous Post
Newer Post

Brak produktów w koszyku.

X