mcpField
B
browser-screenshot

browser-screenshot

github.com/zc277584121生産性04.1k 回インストール9 週間前に更新
リポジトリ

browser-screenshot について

browser-screenshotは、Agent Browserでウェブページの特定領域を撮影するスキルです。ページの調査、領域の特定、切り抜き撮影を行い、専用の永続 Chrome プロファイルを使います。

主な機能

  1. 01候補ページの調査と確認
  2. 02専用 Chrome プロファイルでの操作
  3. 03DOM セレクターによる領域特定
  4. 04切り抜きスクリーンショットの撮影
  5. 05全画面キャプチャの出力回避

活用シーン

  1. 01特定の Reddit 投稿やツイートの撮影
  2. 02記事の一節やチャートの図示
  3. 03関連するページ領域の選択と撮影

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/zc277584121/marketing-skills --skill browser-screenshot

インストール後、Claude Code で/browser-screenshotを入力して読み込まれたか確認します。

詳細

スキル:ブラウザーのスクリーンショット

Web ページ上の特定の領域(Reddit の投稿、ツイート、記事のセクション、グラフなど)に焦点を絞ったスクリーンショットを撮影します。ページ全体をそのまま撮影することはありません。

前提条件:agent-browser がインストールされている必要があります。このスキルは、専用の永続プロファイルを使用する独自のヘッド付き Chrome を使用します。ユーザーが普段使っている Chrome でリモートデバッグを有効にする必要はありません。

専用ブラウザープロファイル

my-chrome-automation と同じ専用の永続プロファイルの規約を使用します。

PROFILE_DIR="${AGENT_BROWSER_PROFILE:-$HOME/.agent-browser-chrome}"
NAMESPACE="${NAMESPACE:-browser-screenshot}"
SESSION="${SESSION:-focused-capture}"

ab() {
  agent-browser --namespace "$NAMESPACE" --session "$SESSION" --headed --profile "$PROFILE_DIR" "$@"
}

ブラウザーコマンドはすべて ab 経由で実行してください。--auto-connect、汎用的な CDP 検出、ユーザーが普段使っている Chrome のプロファイルは使用しないでください。専用プロファイルを使用すると、ユーザーが通常の Chrome を開いたまま、タスク間で Cookie とログイン状態を保持できます。

可能であれば、タスクの内容を表す SESSION を選んでください。同じプロファイルに対して複数のタスクを同時に実行しないでください。プロファイルのロック競合を避けるため、順番に再利用してください。

対象サイトでログインが必要な場合は、ab でサイトを開き、ユーザーが専用のヘッド付きウィンドウでログインまたは認証を完了するまで待ってから、同じプロファイルとセッションで続行してください。

専用プロファイルがすでに実行中の場合

同じプロファイルで Chrome をもう一度起動すると、DevTools エンドポイントを作成する前に終了することがあります。現在開いている Chrome のうち、たまたま見つかったものにフォールバックしてはいけません。このプロファイル用の既知の Agent Browser セッションを再利用するか、コマンドラインに正確な --user-data-dir=$PROFILE_DIR の値が含まれる Chrome プロセスのみに明示的に接続してください。

macOS では、次のようにして専用プロセスとローカルの CDP ポートを特定します。

PROFILE_PID="$(ps -axo pid=,command= | awk -v profile="--user-data-dir=$PROFILE_DIR" 'index($0, profile) && index($0, "--remote-debugging-port=") && $0 !~ /Helper/ {print $1; exit}')"
CDP_PORT="$(lsof -nP -a -p "$PROFILE_PID" -iTCP -sTCP:LISTEN | awk 'NR > 1 && $9 ~ /^127\.0\.0\.1:[0-9]+$/ {split($9, parts, ":"); print parts[2]; exit}')"

ab() {
  agent-browser --namespace "$NAMESPACE" --session "$SESSION" --cdp "$CDP_PORT" "$@"
}

