Невозможно использовать ссылки для извлечения разных заголовков

#node.js #web-scraping #promise #request #cheerio

#node.js #очистка веб-страниц #обещание #запрос #приветствую

Вопрос:

Я создал скрипт, node использующий promise в сочетании с request и cheerio для анализа столбца links under Province с этой веб-страницы, затем повторно использую эти ссылки, чтобы очистить все URL-адреса under Office column со всех таких страниц и, наконец, использовать их links для сбора title со всех таких целевых страниц, как Cairos main Post Office на этой странице.

Мой текущий скрипт в большинстве случаев застревает. Однако иногда он выдает эту ошибку UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'parent' of undefined . Я проверил каждую из функций и обнаружил, что все они работают правильно по отдельности.

Хотя скрипт выглядит немного больше, он построен на очень простой логике, которая заключается в использовании каждой links целевой страницы, пока она не достигнет title целевой страницы.

Это моя попытка до сих пор:

 const request = require('request');
const cheerio = require('cheerio');

const link = 'https://www.egyptcodebase.com/en/p/all';
const base_link = 'https://www.egyptcodebase.com/en/';

const items = [];
const nitems = [];

let getLinks = () => {
    return new Promise((resolve, reject) => {
        request(link, function(error, response, html) {
            let $ = cheerio.load(html);
            if (error) return reject(error);
            try {
                $('.table tbody tr').each(function() {
                    items.push(base_link   $(this).find("a").attr("href"));
                });
                resolve(items);
            } catch (e) {
                reject(e);
            }
        });
    });
};

let getData = (links) => {
    const promises = links
        .map(nurl => new Promise((resolve, reject) => {
            request(nurl, function(error, response, html) {
                let $ = cheerio.load(html);
                if (error) return reject(error);
                try {
                    $('.table tbody tr').each(function() {
                        nitems.push(base_link   $(this).find("a").attr("href"));
                    });
                    resolve(nitems);
                } catch (e) {
                    reject(e);
                }
            })
        }))

    return Promise.all(promises)
}

let FetchData = (links) => {
    const promises = links
        .map(nurl => new Promise((resolve, reject) => {
            request(nurl, function(error, response, html) {
                let $ = cheerio.load(html);
                if (error) return reject(error);
                try {
                    resolve($(".home-title > h2").eq(0).text());
                } catch (e) {
                    reject(e);
                }
            })
        }))

    return Promise.all(promises)
}

getLinks().then(resultList => {
    getData(resultList).then(resultSet => {
        FetchData(resultSet).then(title =>{
            console.log(title);
        })
    })
})
 

Как я могу удалить заголовки с целевых страниц, используя все ссылки с целевых страниц?

Комментарии:

1. Пожалуйста, будьте осторожны с сервером, с которого вы удаляете, он не может обрабатывать сотни запросов одновременно, вот почему ваш скрипт застрял. Сначала Cannot read property 'parent' of undefined вы должны проверить if(error) , а затем выполнить cheerio.load .

2. Брохер, я понял, в чем проблема

Ответ №1:

Было бы намного проще спросить владельца веб-сайта о данных, которые вам нужны.
Он может понять ваш запрос и предоставить его вам бесплатно, вместо того, чтобы очищать свой сайт.

P.S: Я был удивлен, обнаружив вопрос о том, как очистить мой собственный веб-сайт.
P.S2: Если вам просто нужны все заголовки почтовых отделений, я мог бы предоставить их вам бесплатно: D
P.S3: возможно, ваша ошибка возникает из-за того, что в течение некоторого времени на странице нет элемента, которыйвы пытаетесь выполнить синтаксический анализ с помощью cheerio.

Комментарии:

1. Мне вообще не нужны данные. Я использовал ссылку в качестве примера сайта. Все, что я хотел бы знать, это способ добиться того, что я пытался @Samy Massoud.

2. @robots.txt Затем проверьте P.S3 и распечатайте содержимое, которое у вас есть, чтобы убедиться, что элемент, который вы ищете, действительно существует в html

3. Мир тесен! Ха-ха: D

