ぽっぺんしゃんにょろりんこ

匿名・非追跡型アダルト動画検索エンジンの設計ノート

Node.jsでループ処理+データベースをフラグ管理!未処理URLだけを自動でクローリングする話

こんにちは、にょろりんこの備忘録的技術ブログです。

今日は「Node.jsで未処理のURLだけを自動で回していくループ処理モジュール」を紹介します。名前は `loop.js`。このコードはすごく地味だけど、WebクローラやURL収集バッチの「心臓部」とも言える存在です。

やってることはとてもシンプルで、

  • MySQLに入っている `urls` テーブルから `visited = 0` のURLを1件だけ取得
  • 処理が終わったら `visited = 1` に更新

という、SELECT → 処理 → UPDATEの王道ループ構成です。

これによって、たとえば「そのページのURLを抽出し終わったら、フラグ(visited)を記録して次へ進む」といった芸当ができるようになります。

Node.js + MySQL というよくある構成ですが、しっかり非同期処理(async/await)にも対応していて、今後いろんな処理と組み合わせるための「コアモジュール」的なポジションにしてます。

ティアラと箱

まずはDB接続モジュールから見ていきます。今回のループ処理は、MySQL上にある `urls` テーブルを対象にしているので、最初はデータベースとの接続が必要です。

Node.jsでは `mysql` パッケージを使って接続できますが、ここでは複数のクエリを同時に扱えるように「接続プール(Pool)」を使っています。

```js
// DB接続プール
const pool = mysql.createPool({
  connectionLimit: 10,
  host: '***.***.***.***',
  user: 'your_user_name',
  password: 'your_password',
  database: 'your_database_name'
});
pool.query = util.promisify(pool.query);

次はこの行

```js
pool.query = util.promisify(pool.query);

これは何をしてるかというと、mysql の .query() 関数は本来コールバック形式なんですが、それを async/await で使えるようにしてるんです。この1行があるだけで、あとは普通に await pool.query(...) で書けるようになるので、コードがめちゃくちゃスッキリします。

そして、このモジュールのコア部分その1がこちら。

```js
// 外部から使う:未visitのURLを1件取得
async function getNextUrl() {
  const rows = await pool.query(
    'SELECT id, url FROM urls WHERE visited = 0 ORDER BY id ASC LIMIT 1'
  );
  return rows.length > 0 ? rows[0] : null;
}

この関数では、まだ処理していない(visited = 0)URLを1件だけ、id 昇順で取得しています。LIMIT 1 にしているので、常に1件だけ返ってきますし、なければ null を返すようになっているため、「もう処理すべきURLがない」ときにも落ちずに安全にスキップできます。大量のURLが溜まっていても、1件ずつ順に処理していける「ポーリング方式」ですね。

あえて複雑な並列処理はせず、1件ずつ確実に拾って処理する構成にしているのは、精度や安定性を重視しているからです。

続いて、このモジュールのコア部分その2。

```js
// 外部から使う:visited=1 に更新
async function markUrlVisited(pool, id) {
  try {
    await pool.query('UPDATE urls SET visited = 1 WHERE id = ?', [id]);
  } catch (err) {
    console.error(`visited更新失敗 (id=${id}):`, err.message);
  }
}

この関数では、処理が終わったURLの visited フラグを 1(=処理済)に更新しています。SQL文は非常にシンプルですが、ちゃんと try/catch でラップしておくことで、たとえDB接続やクエリに失敗してもスクリプト全体が落ちないようになっています。

ちなみに ? プレースホルダを使っているのは、SQLインジェクション対策も兼ねた安全な書き方ですね。今回はIDしか渡してないので問題になりづらいですが、習慣としては大切です。

この1行のUPDATEによって、「このURLはもう処理したよ」という情報がDBに残る=状態の永続化ができるというわけです。

というわけで、この `loop.js` モジュールだけでも、

  • 未処理のURLを安全に1件ずつ取得して、
  • 処理済みのURLはしっかりDB側でフラグを立てて、
  • 途中でスクリプトを止めてもまた続きから再開できる

という、クローリング系バッチ処理の安定運用に欠かせない基本機能が詰まっています。

このあとは、実際にこの `loop.js` のロジックを使って、

というような「実処理部分」につなげていきます。小さいけど地味に便利なモジュール、ぜひ使いまわしてみてください。ではまた。

それではみなさん、よい開発ライフを。

※このコードが実際に使われている「追跡されないアダルト動画の検索エンジンSae-Porns」はこちら!よかったら見ていってください。(18歳未満の方はご利用できません。)

sae-porns.org