接続する前に、PROFILE_PID と CDP_PORT の値が空でなく、曖昧さがないことを確認してください。確認できない場合は、ユーザーに専用の自動化ウィンドウを閉じてから再試行するよう依頼してください。このタスクで接続しただけのブラウザーは閉じないでください。


概要

このスキルでは、次の一連の処理を行います。

  1. 調査:スクリーンショットに最適なページを探します(ウェブ検索、取得)。
  2. 移動:ブラウザーで適切なページを開きます。
  3. 特定:ページ上の対象要素または領域を見つけます。
  4. 撮影:その領域だけに焦点を絞り、切り抜いたスクリーンショットを撮影します。
厳守事項:画面全体のスクリーンショットは禁止

切り抜いていないビューポート全体またはページ全体のスクリーンショットを、最終結果として絶対に出力しないでください。 全画面のスクリーンショットには、ナビゲーションバー、サイドバー、広告、無関係なコンテンツなどが多く含まれ、記事のイラストとして適していません。すべてのスクリーンショットは、必ず焦点を絞った領域に切り抜いてください。


ステップ 0:調査 — ブラウザーを開く前に情報源を探して検証する

ブラウザーは撮影に使用するものであり、閲覧に使用するものではありません。 Chrome で何かを開く前に、テキストベースのツール(WebSearch、WebFetch)を使用して候補となるページを探し、その内容を読み、実際にスクリーンショットを撮る価値があるページを選んでください。

調査を先に行うワークフロー
  1. WebSearch でトピックに関する候補ページを探します。
  2. 各候補について WebFetch でテキストを読み、必要な情報やビジュアルが含まれているか確認します。
  3. 評価:このページはスクリーンショットを撮る価値がありますか?イラストとして使える、明確で焦点を絞った領域がありますか?
  4. その後に限り、スクリーンショットを撮るためにブラウザーでページを開きます。

これにより大幅に時間を節約できます。スクリーンショットを撮る価値のない候補ページが大半であり、ブラウザーでページを開く手間をかけずに除外できるためです。

代わりにブラウザーを優先する場合

次の場合は、WebSearch/WebFetch の段階を省略し、直接 Chrome で閲覧してください。

  • 対象プラットフォームへのログインが必要な場合 — Reddit、LinkedIn、X/Twitter などのソーシャルプラットフォームでは、ログインしないとコンテンツを閲覧できないことがよくあります。専用のプロファイルを直接使用し、保存済みのログイン状態を再利用してください。
  • ユーザーが検索の必要性が明確なプラットフォームを指定した場合 — 例:「X についての Reddit の投稿を見つけて」や「Y についてのツイートをスクリーンショットして」。Chrome でそのプラットフォームの検索に直接進んでください。
  • WebFetch がブロックされた、または不完全なコンテンツを返した場合 — ブラウザー以外からのリクエストを厳しくブロックするサイトもあります。403、CAPTCHA ページ、または削除されたコンテンツが返された場合は、Chrome に切り替えてください。

このような場合は、Chrome での閲覧が WebSearch に代わります。プラットフォームの検索ページに移動し、検索結果を閲覧して、スクリーンショットを撮るページを決める前に画面上で内容を確認してください。

ページの選択方法

適切なページは、記事の文脈と、対象の新しさや注目度によって異なります。

対象の種類見つけるのに最適なページ検索方法
新しいモデル/機能のリリース(6 か月未満)発表を掲載した公式ブログ投稿WebSearch "<model name>" site:<vendor-domain> blog
定着した製品(6 か月超)製品のランディングページまたはドキュメントの概要WebSearch "<model name>" official page
オープンソースモデルHuggingFace のモデルカードまたは GitHub リポジトリ直接 URL:huggingface.co/<org>/<model>
API サービスAPI ドキュメントページWebSearch "<service name>" API docs

注:この表に挙げたのは一般的な対象の種類であり、すべてを網羅しているわけではありません。どのような対象にも同じく調査を優先する方法を適用し、目的のトピックについて最も信頼性が高く、視覚的にも見やすい情報源のページを見つけてください。

