最近、K-業務ポータルが公文書のタイトルを3か月単位で区切って表示しているのを見て、こんな考えをしてみた。
もしクロールで公文書のタイトルをすべて集めて3年間分をエクセルで作っておいたら?
filterを利用して簡単に公文書番号を検索できると思った。
結論から言うと不可能だった。

1. 誰でも計画はある。
私の壮大な計画はこうだった。
1. pythonのseleniumで業務ポータルにアクセスしログインする。
2. xpathを利用してtableタグ内のtrとtdを収集し、データフレームにする。
3. datetimeとdeltaを利用して日付を計算し、3か月単位で区切って公文書をすべて収集する。
4. エクセル、またはGoogleスプレッドシートに入れて適切に利用する。しかし実際に業務ポータルに入ってみたところ、tableどころかiframeも見つけられなかった。
4時間も無駄にしながらクロールが不可能な理由を一度書いてみる。
2. 君のセキュリティは?
1. WebDRM

最初の障壁はWebDRMだった。
これのせいで開発者モードを開くこともできなかった。
しかし開発者モードが既に開いていると、そのサイトに接続しても維持されるという点を利用してみた。
結局開発者モードを開くことに成功した。
2. ブラウザには見えるのに要素がないって?
この部分が最も理解できなかった。
確かに開発者モードでは要素がはっきり見えるのに、クロムドライバーでpage_sourceをすると要素が一つも出てこなかった。
それはseleniumを使おうが、puppeteerを使おうが同じだった。
さらに不思議なのは、コンソール内でのJavaScriptを使っても見えないということだ。
見えるのに見つからない、そんなもどかしさが私をさらに狂わせた。
const puppeteer = require('puppeteer');
const fs = require('fs');
// puppeteerを使用してウェブページのHTMLを取得するスクリプト
(async () => {
let browser;
try {
browser = await puppeteer.launch({
headless: false,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
defaultViewport: null,
userDataDir: './user_data',
});
const page = await browser.newPage();
await page.goto('https://klef.goe.go.kr/keris_ui/main.do', {
waitUntil: 'networkidle0', // すべてのリソース読み込み待ち
timeout: 60000, // 最大60秒待機
});
const html = await page.content();
fs.writeFileSync('schoolDoc.html', html, 'utf8');
console.log('HTMLファイルがschoolDoc.htmlとして保存されました。');
} catch (error) {
console.error('エラー発生:', error);
} finally {
if (browser) await browser.close();
}
})();こうして保存したファイルを再び開いてみると、以下のような姿が見えた。
私が欲しかったテーブルは出ず、ログインモジュールが表示された。

なぜだろうと思い考えてみると、公文書を表示するテーブル自体が別のインストール型プログラムではないかと思った。
そう考えると少し楽に諦めることができた。
3. 業務ポータルはWebソケットを使用している。
まず業務ポータルは一般的なREST APIを通じてデータを送信するのではなく、websocketを利用してユーザーとデータをやり取りする。
この点が非常に興味深かった。

それでrequestsのような他のライブラリでクッキーをコピーし新しい接続を試みようとしても、websocketのハンドシェイクプロセスとセッション維持方式のため不可能だった。
しかしここで挫折することはできなかった。
selenium-wireを利用してwebsocketを通わる要求と応答を見てみることにした。
# 要求を巡回
for request in driver.requests:
if request.response:
content_type = request.response.headers.get('Content-Type', '')
if content_type.startswith('application/json'):
print("== 要求URL:", request.url)
try:
body = request.response.body.decode('utf-8', errors='ignore')
print("== 応答内容:", body)
except Exception as e:
print("== デコードエラー:", e)
driver.quit()そしてbase64でエンコードされたようなアルファベットが出てきた。
これを再びデコードしたが規格に合わず失敗。
ここで私は入るのをやめることに決めた。
3. 感じたこと
公共機関のサイトの速度が遅いことに不満を感じていたが、今回掘り下げてみるとそれでもセキュリティだけはしっかりしているようだと感じた。
遠隔でデータ一つ得るのも簡単ではなく、たくさん学ばせてもらった。
しかし次回も挑戦してみたくなる。

댓글을 불러오는 중...