2

我一直在使用 Cheerio、node-fetch 和 fs-extra 编写基于 NodeJS 的 Web 抓取 API。在下面的代码中,我为 config.supportedMountains 数组中的每个字符串调用 getReport 方法。对于每个项目,我想通过 fetchAndStore 函数运行它们,该函数发出 html 请求,通过特定的解析器运行它,然后存储 json 结果。

// const fs = require('fs-extra');
const _ = require('lodash');
// const Promise = require('promise');
const schedule = require('node-schedule');

const fetchAndStore = require('./fetchAndStore.js');

const config = require('../config.js');

exports.run = function() {
  schedule.scheduleJob('*/20 * * * * *', function() {
    // Get the most recent reports
    // And write them to storage
    _.forEach(config.supportedMountains, function(fName) {
      getReport(fName);
    });
  });
};

/**
 * Gets the lift statuses for every mountain
 * @param {string} fName the file name of the mountain
 * @return {promise} the promise resolved when the file is written
 */
function getReport(fName) {
  return fetchAndStore.run(fName);
}

在这里您可以看到获取和存储文件。此文件采用 fName,并需要相应的 staticData 文件。此文件包含用于获取页面的 url。现在,发出了 html 请求,并通过解析器运行。然后,使用生成的解析 json,这将通过几个步骤来存储它。最终输出应该是两个文件,一个存储报告,另一个存储historySnowfall,fs.outputJson函数中的大部分逻辑是处理丢失的文件。

const fs = require('fs-extra');
const fetch = require('node-fetch');

exports.run = (function(fName) {
  // Get the staticJson
  let staticJson = require(`../staticData/mountains/${fName}.json`);
  // console.log(staticJson.id)

  // Output the report
  return fetch(staticJson.urls.reportFetchUrl).then(function(res) {
    return res.text();
  }).then(function(html) {
    // Run the html through the parser
    let parser = require(`../scrapers/${staticJson.sName}.js`);
    parsed = parser.run(html);
    // Output the report
    return fs.outputJson(
      `data/reports/${staticJson.id}.json`,
      parsed.report
    ).then(function() {
      // console.log(parsed.report.lifts[0].name);
      // Once output is completed
      if (parsed.snowHistory) {
        // If snow history is defined
        // Read the old file
        return fs.readJson(
          `data/snowHistory/${staticJson.id}.json`
        ).then(function(oldJson) {
          // If the date of the old json is todays date
          if (oldJson[0].date === parsed.snowHistory.date) {
            // Replace the first element in array
            oldJson[0] = parsed.snowHistory;
            return fs.outputJson(
              `data/snowHistory/${staticJson.id}.json`,
              oldJson
            );
          } else {
            // If this is a fresh entry
            oldJson.unshift(parsed.snowHistory);
            // If the record does not exist
            return fs.outputJson(
              `data/snowHistory/${staticJson.id}.json`,
              oldJson
            );
          }
        }).catch(function(e) {
          // If the old file cannot be read
          if (e.code === 'ENOENT') {
            // If the file does not exist
            // Write brand new file
            return fs.outputJson(
              `data/snowHistory/${staticJson.id}.json`,
              [parsed.snowHistory]
            );
          }
        });
      }
    });
  });
});

由于某种原因,当爬虫运行时,大约有 1/4 的时间,一次 fetchAndStore 执行的数据会与另一次 fetchAndStore 执行的数据混在一起,这意味着错误的数据将被写入文件系统。这怎么可能?我想,由于我单独调用 fetchAndStore.run(),数据将无法混淆。知道为什么会这样吗?

4

1 回答 1

0

我看到的第一件事是它parsed是全球范围的。这可能是问题吗?顺便说一句,如果你嵌套得那么深,你真的应该考虑把它分解成几个函数。

于 2017-08-31T02:05:21.513 回答