スクリーンショットの情報源として適したページ

基本原則:シンプルにするほど良いです。装飾ではなく、コンテンツに集中してください。

スクリーンショットの情報源として適したページには、文章の段落、重要な引用、データ表、図など、焦点が明確で、それだけで完結した情報が含まれています。ボタン、ナビゲーション、サイドバー、インタラクティブな要素が多いページは避けてください。

  • 推奨:明確な見出しと 1~2 段落の文章があるブログ投稿の一節。単一のグラフや図。名前と説明があるモデルカードのヘッダー。引用や重要な調査結果。
  • 避ける:CTA やナビゲーションがあるランディングページ全体。複数のパネルがあるダッシュボード表示。読みやすいコンテンツよりも UI コントロール(ボタン、ドロップダウン、フォーム)が目立つページ。
  • 公式ブログ投稿は最適です。共有を想定したヒーロー画像、目立つタイトル、簡潔な説明が含まれています。
  • 製品のランディングページも使えますが、ヒーローセクションだけを切り取る場合に限ります。それ以外は無視してください。
  • HuggingFace のモデルカードはオープンソースモデルに適しています。レイアウトが統一されており、モデル名と説明が常に上部にあります。
  • API ドキュメントは代替手段として許容されます。製品名と主要な仕様を表示してください。

目安:キャプチャする範囲に、読みやすいテキストよりもインタラクティブな UI 要素(ボタン、リンク、ナビゲーション項目)が多く含まれる場合は、適切な切り取り範囲ではありません。コンテンツがより充実した範囲を見つけるか、別のページを選んでください。

開く前の URL 検証

404 やリダイレクトに時間を無駄にしないよう、ブラウザーで開く前に WebFetch(軽量な HEAD/GET)で URL を検証してください。

WebFetch: <candidate-url>
→ Check status code, title, and content snippet
→ If 404 or redirect to unrelated page, try next candidate
領域選択の戦略

このスクリーンショットで、記事の読者が何を目にする必要があるかを考えましょう。

記事のコンテキストキャプチャする内容対象領域
ラインアップの中でモデルを紹介するモデル名と主要なキャッチコピー/説明ブログのヒーローセクションまたはHFモデルカードのヘッダー
機能を比較する特徴のハイライトまたは仕様表仕様/機能を示すブログのセクション
特定の機能について説明するその機能の説明該当するセクション見出しと1~2段落
製品/サービスを紹介するブランドアイデンティティと価値提案ランディングページのヒーロー(タイトル+サブタイトル+ビジュアル)

スクリーンショットを見た読者が「なるほど、これがこのモデル/製品なのか」と思えるようにしましょう。「これは何を見せられているのだろう?」と思わせてはいけません。


ステップ1:対象ページに移動する

必ず最初にタブを一覧表示する
ab tab list

専用セッションですでにページが開いているか確認します。ログイン状態とページの状態が適切な既存のタブがあれば、それを再利用します。

入力の種類に応じた移動方法
ユーザーが指定するもの戦略
直接URLab open <url>
検索クエリab open https://www.google.com/search?q=<encoded-query> → 最適な結果を見つけてクリックする
プラットフォーム+トピックプラットフォームの検索URLを作成する(下記参照)→ 対象コンテンツを探す
あいまいな説明Google検索 → 結果を評価する → 最適な候補に移動する
プラットフォーム固有の検索URL
プラットフォーム検索URLのパターン
Reddithttps://www.reddit.com/search/?q=<query>
X / Twitterhttps://x.com/search?q=<query>
LinkedInhttps://www.linkedin.com/search/results/content/?keywords=<query>
Hacker Newshttps://hn.algolia.com/?q=<query>
GitHubhttps://github.com/search?q=<query>
YouTubehttps://www.youtube.com/results?search_query=<query>
ページの読み込みを待つ

移動後、コンテンツの読み込みが落ち着くまで待ちます。

