Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Organization

【2019年6月版】fs.writeFile したら ERR_INVALID_CALLBACK ですって

Qiitaの記事、10000件?!

こちらの記事「Qiitaの記事をランダムに読める API / サービス を4時間ぐらいで作った」を見て私も「Qiitaの最新記事10000件、取ってみよ!」と思いついたのでございます。

さっそくコーディング

上記記事のGithubリポジトリ https://github.com/redshoga/random-qiita-api を参考にしつつ、軽量な Http Client らしい 「phin」を使って意気揚々とプログラムを作成いたしました。

まずはプロジェクトの準備から。

$ npm init
...
$ npm i phin

で〜package.jsonscripts"start":"node indes.js",を追記してnpm startコマンドでindex.jsが実行されるように仕掛け、以下のような感じでindex.jsを作成いたします。

index.js
const p = require('phin');
const fs = require('fs');

(async ()=>{
    const TOKEN = '...';

    // 1ページあたり100件は固定
    const q = ((n) => (p) => `?page=${p}&per_page=${n}`)(100);

    for(let i = 1 ;i <= 100; i++) {
        const res = await p({
            'url' : 'https://qiita.com/api/v2/items' + q(i),
            'headers' :{
                'Authorization' : `Bearer ${TOKEN}`
            },
            'parse': 'json'});
        fs.writeFile(`./items/qiita_item_${i}.json`, JSON.stringify(res.body, '', ' '));
    }
})();

Qiita の APIは認証キーがないと1時間に60回までのリクエストしか送れないため、短時間に100ページを取得して「最新の10000万件」を取ってくることができないのです。
というわけで、TOKEN で定義している認証キーをこちらの「Qitta APIのヘルプ#認証認可」のページを参照して取得します。今回は手っ取り早く、"ユーザーの管理画面"からサクッと取得してきました。

ついでにクエリパラメータの作成で、ちょっぴりカリー化(「3歳娘「パパ、関数をカリー化して?」」をかじった程度)を試してみたり、jsonのフォーマットでインデント指定してみたり。

そして実行、ポチッとな!

いよいよ npm start で実行してみます!

$ npm start
...
(node:13149) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
    at maybeCallback (fs.js:128:9)
    at Object.writeFile (fs.js:1158:14)
...

ERR_INVALID_CALLBACKッ! ERR_INVALID_CALLBACKッッ!!! ガーン!!!( ̄◇ ̄;)!!!

writeFileCallback must be a function のエラーってなんだそりゃ?!

node.jsの公式ドキュメントをちゃんと確認

はい、node.jsの公式ヘルプを覗くと・・・
https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_fs_writefile_file_data_options_callback

fs.writeFile(file, data[, options], callback)

・・・いつの間にか必須になっていたんですね、第3引数。ていうか、非同期になっていたのですね・・・
ってことで、callback は必須です。

ちなみに、更新履歴は・・・

Version Changes
v10.10.0 The data parameter can now be any TypedArray or a DataView.
v10.0.0 The callback parameter is no longer optional. Not passing it will throw a TypeError at runtime.
v7.4.0 The data parameter can now be a Uint8Array.
v7.0.0 The callback parameter is no longer optional. Not passing it will emit a deprecation warning with id DEP0013.
v5.0.0 The file parameter can be a file descriptor now.
v0.1.29 Added in: v0.1.29

Oh... The callback parameter is no longer optional. の文字が踊る。。。
v.7 の時点で必須だったかぁ・・・

というわけで、以下のようにコールバックを追加して再チャレンジいたしました。

index.js
const p = require('phin');
const fs = require('fs');

(async ()=>{
    const TOKEN = '....';

    // 1ページあたり100件は固定
    const q = ((n) => (p) => `?page=${p}&per_page=${n}`)(100);

    for(let i = 1 ;i <= 100; i++) {
        const res = await p({
            'url' : 'https://qiita.com/api/v2/items' + q(i),
            'headers' :{
                'Authorization' : `Bearer ${TOKEN}`
            },
            'parse': 'json'});
        fs.writeFile(`./items/qiita_item_${i}.json`, JSON.stringify(res.body, '', ' '), (err)=>{
            if(err) console.log(`error!::${err}`);
        });
    }
})();

はい、こちらのコードで無事にjsonファイル100個、10000件の記事データが取得できました。

このjsonでこれから〇〇なことや、××をことを試してみたいと思います♪ :sunglasses:

参考ページ

宣伝

JSネタ、こちらもどうぞ〜!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?