unhurried

コンピュータ関連ネタがほとんど、ときどき趣味も…

API for DMM Mobile

Node.jsからGoogle ChromeChromium)を操作するライブラリpuppeteerを試してみるついでに、スクレイピングを利用したアプリケーションを開発しました。DMM Mobileのマイページを操作することで以下の機能をAPI化しています。 * 高速通信容量残高の取得 * 通信モード(高速・低速)の取得 * 通信モード(高速・低速)の切り替え

ソースコードhttps://github.com/unhurried/api-for-dmm-mobile

※ あくまでpuppeteerでのスクレイピングのサンプルコードですので、実行する際にはサービスの最新の利用規約に従ってください。

Express.jsでタイムアウトレスポンスを返却する

時間がかかってしまう可能性がある処理に対して、一定時間内に結果が返却できないときにはタイムアウトをエラーレスポンスで返す方法を調べました。結論としては、connect-timeoutを利用すれば実現できますが、いくつか注意点があります。

connect-timeout

connect-timeoutExpress.jsでリクエストに対して一定時間でタイムアウトイベントを発生させるためのミドルウェアです。公式のドキュメントがあまり充実していないためテストコードを作り動作検証をしてみました。

わかったこと

  • connect-timeoutを最初のミドルウェアに設定することで、指定時間経過時に例外を発生させ、エラー処理ミドルウェアに処理を渡すことができる。
  • ルートの処理でレスポンスを送信する場合には、既にエラー処理ミドルウェアからレスポンスが送信されている可能性があるため、req.timedout(timeoutミドルウェアタイムアウト発生時に設定する)を確認して、falseの場合にのみ行う。
  • エラー処理でレスポンスを返却するときにはルートの処理でレスポンスが送信されている可能性があるため、res.headersSentfalseの場合のみ行う。
  • タイムアウトが発生しエラー処理ミドルウェアが実行されても、実行中のミドルウェアもしくはルートの処理は停止せずに続行する。
  • ただし、タイムアウト発生後に次のミドルウェアが実行されることはないので、公式ドキュメントに記載されているhaltOnTimeoutミドルウェアの設定は不要である。

動作検証コード

const co = require('co')
const express = require('express')
const sleep = require('sleep-promise')
const timeout = require('connect-timeout')

const app = express()
const timeout_ms = process.argv[2] || 1500
app.use(timeout(timeout_ms))

// ミドルウェア1
app.use(function(req, res, next) {
    co(function*(){
        console.log('middleware 1: begin')
        yield sleep(1000)
        console.log('middleware 1: end')
        next()
    })
})
app.use(haltOnTimedout)

// ミドルウェア2
app.use(function(req, res, next) {
    console.log('middleware 2: begin')
    next()
})
app.use(haltOnTimedout)

// GETルート
app.get('/', function(req, res, next) {
    co(function*(){
        console.log('app.get: begin')
        yield sleep(1000)
        if (!req.timedout) {
            console.log('app.get: send response')
            res.send('app.get')
        }
        console.log('app.get: end')
    })
})

// エラー処理ミドルウェア
app.use(function (err, req, res, next) {
    console.log('error filter: begin')
    if(req.timedout && !res.headersSent) {
        console.log('error filter: send response')
        res.status(500).send('request timeout')
    }
})

// タイムアウト発生時に以降のミドルウェアを停止するためのミドルウェア
// 公式のガイドラインで必要とされている
function haltOnTimedout (req, res, next) {
    console.log('haltOnTimeout: begin')
    if (!req.timedout) {
        console.log('haltOnTimeout: call next()')
        next()
    }
}

app.listen(3000)

実行結果

$ node app.js 500
middleware 1: begin
error filter: begin
error filter: send response
middleware 1: end
$ node app.js 1500
middleware 1: begin
middleware 1: end
haltOnTimeout: begin
haltOnTimeout: call next()
middleware 2: begin
haltOnTimeout: begin
haltOnTimeout: call next()
app.get: begin
error filter: begin
error filter: send response
app.get: end

利用したバージョン

  • Node.js 6.11.0
  • express 4.16.2
  • connect-timeout 1.9.0

Node.jsでURLからファイル名の拡張子を抽出する