ab wait --load networkidle

注:一部のサイト(Reddit、X、LinkedIn)は、networkidle に到達しないことがあります。open の出力にページタイトルがすでに表示されている場合は、待機を省略してください。代替策として wait 2000 を使用できます。


ステップ2:対象領域を見つける

ここが重要なステップです。目標は、キャプチャするコンテンツを正確に囲む CSSセレクター を見つけることです。

主な方法:DOMセレクターの特定
  1. 注釈付きスクリーンショットを撮影して、ページのレイアウトを把握します。

    ab screenshot --annotate
    
  2. スナップショットを撮影して、ページのアクセシビリティツリーを確認します。

    ab snapshot -i
    
  3. 対象のコンテナー要素を特定します。次の要素を探してください。

    • セマンティックなHTMLコンテナー:<article>、<main>、<section>
    • プラットフォーム固有のコンポーネント(プラットフォームセレクターを参照)
    • データ属性:[data-testid="..."]、[data-id="..."]
  4. get box で検証し、要素のバウンディングボックスが妥当であることを確認します。

    ab get box "<selector>"
    

    { x, y, width, height } が返されます。次の点を確認してください。

    • 幅は100pxより大きく、ビューポート幅より小さいこと
    • 高さは50pxより大きいこと
    • ボックスがページ全体を囲んでいる場合、セレクターの範囲が広すぎるため、絞り込みます
  5. セレクターが見つけにくい場合は、eval を使ってDOMを調べます。

    ab eval "document.querySelector('article')?.getBoundingClientRect()"
    
プラットフォームセレクター

よく利用されるプラットフォームで一般的なコンテナーセレクターは次のとおりです。

プラットフォーム対象一般的なセレクター
Reddit投稿shreddit-post、[data-testid="post-container"]
X / Twitterツイートarticle[data-testid="tweet"]
LinkedInフィードの投稿.feed-shared-update-v2
Hacker Newsストーリーとコメント#hnmain .fatitem
GitHubリポジトリーカード[data-hpc]、.repository-content
YouTube動画プレーヤー領域#player-container-outer
一般的な記事メインコンテンツarticle、main、[role="main"]、.post-content、.article-body

これらのセレクターは時間の経過とともに変更されることがあります。使用する前に必ず get box で確認してください。

複数の一致する要素

セレクターが複数の要素に一致する場合(例:タイムライン上の複数のツイート)、対象を絞り込みます。

# Count matches
ab get count "article[data-testid='tweet']"

# Use nth-child or :first-of-type, or a more specific selector
# Or use eval to find the right one by text content:
ab eval --stdin <<'EOF'
const posts = document.querySelectorAll('article[data-testid="tweet"]');
for (let i = 0; i < posts.length; i++) {
  const text = posts[i].textContent.substring(0, 80);
  console.log(i, text);
}
EOF

次に、:nth-of-type(N) または一意の親セレクターを使って、特定の要素を対象にします。


ステップ 3:フォーカスしたスクリーンショットを撮影する

方法 A:スクロール+ビューポートのスクリーンショット(ビューポートサイズの対象に推奨)

対象の要素がビューポート内に収まる場合に最適です。

# Scroll the target into view
ab scrollintoview "<selector>"
ab wait 500

# Take viewport screenshot
ab screenshot /tmp/browser-screenshot-raw.png

次に、バウンディングボックスを使って切り抜きます(切り抜きを参照)。

方法 B:ページ全体のスクリーンショット+切り抜き(サイズを問わない対象向け)

対象がビューポートより大きい可能性がある場合や、正確な切り抜きが必要な場合に最適です。

# Take full-page screenshot
ab screenshot --full /tmp/browser-screenshot-full.png

# Get the target element's bounding box
ab get box "<selector>"
# Output: { x: 200, y: 450, width: 680, height: 520 }

次に、切り抜きます(切り抜きを参照)。

切り抜き