Ответ №2:

Итак, проблема в 2D массиве. Если вы внимательно изучите свою getData функцию, вы вернете 2D-массив.

map верните массив, и внутри этой карты вы разрешаете другой массив nitems .

Вот рабочий код:

 const base_link = 'https://www.egyptcodebase.com/en/';

// helper wrapper DRY
const getHtmls = (url) => {
  return new Promise((resolve, reject) => {
    request({ uri: url, method: 'GET', followAllRedirects: true } , function(error, response, html) {
      if (error) reject(error);
      else resolve(html);
    });
  })
}

let getLinks = async () => {
  const link = 'https://www.egyptcodebase.com/en/p/all';
  const items = [];
  try {
    const html = await getHtmls(link);
    let $ = cheerio.load(html);
    $('.table tbody tr').each(function() {
      items.push(base_link   $(this).find("a").attr("href"));
    });
  } catch (e) {
    // handling error here so execution can continue for good eggs
    console.error(e.message)
  }
  return items;
};

let getData = async (links) => {
  const out = [];
  try {
    const promises = links.map(nurl => getHtmls(nurl));

    const htmls = await Promise.all(promises);
    htmls.forEach(html => {
      let $ = cheerio.load(html);
      $('.table tbody tr').each(function() {
        out.push(base_link   $(this).find("a").attr("href"));
      });
    })
  } catch (e) {
    // handling error here so execution can continue for good eggs
    console.error(e.message)
  }
  return out;
}

let FetchData = async (links) => {
  const out = [];
  try {
    const promises = links.map(nurl => getHtmls(nurl));
    const htmls = await Promise.all(promises)
    htmls.forEach(html => {
      try {
        let $ = cheerio.load(html);
        out.push($(".home-title > h2").eq(0).text());
      } catch (e){
        // handling error here so execution can continue for good eggs
        console.error(e.message)
      }
    })
  } catch (e) {
    // handling error here so execution can continue for good eggs
    console.error(e.message)
  }
  return out;
}

getLinks().then(resultList => {
  getData(resultList).then(resultSet => {
    FetchData(resultSet).then(title =>{
      console.log(title);
    })
  })
})
 

Примечание: вместо написания собственной Promise оболочки вы можете использовать request-promise package

Комментарии:

1. Я исправил свой скрипт так, как вы предложили, и запустил его. Тем не менее, он все равно застревает. После выполнения я часами ждал какого-либо результата, но он ничего не напечатал @1556089774. Можете ли вы подсказать, где я ошибаюсь? Это исправленный скрипт . Спасибо.

2. @robots.txt обновленный ответ, проверьте (я не смог протестировать все ~ 540 ссылок, проблема с сетью), дайте мне знать, работает ли это

3. Я ждал полчаса после выполнения предложенного вами скрипта, но он все еще зависает. Должен ли я ждать больше? Я собираюсь наградить эту награду за ваш ответ, и я надеюсь, что вы посмотрите, что происходит. Спасибо.

4.@robots.txt некоторый рефакторинг и некоторые изменения, протестированные и работающие (похоже, что некоторые htmlsне ".home-title > h2" ' Exhibits Island Post Office', ' Kafr Moyes Post Office', '', замечают, что третья запись является пустой строкой в одинарных кавычках). Проверено на всех ~ 540 URL-адресах. Также похоже, что сервер не справляется с нагрузкой. (частый сброс соединения) @SamyMassoud извините за бомбардировку вашего сайта, пришлось сделать это для тестирования 🙂

Ответ №3:

Проблема с вашим кодом заключается в функции fetchData, так как в этой функции вы передаете ссылки, а затем используете map поверх нее. Но если вы заглянете внутрь этой функции map и проверите значение переменной ‘nurl’, это будет массив ссылок, а его тип данных будет object. Согласно семантике функции запроса, ее первым параметром должна быть строка, поэтому, если вы выполните итерацию по переменной ‘nurl’, чтобы получить значения, тогда это сработает.

Мой фрагмент кода для одного URL-адреса из массива