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

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

Node.js + Puppeteer で動画タイトルをなるべく正確に取得する方法

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

今日は Sae-Porns の中でも地味だけど重要な処理──動画ページのタイトルをなるべく正確に取得する仕組みについて解説します。

今回取り上げるのは、以下のモジュールです:

async function fetchTitle(page) {
    let finalTitle = null;
  
    const jsonLdHandle = await page.$('script[type="application/ld+json"]');
    if (jsonLdHandle) {
      const jsonText = await page.evaluate(el => el.textContent, jsonLdHandle);
      try {
        const data = JSON.parse(jsonText);
        const items = Array.isArray(data) ? data : [data];
        for (const item of items) {
          if (item && item['@type'] === 'VideoObject' && item.name) {
            finalTitle = item.name;
            break;
          }
        }
      } catch (e) {
        console.warn(' JSON-LD parse failed:', e.message);
      }
    }
  
    if (!finalTitle) {
      const ogTitleMeta = await page.$('meta[property="og:title"]');
      if (ogTitleMeta) {
        finalTitle = await page.evaluate(el => el.content, ogTitleMeta);
      }
    }
  
    if (!finalTitle) {
      finalTitle = await page.title();
    }
  
    return { title: finalTitle || null };
  }

なぜ「正確なタイトル取得」が重要なのかというとSae-Porns では、動画メタデータの基礎として、正確なタイトルの取得がすべての出発点になっているからです。

取得されたタイトルは、後続の処理──「日本語変換(MythoMax)」「タグ化(形態素解析)」「検索クエリ生成」などに連動しており、ここで間違ったタイトルを拾ってしまうと、全体の精度が一気に落ちてしまいます。

とはいえ、動画タイトルの記載方法は1つではなく、また、HTML内に複数存在する場合も多く、「これだけ見れば正解が取れる」という絶対的な手段はありません。

ティアラ_びっくり

たとえば、JSON-LD(構造化データ)に書かれている場合もあれば、OGPメタタグ(SNSシェア用)で明示されていることもあるし、単純に <title> タグで済ませているサイトもあります。

このため、Sae-Porns のクローラーでは複数の取得手段を信頼度順に並べて、上から順に試す設計を採用しています。以下がその優先順位です:

 

1位:application/ld+json 内の VideoObject.name

構造化データ(JSON-LD)で、しかも @type: VideoObject の中の name に指定されているタイトル。これは検索エンジン向けに明示的に設定されている情報であり、信頼度が非常に高いです。

そもそも JSON-LD で VideoObject をわざわざ記述しているということは、サイト側が「これは動画ですよ」と検索エンジンにアピールしているという情報です。検索エンジン対策としても極めて丁寧な実装であるため、ここに書かれているタイトルは“本物”である確率が高いのです。

2位:meta[property="og:title"]

いわゆる OGP(Open Graph Protocol)のメタデータ。これはTwitterFacebookでの拡散用に使われる情報です。拡散時に表示されるタイトルなので、サイト運営者が「これを見てほしい」と思う内容になっていることが多いですね。表現は整っているが、逆に「盛られている」こともあり、意図的に誇張されたタイトルである可能性もあります。

3位:<title> タグの中身

最終手段として、ブラウザ上に表示される <title> を取得します。通常、どんなページにも <title> タグはあるため、「どれも取れなかった場合の保険」としては有効。ただし、これはページ全体のタイトルであって、動画のタイトルとは限らない点に注意が必要です。

たとえば「タイトル | サイト名」といった形式も多く、検索性を損なうノイズが含まれることがあります。

このように「信頼度の高いものから順に、フェイルセーフで取得していく」という設計が、Sae-Porns のクローラーの堅牢さを支えています。それでも、HTML構造が壊れていたり、JSON-LDが不正なフォーマットだったりすると、当然ながら JSON.parse() は失敗します。そこで有効なのが以下の行です:

console.warn('JSON-LD parse failed:', e.message);

実運用では、パースエラーやHTML破損は日常茶飯事です。そのため try-catch で囲み、失敗しても全体処理が止まらないように設計しています。安定運用の鍵は、「壊れても落ちない」こと。完璧な構造を前提とするのではなく、例外が出ても処理を進める耐久性が求められます。

そして耐久性の向上と同時に必要なのがメンテナンス性の確保です。

以下の関数の返り値は、以下のように非常にシンプルです:

return { title: finalTitle || null };

このように、出力形式を { title: ... } に統一しておくことで、fetch-description.js や fetch-duration.js など、他の fetch-*.js 系モジュールでも同じインターフェースでデータを扱えるようになります。

これは後々、複数のメタデータを統合して INSERT 処理を行う段階や、メタ情報の欠損を再取得するバッチ処理を組む際にも非常に役立ちます。一見すると単純な「タイトル取得」ですが、こうした基本設計の整合性がSae-Porns 全体の堅牢性と拡張性を支えているのです。

動画ページのタイトル取得は、Sae-Porns におけるすべての処理の出発点です。それゆえに、ただ「取れればいい」というのではなく

  • 構造化データから正確に拾う
  • 取得方法に優先順位をつける
  • 壊れても止まらない設計にする
  • モジュール間のインターフェースを揃える

こうした「地味だけど堅実な工夫」の積み重ねが、1万ページを超える動画ページに対応する信頼性へとつながっています。

クローラー設計において、「壊れたページでも落ちない」「次の処理に繋がる形で返す」──これらは技術というより思想です。

Sae-Porns は、こうした思想に基づいて、日々ちょっとずつ改善を重ねています。

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

sae-porns.org