ImageMagick(IMv7 では magick、convert は非推奨)を使って、スクリーンショットから対象領域を切り抜きます。見た目に余裕を持たせるため、余白を追加します。

Retina ディスプレイの処理

重要:macOS の Retina ディスプレイでは、スクリーンショットは 2 倍の解像度で撮影されます。1728x940 のビューポートでは、3456x1880 の画像が生成されます。この点を必ず考慮してください。

  1. スケール係数を検出する:ビューポートのサイズと実際の画像サイズを比較します。

    # Check actual image dimensions
    magick identify /tmp/screenshot.png
    # → 3456x1880 means 2x scale on a 1728x940 viewport
    
  2. 切り抜く前に、get box の座標にスケール係数を掛けます。

    # get box returns viewport coordinates: { x: 200, y: 450, width: 680, height: 520 }
    # For 2x Retina, actual image coordinates are:
    SCALE=2
    X=$((200 * SCALE))
    Y=$((450 * SCALE))
    W=$((680 * SCALE))
    H=$((520 * SCALE))
    PADDING=$((16 * SCALE))
    
切り抜きコマンド
magick /tmp/browser-screenshot-full.png \
  -crop $((W + PADDING*2))x$((H + PADDING*2))+$((X - PADDING))+$((Y - PADDING)) \
  +repage \
  <output-path>.png

重要:get box は浮動小数点数を返します。ImageMagick に渡す前に整数に丸めてください。

余白:12~20px(ビューポートの px)を使用します。対象にカードや枠付きボックスなど、はっきりした視覚的な境界がある場合は、約 30px に増やします。ユーザーがぴったりの切り抜きを希望する場合は 0 にします。

出力先
  • ユーザーが出力先を指定した場合は、それを使用します
  • 指定がない場合は、現在のディレクトリに reddit-post-screenshot.png、tweet-screenshot.png など、内容が分かる名前で保存します

ステップ 4:結果を確認する

切り抜いた後、出力画像を読み込んで、正しい内容が撮影されていることを確認します。

# Use the Read tool to visually inspect the cropped screenshot

切り抜きが正しくない場合(内容が欠けている、余白が多すぎる、要素が違うなど)、セレクターまたはバウンディングボックスを調整して再試行します。


フォールバック:視覚的なハイライトによる確認

DOM に基づく位置の特定が確実でない場合(セレクターが誤っている可能性がある、候補が複数ある、対象が曖昧など)は、切り抜く前に JS を挿入してハイライトし、視覚的に確認します。

動作の仕組み
  1. 候補要素にハイライト用の枠線を挿入します。

    ab eval --stdin <<'EOF'
    (function() {
      const el = document.querySelector('<selector>');
      if (!el) { console.log('NOT_FOUND'); return; }
      el.style.outline = '4px solid red';
      el.style.outlineOffset = '2px';
      el.scrollIntoView({ block: 'center' });
    })();
    EOF
    
  2. スクリーンショットを撮影し、目視で確認します。

    ab screenshot /tmp/highlight-check.png
    

    スクリーンショットを読み込み、赤い枠線が正しい内容を囲んでいるか確認します。

  3. 正しい場合は、ハイライトを削除して切り抜きに進みます。

    ab eval "document.querySelector('<selector>').style.outline = ''; document.querySelector('<selector>').style.outlineOffset = '';"
    
  4. 間違っている場合は、次の候補を試すかセレクターを調整し、再度ハイライトして確認します。

このフォールバックを使う場合
  • ページに複雑なコンポーネントや入れ子のコンポーネントがあり、どのコンテナーが適切か分からない
  • 似た要素が複数あり、正しいものを選ぶ必要がある
  • ユーザーの説明が曖昧(「ページの中央にあるあのグラフ」など)
  • get box の結果が不自然に見える(大きすぎる、小さすぎる、サイズが 0 など)

ページの準備:撮影前に整理する

最終的なスクリーンショットを撮影する前に、より良い結果を得られるようページを整理します。

