こんにちは、にょろりんこの備忘録的技術ブログです。
今回は、私が自作している Node.js 製のクローラーの中で使っている flag-update.js という小さなファイルについて紹介します。

このファイルの役割はとてもシンプルで、対象のURLの「処理状況フラグ(visitedカラム)」を更新するだけです。しかし、クローラー全体の挙動をコントロールするうえで、この処理は意外と重要な意味を持っています。
まず前提として、Saepornsのクローラーでは、MySQLのurlsテーブルにある visited カラムで、各URLの処理状態を管理しています。処理対象かどうかを判別するためのフラグとして使っていて、意味は以下のように決めています。
- visited = 0:まだ処理していないURL(初期値)
- visited = 1:処理が完了したURL、または404などで二度と処理しないと判断したURL
- visited = -1:一時的な問題でスキップされたURL(あとで再処理したい)
flag-update.js にはこの visited フラグを更新する関数が2つだけ定義されています。コードとしては以下のようになります。シンプルなコードですね。
//flag-update.js
/**
* URLsテーブルの visited カラムを更新するためのモジュール
* - visited = 1 → 正常処理済み
* - visited = -1 → ブロック・スキップ・エラー等
*/const VISITED_NORMAL = 1;
const VISITED_SKIPPED = -1;async function markUrlVisited(pool, id) {
try {
await pool.query(
'UPDATE urls SET visited = ? WHERE id = ?',
[VISITED_NORMAL, id]
);
console.log(`visited=1 に更新しました (ID=${id})`);
} catch (err) {
console.error(`markUrlVisited エラー: ${err.message}`);
}
}async function markUrlSkipped(pool, id) {
try {
await pool.query(
'UPDATE urls SET visited = ? WHERE id = ?',
[VISITED_SKIPPED, id]
);
console.warn(`visited=-1 に更新しました (ID=${id})`);
} catch (err) {
console.error(`markUrlSkipped エラー: ${err.message}`);
}
}module.exports = {
markUrlVisited,
markUrlSkipped
};
ひとつは markUrlVisited(pool, id) という関数で、正常に処理が完了したときにそのURLの visited を 1 に更新します。もうひとつは markUrlSkipped(pool, id) という関数で、ページの取得に失敗した場合や、IPブロック、タイムアウトなどによって一時的にアクセス不能だったURLに対して visited = -1 を設定します。
重要なのは、visited = -1 にしたURLは「完全にあきらめる」のではなく、「あとでまた見るかもしれない」と判断して一時的に保留しているという点です。
たとえば、429エラー(リクエスト過多)や、「空のページが返ってくる現象」は、時間を置けば再び見れるようになるケースが多いです。そういった場合、ドメインを一時的にクールダウン(アクセス停止)リストに入れつつ、対象URLの visited を -1 にしておきます。
一方で、明らかにもう見る必要がないURL、たとえば404エラーやURL構造上のミスなどは、visited = 1 にして処理済み扱いにしています。
こうしてフラグの値を分けておくことで、「再挑戦すべき失敗」と「永久にスキップすべき失敗」をコード上で明確に分離することができます。
visited = -1 にしたURLについては、夜間に走らせているCRONジョブによって、毎日一度 visited = 0 に戻しています。つまり一度スキップされたURLでも、翌日以降のクローリング対象として自動的に復帰する設計です。
CRONジョブはこういう感じ
# visited=-1 を visited=0 に戻して再処理対象にする(毎朝5:00)
0 5 * * * node /var/www/html/reset-visited-minus1.js >> /var/log/reset-visited.log 2>&1
reset-visited-minus1.jsは以下
// reset-visited-minus1.js
// visited = -1 のURLを再度 visited = 0 に戻す(再試行キュー復帰)const mysql = require('mysql');
const util = require('util');// DB接続設定
const pool = mysql.createPool({
connectionLimit: 10,
host: '*****',
user: '*****',
password: '*******',
database: '****'
});
pool.query = util.promisify(pool.query);(async () => {
try {
const [result] = await pool.query(`
UPDATE urls
SET visited = 0
WHERE visited = -1
`);
console.log(`visited=-1 → 0 に変更: ${result.affectedRows} 件`);
} catch (err) {
console.error(`エラー: ${err.message}`);
} finally {
pool.end();
}
})();
これにより、突発的なブロックやネットワーク障害に対しても柔軟に対応でき、再クローリングのチャンスを確保しつつ、無限リトライによる無駄も避けられます。
このように、flag-update.js は一見するとただのSQL発行用ユーティリティに見えますが、クローラー全体の再試行制御や安定運用にとって非常に重要な「状態管理の中核」を担っているファイルです。
地味だけど大事な処理。それが、今回紹介した flag-update.js でした。
それではみなさん、よい開発ライフを。
このコードを実際に使って動いている、あなたを追跡しないアダルト動画検索エンジン「Sae-Porns」はこちら
※18歳未満の方はご利用になれません。