ファイルシステムのパスからファイル名の拡張子を抽出するときにはpathのみで対応できますが、URLの場合にはクエリやフラグメントが付くので最初にurlでパス部分を抽出する必要があります。単純な問題ですが実装するときに少し悩んだので、備忘録としてまとめました。

app.js
const path = require('path');
const url = require('url');

const targetUrl = 'http://example.com/a.b?c=d#e'

const parsedUrl = url.parse(targetUrl)
const ext = path.extname(parsedUrl.pathname)
console.log(`ext: ${ext}`)
実行結果
$ node app.js
ext: .b

Union Bank アラート通知設定

最近届いたDMで知ったのですが、Union Bankでは口座に対するアクティビティについて、メールもしくはSMS(アメリカ・カナダの電話番号のみ)で通知を受け取れる機能があるそうです。普段あまり口座をチェックしない方は設定しておくと安心できそうです。

具体的には、以下のアクティビティに対してアラートが設定できます。

  • 口座
    • 残高が〇以下となったとき
    • 〇ドル以上の引出があったとき
    • 〇ドル以上の預金があったとき
  • その他
    • ATM(デビッド)カードに疑わしいアクティビティがあったとき
    • オンラインバンキングにログインしたとき

Herokuでpuppeteerを動かす

Herokuにpuppeteerを使ったアプリをデプロイする方法について、ブログ等で色々な方法が紹介されていますが、2018年1月時点で最も簡単な方法について整理しました。

  • (1) nodejs、(2) puppeteer-heroku-buildpack 順でBuildpackを適用する。
  • 日本語のフォントが必要な場合はjontewksではなくCoffeeAndCodeのpuppeteer-heroku-buildpackを利用する。

下記のコマンドでBuildpackを適用したアプリが作成できる。

heroku create app-name
heroku buildpacks:set heroku/nodejs -a app-name
heroku buildpacks:add https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack -a app-name
参考

つみたてNISA調査

従来のNISA制度に加えて今年から新たにつみたてNISAという制度が始まりました。NISAとの違いやNISAを利用している人の移行のポイントを自分なりに調べてみました。

つみたてNISAとNISAの比較
つみたてNISA NISA
年間投資上限 40万円 120万円
非課税期間 20年 5年
ロールオーバー できない できる
投資対象 金融庁が承認した投信・ETF 投信・ETF・株式など
投資方法 積み立て 制限なし
投資可能期間 2037年 2023年
NISAからつみたてNISAへの移行
  • 1年ごとにつみたてNISA、NISAのどちらかを選択する必要がある。
  • NISAが利用できる2023年までは自分投資タイプに合った方を利用するのが良い。(例えば、年間に40万円以下の積み立て投信しかしないのであれば、つみたてNISAの方が適する。)
  • NISAからつみたてNISAに移行する場合、NISAの資産は購入から5年間は非課税で保有できるため、その期間の評価益が出ているタイミングで売却もしくは課税口座に移管できると良い。
参考

JRの乗車区間分割

あまり知られていませんが、JRの運賃は乗車区間を分割することで安くなることがあります。例えば新宿~千葉の区間であれば、全区間でのIC運賃は799円ですが、新宿~錦糸町(216円)+錦糸町~千葉(550円)とすると766円となり、33円安くなります。

乗車区間分割で運賃が安くなる理由

JRの運賃の計算方法は複雑なので理由は単純ではないのですが、一つの要因として「JRの運賃は営業キロの数キロきざみで設定されていて、その運賃が単純にキロ数に比例していない」ということがあります。

例えば、幹線と呼ばれる主要都市間を結ぶ路線の運賃表は下記の通りです。15キロでは240円ですが、30キロでは500円と割高になっています。

営業キロ 運賃(切符) IC運賃
1~3 140円 144円
4~6 190円 185円
7~10 200円 195円
11~15 240円 237円
16~20 320円 324円
21~25 410円 410円
26~30 500円 496円

※ このような運賃表になる理由は、「営業キロ数に賃率を掛けて端数を切り上げ、その後に消費税を加える」という計算方法により、切り上げされる端数や消費税の四捨五入部分でズレが生じるためです。

参考