# Dismiss cookie banners, popups, overlays
ab eval --stdin <<'EOF'
(function() {
  // Common cookie/popup selectors
  const selectors = [
    '[class*="cookie"] button',
    '[class*="consent"] button',
    '[class*="banner"] [class*="close"]',
    '[class*="modal"] [class*="close"]',
    '[class*="popup"] [class*="close"]',
    '[aria-label="Close"]',
    '[data-testid="close"]'
  ];
  selectors.forEach(sel => {
    document.querySelectorAll(sel).forEach(el => {
      if (el.offsetParent !== null) el.click();
    });
  });

  // Hide fixed/sticky elements that overlay content (nav bars, banners)
  document.querySelectorAll('*').forEach(el => {
    const style = getComputedStyle(el);
    if ((style.position === 'fixed' || style.position === 'sticky') && el.tagName !== 'HTML' && el.tagName !== 'BODY') {
      el.style.display = 'none';
    }
  });
})();
EOF

注意して使用してください:固定要素を非表示にすると、重要な文脈が失われることがあります。オーバーレイが対象領域を目に見えて妨げている場合にのみ実行してください。

閉じられない Cookie バナー

一部の Cookie 同意バナー(Jina AI の Usercentrics など)はシャドウ DOM や iframe 内にあり、JS の click() や remove() では閉じられません。JS で何度も試して時間を無駄にしないでください。代わりに、次の方法を使います。

  1. 切り抜いて除外する — バナーが上端または下端にある場合は、切り抜き範囲を調整して除外します。最も速く、確実な方法です。
  2. スクロールして避ける — 撮影前に対象のコンテンツをスクロールし、バナーの領域から移動させます。

ビューポートのサイズ設定

一貫した高品質のスクリーンショットを撮影するため、撮影前にビューポートを設定します。

# Standard desktop viewport
ab set viewport 1280 800

# Wider for dashboard/data-heavy pages
ab set viewport 1440 900

# Narrower for mobile-like content (social media posts)
ab set viewport 800 600

対象のコンテンツがきれいに表示されるよう、幅が狭すぎず広すぎないビューポートを選びます。


トラブルシューティング

get box が null またはサイズ 0 を返す
  • セレクターがどの要素にも一致していません。get count "<selector>" を使って確認してください。
  • 要素が非表示になっているか、まだレンダリングされていない可能性があります。wait 2000 を試してから、もう一度実行してください。
切り抜いた画像が真っ白、または範囲が間違っている
  • ページ全体のスクリーンショットの座標は、ビューポートの座標と異なる場合があります。get box と併せて screenshot --full を使ってください(両者は同じ座標系を使用します)。
  • ページに横スクロールがあるか確認してください。get box の x 値がずれている場合があります。
対象の要素が iframe 内にある
  • get box と snapshot -i では iframe 内を見ることができません。
  • eval を使って iframe のコンテンツにアクセスしてください。
    ab eval "document.querySelector('iframe').contentDocument.querySelector('<sel>').getBoundingClientRect()"
    
    注:同一オリジンの iframe でのみ機能します。
open は成功したが、ページのコンテンツが違う
  • ブラウザーが別のタブに切り替わっている可能性があります(例:ポップアップやリダイレクトによって新しいタブが開かれた場合)。ナビゲーション後は必ず確認してください。
    ab eval "document.location.href"
    
  • URL が違う場合は、tab list で正しいタブを見つけ、tab goto <N> で切り替えてください。
フォントの読み込み中にスクリーンショットコマンドがタイムアウトする
  • 一部のページ(例:Google の開発者向けドキュメント)は document.fonts.ready で停止します。先に強制的に解決してください。
    ab eval "document.fonts.ready.then(() => 'ok')"
    
    その後、スクリーンショットを再試行してください。
ページに遅延読み込みされるコンテンツがある
  • スクリーンショットを撮る前に下へスクロールして、読み込みを開始してください。
    ab scroll down 1000
    ab wait 1500
    ab scroll up 1000
    

コメント