LoginSignup
2
4

More than 5 years have passed since last update.

Puppeteerを使ってページごとにスクレイピングした結果をjson2csvでcsvに出力しようとしたら躓いた

Last updated at Posted at 2018-09-03

やりたかったこと

Puppeteerを使用し、ページごとにスクレイピングを行いその結果を配列に格納します。
その後csv2jsonを使用し全てのページのスクレイピング結果をcsvで出力したかったんです。
しかし、できはしたのですがページごとに必要ないヘッダーが出力されてしまいました。

原因は簡単で、ページごとに配列を切っていたせいでした。

こんなJSONを

{
  0:[
    { 
      title: 'タイトル',
      releaseDate: '2018年2月1日',
    },
    { 
      title: 'タイトル',
      releaseDate: '2018年2月3日',
    },
  ],  
  1:[
    { 
      title: 'タイトル',
      releaseDate: '2018年3月1日',
    },
    { 
      title: 'タイトル',
      releaseDate: '2018年3月3日',
    },
  ]
}

CSV出力すると

const fs = require('fs');
const Json2csvParser = require('json2csv').Parser;

/**
 * スクレイピング結果のCSV作成
 */
async function createCsv(result) {
  const fields = ['title', 'releaseDate'];
  const parser = new Json2csvParser({ fields, header: true });
  var csv = [];

  // ページごとにパースしていきCSV作成
  for (var i = 0; i < result.length; i++) {
    csv[i] = parser.parse(result[i]);
  }

  fs.writeFile(CSV_FILE_NAME + '.csv', csv, function(err) {
    if (err) throw err;
    console.log('file saved');
  });
}

こんな感じになっちゃう

"title","releaseDate"
"タイトル","2018年3月1日"
"タイトル","2018年3月3日","title","releaseDate"
"タイトル","2018年2月1日"
"タイトル","2018年2月3日"

二次元配列的な感じでスクレイピング結果を格納してjson2csvを使ってパースするときにループすればいい感じに出ると思っていたんですが甘かったようです。

だめだったケース

だめコード

/**
 * 全記事ページを対象にスクレイピングを行う
 *
 * @param {object} browser 開いているブラウザ
 * @returns スクレイピングページ全ての結果
 */
async function scraping(browser) {
  var result = [];
  var page = await browser.newPage();
  const lastPageIndex = await fetchPageListLastIndex(browser);

  // ページごとのループ
  for (var i = 0; i < lastPageIndex; i++) {
    await page.goto(URL + (i + 1), { waitUntil: "domcontentloaded" });
    // ページごとの記事全体スクレイピング結果を入れる
    result.push(await scrapingPageAllLink(browser, await fetchPageAllLink(page)));
  }
  return result;
}

/**
 * ページごとの記事全体にスクレイピングを行う
 *
 * @param {*} browser 使用ブラウザ
 * @param {*} linkList ページごとの記事全てのリンク
 * @returns ページごとの記事全体スクレイピング結果
 */
async function scrapingPageAllLink(browser, linkList) {
  var resultPageAll = [];

  for (var j = 0; j < linkList.length; j++) {
    // titleとreleaseDateの配列を返す
    resultPageAll.push(await scrapingPageLinkEach(browser, linkList[linkIndex]));
  }

  return resultPageAll;
}

うまくいったケース

うまくいったコード

/**
 * 全記事ページを対象にスクレイピングを行う
 *
 * @param {object} browser 開いているブラウザ
 * @returns スクレイピングページ全ての結果
 */
async function scraping(browser) {
  var result = [];
  var page = await browser.newPage();
  const lastPageIndex = await fetchPageListLastIndex(browser);

  // ページごとのループ
  for (var i = 0; i < lastPageIndex; i++) {
    await page.goto(URL + (i + 1), { waitUntil: "domcontentloaded"} );
    const linkList = await fetchPageAllLink(page);

    // ページの記事ごとのループ
    for (var j = 0; j < linkList.length; j++) {
      // titleとreleaseDateを返す
      result.push(await scrapingPageLinkEach(browser, linkList[j]));
    }
  }
  return result;
}

改善方法

こんな感じのJSONになっていればOK

[ 
  { 
    title: 'タイトル',
    releaseDate: '2018年月1日'
  },
  { 
    title: 'タイトル',
    releaseDate: '2018年1月3日'
  }
]

スクレイピング結果がページごとに配列になって分けられていると上手くいかないので気をつけましょう。

うまくいったCSV出力コード

const fs = require('fs');
const Json2csvParser = require('json2csv').Parser;

/**
 * スクレイピング結果のCSV作成
 */
async function createCsv(result) {
  const fields = ['title', 'releaseDate'];
  const parser = new Json2csvParser({ fields, header: true });
  var csv = parser.parse(result);

  fs.writeFile(CSV_FILE_NAME + '.csv', csv, function(err) {
    if (err) throw err;
    console.log('file saved');
  });
}

ループする必要なく一発でいい感じのcsvが出力されます。

CSV出力例

"title","releaseDate"
"タイトル","2018年3月1日"
"タイトル","2018年3月3日"
"タイトル","2018年2月1日"
"タイトル","2018年2月3日"
2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4