Skip to content

Instantly share code, notes, and snippets.

@cocoabox
Created March 6, 2025 12:58
Show Gist options
  • Save cocoabox/f52e5ca9d5d6f416a87228447c7975f9 to your computer and use it in GitHub Desktop.
Save cocoabox/f52e5ca9d5d6f416a87228447c7975f9 to your computer and use it in GitHub Desktop.
クロネコヤマトの配達状況をJSONで出力する
//
// 使い方: node kuroneko-yamato-tracking.js [伝票番号] | jq
// OR : const kuroneko_yamato_tracking = reuqire('./kuroneko_yamato_tracking');
// const tracking_info_dict = await kuroneko_yamato_tracking('0000-0000-0000');
//
const cheerio = require('cheerio');
async function kuroneko_yamato_tracking(tracking_number) {
tracking_number = tracking_number.trim().replaceAll(/[\-]/g, '');
try {
const body = (new URLSearchParams({
number00: '1',
number01: tracking_number
})).toString();
const response = await fetch('https://toi.kuronekoyamato.co.jp/cgi-bin/tneko', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body,
});
const html = await response.text();
const $ = cheerio.load(html);
let out = {};
$('div.parts-tracking-invoice-block').each((_, block) => {
const report_title = $(block).find('.tracking-invoice-block-title')?.first()?.text().trim();
const tracking_num = report_title?.match(/(.*)$/)?.[1];
const state_title = $(block).find('.tracking-invoice-block-state-title')?.first()?.text().trim();
if (state_title === '伝票番号誤り') {
throw {error:'not-found'};
}
const state_summary = $(block).find('.tracking-invoice-block-state-summary')?.first()?.text().trim();
const state_note = $(block).find('.tracking-invoice-block-state-note')?.first()?.text().trim();
out = {...out, report_title, tracking_num, state_title, state_summary, state_note};
const extra_info = [];
$(block).find('.tracking-invoice-block-summary > ul > li').each( (_, li) => {
const key = $(li).find('.item').first().text();
const key_str = key.match(/^(.*?)$/)?.[1] ?? key;
const val = $(li).find('.data').first().text();
extra_info.push({key: key_str, val});
if (key_str === 'お届け予定日時') {
date_mat = val.match(/^([0-9]+)\/([0-9]+)$/);
if (date_mat) {
const eta_date_elements = {
month : parseInt(date_mat[1]),
date : parseInt(date_mat[2]),
};
out = {...out, eta_date_elements};
}
}
} );
out = {...out, extra_info};
const events = [];
$(block).find('.tracking-invoice-block-detail > ol > li').each((_, li) => {
const event = $(li).find('.item')?.first()?.text();
const date = $(li).find('.date')?.first()?.text();
const date_mat = date.match(/^(([0-9]+))?([0-9]+)([0-9]+) ([0-9]+):([0-9]+)/);
const date_elements = {
year: date_mat[2] ? parseInt(date_mat[2]) : null,
month: date_mat[3] ? parseInt(date_mat[3]) : null,
date: date_mat[4] ? parseInt(date_mat[4]) : null,
hour: date_mat[5] ? parseInt(date_mat[5]) : null,
minute: date_mat[6] ? parseInt(date_mat[6]) : null,
};
const where = $(li).find('.name')?.first()?.text();
if (event && date && where)
events.push({event, date, date_elements, where});
});
out = {...out, events: events.reverse()};
});
return out;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
module.exports = kuroneko_yamato_tracking;
if (require.main === module) {
const args = process.argv.slice(2);
if (args.length !== 1) {
console.warn('Usage: node . [TRACKING_NUMBER]');
return process.exit(1);
}
const tracking_num = args[0];
kuroneko_yamato_tracking(tracking_num).then(out => {
console.log(JSON.stringify(out));
}).catch(error => { console.log(JSON.stringify(error)); process.exit(1); });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment