はじめに
転職活動や企業研究で OpenWork(旧Vorkers)を使っている方は多いと思います。
企業の「総合評価スコア」が一目でわかるのは非常に便利ですが、検索結果一覧を見ているときにふと気になることがありませんか?
「この高スコア、何人の回答に基づいているの?」
「平均年収はどのくらいなんだろう?」
スコアが 4.0 でも、回答者が 3人 なのか 300人 なのかで、その信頼性は大きく異なります。
また、年収情報も企業選びの重要な判断材料ですが、現在のOpenWorkの検索結果画面(企業一覧)では、回答者数(口コミ数)も平均年収も 表示されません。詳細ページに飛ばないと確認できない仕様になっています。
これでは「良さそうな企業だと思ってクリックしたら、回答数が少なすぎて参考にならなかった…」「年収が想定より低かった…」ということが頻発し、効率が悪いです。
そこで、検索結果画面に「回答者数」と「平均年収」を自動で表示するChrome拡張機能 を自作しました。
本記事では、このツールの紹介と、実装にあたって工夫した 「スクレイピングの負荷対策(Rate Limiting)」 や 「パフォーマンス最適化(IntersectionObserver)」 の技術的なポイントを解説します。
ツール紹介
機能概要
この「OpenWork クイック検索」 拡張機能をインストールすると、OpenWorkの企業検索結果一覧において、総合スコアの横に (XX人)💰XXX万円 という形で回答者数と平均年収が自動的に追加表示されます。
(※イメージ: スコアの横にバッジが表示される)
もう一つの便利機能:右クリックで一発検索
求人サイト(GreenやWantedlyなど)を見ているときに、「この会社の評判どうなんだろう?」と思うことはありませんか?
通常なら 「社名をコピー」→「OpenWorkを開く」→「検索窓にペースト」→「検索」 という手順が必要で、地味に面倒です。
この拡張機能を入れると、社名を選択して右クリックするだけ で、直接OpenWorkの検索結果に飛べるようになります。
(※イメージ: 右クリックメニューに「OpenWorkで検索」が表示される)
主な特徴
- 自動表示: ページを開くだけで、各企業の詳細ページから回答者数を取得して表示します
- 右クリック検索: 任意のWebページでテキストを選択し、右クリックメニューから即座にOpenWork検索が可能です。
-
キャッシュ機能: 一度取得したデータは
localStorageに保存されます。次回以降はAPIリクエスト(詳細ページへのアクセス)が発生しないため、高速かつサーバーに優しい設計です - サーバー負荷対策: 検索結果には数十件の企業が並びますが、一気に数十件のリクエストを送るとサーバーに負荷がかかり、最悪の場合IPBANされるリスクがあります。そのため、リクエストキュー(Queue) を実装し、一定間隔(例: 3秒に1回)でゆっくりデータを取得するようにしています
実装のポイント
Chrome拡張機能(Manifest V3)として実装しました。
特に「サーバーに迷惑をかけない」「ブラウザを重くしない」という点に注力しました。
1. Rate Limiting (リクエストキュー)
検索結果ページには多くの企業が表示されますが、Promise.all などで一斉に fetch すると、短期間に大量のアクセスが発生してしまいます。これを防ぐため、直列実行するキュー を作成しました。
// 簡易的な非同期キューの実装
class Queue {
constructor(n = 1, delay = 3000) {
this.n = n; // 同時実行数(今回は1)
this.delay = delay; // 実行間隔(ms)
this.busy = 0;
this.q = [];
}
push(task) {
this.q.push(task);
this.run();
}
async run() {
if (this.busy >= this.n || this.q.length === 0) return;
this.busy++;
const fn = this.q.shift();
try {
await fn();
} finally {
this.busy--;
// 次のタスクまで指定時間待機
setTimeout(() => this.run(), this.delay);
}
}
}
// 3秒に1回だけリクエストを実行するキュー
const queue = new Queue(1, 3000);
このようにすることで、ユーザーがスクロールして大量の企業が表示されても、バックグラウンドでは「3秒に1件ずつ」お行儀よくデータを取得しに行きます。
2. Lazy Loading (IntersectionObserver)
ページ下部の企業まで最初から取得する必要はありません。「ユーザーの画面に見えている企業だけ」 データを取得すれば十分です。
そこで IntersectionObserver を使い、要素が画面内に入ったタイミングでキューにタスクを追加するようにしました。
const observer = new IntersectionObserver(entries => {
entries.forEach(e => {
if (e.isIntersecting) {
// 画面に入ったら処理開始
handleCard(e.target);
// 一度処理したら監視を解除
observer.unobserve(e.target);
}
});
}, { rootMargin: "100px" }); // 少し手前で読み込み開始
// 検索結果の各カード要素を監視
document.querySelectorAll("li.box-15").forEach(c => observer.observe(c));
3. DOM解析の工夫
取得した詳細ページのHTMLから「回答者数」を抜き出す処理も、クラス名が変わってもある程度耐えられるように工夫しています。
function extractResponderCount(html) {
const doc = new DOMParser().parseFromString(html, "text/html");
// 1. まずは既知のCSSセレクタで探す
const sel = '...'; // (省略)
const el = doc.querySelector(sel);
if (el) return parseNumber(el.textContent);
// 2. 見つからない場合、TreeWalkerで「回答者」という文字を含むテキストノードを探す(兜底)
const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT);
// ... (周辺の数字を探すロジック)
}
まとめ
今回は、OpenWorkの検索結果をより便利にするChrome拡張機能を紹介しました。
技術的には Queueによる流量制御 と IntersectionObserverによる遅延読み込み を組み合わせることで、実用性と安全性を両立させています。
もし「自分も使ってみたい!」という方がいれば、ぜひ試してみてください。(※自己責任でお願いします)
今後の展望
- キャッシュの有効期限(TTL)の実装
- 取得状況(Loading/Error)のUI改善
※本記事は技術共有を目的としており、スクレイピングを推奨するものではありません。利用規約を遵守してご利用ください。

