【问题标题】:Unable to make use of links to fetch different titles无法使用链接来获取不同的标题
【发布时间】:2021-12-03 05:26:58
【问题描述】:

我在node 中创建了一个脚本,使用promise 结合requestcheerio 来解析Province 列下的links webpage 然后重用这些链接来抓取所有所有这些pages 中的Office 列下的url,最后使用这些links 从所有这些target pages 中收集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);
        })
    })
})

如何利用登陆页面的所有链接从目标页面中抓取标题?

【问题讨论】:

  • 请对您要从中报废的服务器保持温柔,它无法同时处理数百个请求,这就是您的脚本卡住的原因。对于Cannot read property 'parent' of undefined,您应该先检查if(error),然后再检查cheerio.load
  • 兄弟,我知道是什么问题

标签: node.js web-scraping promise request cheerio


【解决方案1】:

向网站所有者询问您需要的数据会容易得多。
他可能会理解您的请求并将其免费提供给您,而不是抓取他的网站。

P.S:我很惊讶地发现一个关于如何抓取我自己的网站的问题。
P.S2:如果您只需要所有邮局标题,我可以免费为您提供:D
P.S3:您的错误可能是由于页面没有您尝试使用 Cheerio 解析的元素而发生的。

【讨论】:

  • 我根本不关心数据。我使用该链接作为示例站点。我只想知道如何实现我一直在尝试的@Samy Massoud。
  • @robots.txt 然后检查 P.S3 并打印您必须确保您要查找的项目确实存在于 html 中的内容
  • 世界很小!哈哈:D
【解决方案2】:

所以问题在于2D 数组。如果您仔细检查 getData 函数,您将返回二维数组。

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);
    })
  })
})

注意:您可以使用 request-promise 包,而不是编写自己的 Promise 包装器

【讨论】:

  • 我按照您建议的方式修正了我的脚本并运行它。但是,它仍然卡住了。执行后,我等待了几个小时的任何结果,但它没有打印任何内容@1556089774。你能建议我哪里出错了吗?这是the rectified script。谢谢。
  • @robots.txt 更新答案,检查(我无法测试所有 ~540 个链接,网络问题),让我知道它是否有效
  • 执行您建议的脚本后,我等了半个小时,但它仍然挂起。我应该再等一下吗?我会根据你的回答奖励这个赏金,我希望你能看看发生了什么。谢谢。
  • @robots.txt 一些重构和一些更改,经过测试和工作(看起来有些 htmls 没有 ".home-title > h2" ' Exhibits Island Post Office', ' Kafr Moyes Post Office', '', 注意第三个条目是单引号中的空字符串)。在所有 ~540 个网址上进行了测试。看起来服务器也无法处理负载。 (频繁重置连接)@SamyMassoud 抱歉轰炸了您的网站,必须这样做才能测试 :)
【解决方案3】:

您的代码问题出在 FetchData 函数中,因为在该函数中您正在传递链接,然后在其上使用 map。 但是,如果您查看该映射函数并检查“nurl”变量的值,它将是一个链接数组,其数据类型将是对象。 根据请求函数的语义,它的第一个参数应该是字符串,所以如果你遍历'nurl'变量来获取值,那么它就可以工作了。

My code snippet for one url from array

【讨论】:

    猜你喜欢
    • 2017-08-04
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    相关资源
    最近更新 更多