1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
// LoGoフォーム(小平市)の手続き一覧を取得する。
//
// LoGoフォームのページ自体は自動アクセス防止(TRUSTBANKの確認画面)で
// ブラウザから開けないことがある。一方、一覧のAPIは直接呼べる。
// ただし Node の fetch では 403 になるため、curl を使って取得している。
//
// 使い方:
// node scripts/fetch-logoform-events.mjs # イベント情報(procedure id 49006)
// node scripts/fetch-logoform-events.mjs 48668 # 電子申請(手続き一覧)
// node scripts/fetch-logoform-events.mjs 49006 --json
//
// procedure id は、手続きページの設定に "id" として入っている。
// 2517(イベント情報)は 49006、666(電子申請)は 48668。
import { execFileSync } from 'node:child_process';
const DEFAULT_ID = '49006';
const args = process.argv.slice(2);
const asJson = args.includes('--json');
const procedureId = args.find((a) => /^\d+$/.test(a)) ?? DEFAULT_ID;
const PAGE = procedureId === '48668'
? 'https://logoform.jp/procedure/exdH/666'
: 'https://logoform.jp/procedure/exdH/2517';
const UA =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
const curlArgs = [
'-s',
'--max-time',
'30',
'-X',
'POST',
'-H',
'Content-Type: application/json',
'-H',
'Accept: application/json',
'-H',
`Referer: ${PAGE}`,
'-H',
`User-Agent: ${UA}`,
// 空のオブジェクトで全件返る。page などの項目を足すと「不明な項目」で弾かれる。
'-d',
'{}',
`https://logoform.jp/api/procedure/branch/detail/search/${procedureId}`,
];
let raw;
try {
raw = execFileSync('curl', curlArgs, { encoding: 'utf8', maxBuffer: 20 * 1024 * 1024 });
} catch (error) {
console.error('curl の実行に失敗しました。curl が使えるか確認してください。');
console.error(String(error).slice(0, 300));
process.exit(1);
}
let data;
try {
data = JSON.parse(raw);
} catch {
console.error('JSONとして読めませんでした(自動アクセス防止の画面が返っている可能性があります)。');
console.error(raw.slice(0, 300));
process.exit(1);
}
const items = Array.isArray(data.items) ? data.items : [];
if (asJson) {
console.log(JSON.stringify(items, null, 2));
process.exit(0);
}
console.log(`procedure_id=${procedureId} 件数=${items.length}`);
for (const item of items) {
const description = (item.description ?? '').replace(/\s+/g, ' ').trim();
console.log(
`- [${item.category_name}] ${item.procedure_name}` +
(description ? `\n ${description.slice(0, 120)}` : '') +
`\n https://logoform.jp${item.form_url ?? ''}`,
);
}
|