【问题标题】:How to iterate over different URLs when fetching data?获取数据时如何迭代不同的 URL?
【发布时间】:2019-11-29 16:14:29
【问题描述】:

我在一些评论者的帮助下更新了代码。一个简短的总结: - 我想从 800 多个页面中抓取包含产品信息的 HTML,并将该数据解析为 JSON 并将其保存在 JSON 文件中。当我一次喜欢 20 页时,该代码可以工作,但是当我尝试完成所有这些时,我收到以下错误:

Error: Max redirects exceeded.

这是完整的代码:

// Import required modules
const axios = require('axios');
const cheerio = require('cheerio');
const fs = require('fs');

const url = "http://johndevisser.marktplaza.nl/?p=";

async function getProductsHtml(data) {
    const $ = await cheerio.load(data);
    let productsHTML = [];
    $("div.item").each((i, prod) => {
        productsHTML.push(($(prod).html()));
    });
    return productsHTML;
};

async function parseProducts(html) {
  let products = [];
  for (item in html) {
    // Store the data we already have
    const $ = await cheerio.load(html[item]);
    let product = {};
    let mpUrl = $("a").attr("href");
    product["title"] = $("a").attr("title");
    product["mpUrl"] = mpUrl;
    product["imgUrl"] = $("img").attr("src");
    let priceText = $("span.subtext").text().split("\xa0")[1].replace(",", ".");
    product["price"] = parseFloat(priceText);
    products.push(product);
  }
  return products;
}

async function addDescriptionToProducts(prods) {
  for (i in prods) {
    const response = await axios.get(prods[i]["mpUrl"])
    const $ = cheerio.load(response.data);
    description = $("div.description p").text();
    prods[i]["descr"] = description;
  }
  return prods
}

async function getProductsFromPage(i) {
  try {
      const page = await axios.get(`http://johndevisser.marktplaza.nl/?p=${i}`);
      console.log("GET request succeeded!");
      // Get the Array with HTML of each product
      const productsHTML = await getProductsHtml(page.data);
      console.log("HTML array obtained!");
      // Get the Array of objects with meta info
      const productsParsed = await parseProducts(productsHTML);
      console.log("Products parsed!")
      // Add description to each product
      const productsMeta = await addDescriptionToProducts(productsParsed);
      console.log("Descriptions added!")
      // Return the Array with all product information
      return productsMeta;
    } catch(e) {
      console.log(e);
    }
};

async function saveAllProducts() {
  try {
    const allProducts = await getAllProducts();
    let jsonProducts = await JSON.stringify(allProducts);
        fs.writeFile("products.json", jsonProducts, "utf8", (e) => {
          if (e) {
            console.log(e)
          }
        });
  } catch(e) {
    console.log(e);
  }
}

async function getAllProducts() {
  try {
    let allProducts = [];
    for (let i = 1; i < 855; i++) {
      const productsFromPage = await getProductsFromPage(i);
      allProducts = [...allProducts, ...productsFromPage];
      console.log("Saved products from page " + i);
    }
    return allProducts
  } catch(e) {
    console.log(e);
  }
}

saveAllProducts();

【问题讨论】:

  • 显示你的代码,你是如何迭代的?
  • 如前所述,我目前不在,因为我尝试的两件事没有奏效。您如何建议我遍历所有不同的 URL?
  • async function fetchAndUpdateProducts(pageNumber) {...} 然后你在其他地方循环for (let i=0; i&lt;800; i++) { fetchAndUpdateProducts(i); }
  • 那行不通,因为我会在不同的 JSON 文件中获取数据,而不是全部合并为一个。
  • 好吧fs.appendFile不是邪恶的:)

标签: javascript json asynchronous web-scraping axios


【解决方案1】:

在尝试获取所有 800 种产品之前,我强烈建议您退后一步,看看您当前的代码。有几件事使这个脚本运行 800 次变得更加困难。

  1. getProducts 接收页面 html 并将产品的 html 放入全局变量中。这称为副作用,它会使代码变得比实际需要的复杂。
  2. parseProducts 接收产品 html 数组但不使用它。相反,它使用的是全局变量。
  3. parseProducts 解析每个产品 html 并将元数据放入另一个全局变量中。
  4. fetchAndUpdateProducts 正在做两件事;解析页面并写入 json

由于这些原因,fetchAndUpdateProducts 中的流程变得有点不清楚,调试变得困难。

所以我建议从一个类似这样的新方法开始

async getProductsFromPage(i) {
  try {
      const page = await axios.get(`http://johndevisser.marktplaza.nl/?p=${i}`);
      
      // Get the Array with HTML of each product
      const productsHTML = getProductsHtml(response.data);
      
      // Get the Array of objects with meta info
      const productsParsed = parseProducts(productsHTML);
      
      // Add description to each product
      const productsMeta = await addDescriptionToProducts(productsParsed);

      // Return the Array with all product information
      return productsMeta;
    } catch(e) {

    }
}

之后,您将能够执行以下操作:

const p1 = await getProductsFromPage(1);
const p2 = await getProductsFromPage(2);
const p3 = await getProductsFromPage(3);
// etc.

甚至将所有数据合并到一个数组中:

let allProducts = [];

for(let i = 0; i < 800; i++){
  const productsFromPage = await getProductsFromPage(i);
  allProducts = [...allProducts, ...productsFromPage];
}

// Write to JSON

【讨论】:

  • 我在这方面取得了巨大的进步,谢谢!除了,现在我收到一条错误消息“超出最大重定向。我将更新代码。
  • 我发现了问题,显然其中一种产品的标题中只有希腊字符,并且不知何故阻塞了整个页面。网站上的产品页面无法访问,但通过在产品标题中添加“希腊音乐”来帮助实现这一点。感谢您的帮助!
【解决方案2】:

这是获取所有数据并进行迭代的方法。

假设您有 1 - 800 个页面

const pageCount = 800;
const promises = [];
for (let page = 0; page < pageCount; page++) {
  promises.push(axios.get(`https://someurl?page=${page}`));
}

Promises.all(promises).then(responses => {
  // responses will be list of reaponse for each page in order
  // So now manipulate this data accordingly and write in your file here/
  
  const data = responses.flat();
});

【讨论】:

  • 这不会与现有的 try/catch 和 async/await 发生冲突吗?
  • 我编辑了帖子以显示整个代码,希望对您有所帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-03
  • 2018-11-30
  • 2018-07-10
相关资源
最近更新 更多