Параллельный соскоб с Cherio - PullRequest
0 голосов
/ 11 апреля 2020

Я пытаюсь удалить сайт с несколькими разделами и разбивкой на страницы. Идея состоит в том, чтобы go через каждый раздел на странице.

Например, если имеется 6 разделов и в каждом разделе по 6 элементов на странице (общее количество страниц не более 10), я хотел бы, чтобы код выполнял как минимум 6 заданий в параллельном режиме.

Ниже приведено то, что у меня есть

const cheerio = require('cheerio');
const request = require('request-promise');
const baseUrl = 'https://www.bankmega.com/';
let category = 0;
let page = 0;

(async function () {
    try {
        const homePage = baseUrl + '/promolainnya.php';
        const html = await request(homePage);
        const $ = cheerio.load(html);
        const jobs = $('div[id="subcatpromo"]').find('img').map((i, img) => scrapePerCategory({title: $(img).attr('title'), category: i + 1}));
        await Promise.all(jobs); // error  TypeError: undefined is not a function
    } catch (e) {
        console.log('error in main ', e);
    }
})();

const scrapePerCategory = async (job) => {
    try {
        let pageNumber;
        let i = 1;
        let result = [];
        console.log('start scraping for category ' + job.title);
        do {
            page = i;
            category = job.category;

            const url = baseUrl + `/ajax.promolainnya.php?product=1&subcat=${category}&page=${page}`;
            const html = await request(url);
            const $ = cheerio.load(html);
            if (!pageNumber) {
                pageNumber = $('a.page_promo_lain[id]').length;
            }
            const temp = $('#promolain').find('a').map(async (i, promoElem) => {
                const title = cheerio(promoElem).find('img').attr('title');
                const detailLink = cheerio(promoElem).attr('href');
                const detailHTML = await request(baseUrl + detailLink);
                const $ = cheerio.load(detailHTML);
                const imageurl = baseUrl + $('.keteranganinside').find('img').attr('src');
                console.log('category : ' + job.category + ' with item  => ' + JSON.stringify({title: title, imageurl: imageurl}));
                return {title: title, imageurl: imageurl};
            }).get();
            await Promise.all(temp).then(r => result.push(r));
            i++;
        } while (i <= pageNumber) ;
        await Promise.all(result).then((r) => "done scraping for category " + job.title);
        return result;
    } catch (e) {
        console.log('error in category', e);
    }
};

Печатается, как и ожидалось, когда я запускаю

start scraping for category Travel
start scraping for category Lifestyle
start scraping for category Food & Beverages
start scraping for category Gadget & Entertainment
start scraping for category Daily Needs
start scraping for category Others
category : 6 with item  => {"title":"Perubahan Minimum Payment","imageurl":"https://www.bankmega.com//files/images/minimum payment-lp- rev.jpg"}
category : 1 with item  => {"title":"Visa Bluebird Diskon hingga 25ribu","imageurl":"https://www.bankmega.com//files/images/0-landing-page-BLUE-BIRD.jpg"}
category : 6 with item  => {"title":"Aktivasi Kartu Kredit dan PIN","imageurl":"https://www.bankmega.com//files/images/AKTIVASI-CC-lp-CS5-revrainy.jpg"}

Однако, когда вызывающая сторона (основной метод выдает ошибку), выглядит следующим образом

error in main  TypeError: undefined is not a function
    at Function.all (<anonymous>)

Это заставляет меня задуматься, действительно ли код работает так, как я ожидал.

1 Ответ

1 голос
/ 11 апреля 2020

Итеративный подход должен работать, хотя обнаружение pageNumber на первой итерации делает его несколько беспорядочным. Рекурсия должна сделать ее намного более аккуратной.

Не так много времени сейчас, как мне нужно, чтобы отправиться на юридическое занятие, так что вот версия вашей итерации, которая имеет шанс на работу. Возможно, вам придется исправить это здесь и там.

const cheerio = require('cheerio');
const request = require('request-promise');
const baseUrl = 'https://www.bankmega.com/';

(async function () {
    try {
        const $ = cheerio.load(await request(baseUrl + '/promolainnya.php'));
        // map img elements to array of promises ...
        let promises = $('div[id="subcatpromo"]').find('img').get().map((img, i) => scrapePerCategory({'title': $(img).attr('title'), 'category': i + 1}));
        // ... and await the promises.
        const jobs = await Promise.all(promises);
        console.log(jobs);
    } catch (e) {
        console.log('error in main ', e);
    }
})();

const scrapePerCategory = async (job) => {
    try {
        let pageNumber;
        let page = 1; // both `page` and `i` counters seem unnecessary - one or the other?
        const results = [];
        do {
            let url = baseUrl + `/ajax.promolainnya.php?product=1&subcat=${job.category}&page=${page}`;
            let $ = cheerio.load(await request(url));
            if (!pageNumber) {
                pageNumber = $('a.page_promo_lain[id]').length;
            }
            // here compose `innerResults` in much the same way `results` is composed ...
            let innerResults = [];
            let anchors = $('#promolain').find('a');
            for(var i=0; i<anchors.length; i++) { // for loop here allows `await` to await
                let promoElem = cheerio(anchors[i]);
                let $ = cheerio.load(await request(baseUrl + promoElem.attr('href')));
                innerResults.push({
                    'title': promoElem.find('img').attr('title'), 
                    'imageurl': baseUrl + $('.keteranganinside').find('img').attr('src')
                });
            }
            // ... and aggregate `innerResults` into `results`
            results.push(innerResults); // or results = results.concat(innerResults); ?
            page++;
        } while (page <= pageNumber);
        console.log("done scraping for category " + job.title);
        return results;
    } catch (e) {
        console.log('error in category', e);
        throw e;
    }
};
...