Agent Skills by ALSEL
Anthropic Claudeソフトウェア開発⭐ リポ 0品質スコア 50/100

web-scraping

アンチBot回避・コンテンツ抽出・未公開API活用・ポイズンピル検出を備えたWebスクレイピングスキル。Webサイトからのコンテンツ取得、ペイウォール突破、スクレイピングカスケードの実装、SNSデータ処理が必要な際に使用。requests・trafilatura・ステルスモード付きPlaywright・yt-dlp・instaloaderのパターンに対応。

description の原文を見る

Web scraping with anti-bot bypass, content extraction, undocumented APIs and poison pill detection. Use when extracting content from websites, handling paywalls, implementing scraping cascades or processing social media. Covers requests, trafilatura, Playwright with stealth mode, yt-dlp and instaloader patterns.

SKILL.md 本文

Webスクレイピング方法論

フォールバック戦略とアンチボット対策を備えた、信頼性の高い倫理的なWebスクレイピングのパターン。

スクレイピングカスケードアーキテクチャ

複数の抽出戦略を自動フォールバック付きで実装します:

from abc import ABC, abstractmethod
from typing import Optional
import requests
from bs4 import BeautifulSoup
import trafilatura

#for .py files
from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync

#for .ipynb files
import asyncio
from playwright.async_api import async_playwright

class ScrapingResult:
    def __init__(self, content: str, title: str, method: str):
        self.content = content
        self.title = title
        self.method = method  # Track which method succeeded

class Scraper(ABC):
    @abstractmethod
    def fetch(self, url: str) -> Optional[ScrapingResult]: ...

class TrafilaturaCscraper(Scraper):
    """Fast, lightweight extraction for standard articles."""

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        try:
            downloaded = trafilatura.fetch_url(url)
            if not downloaded:
                return None

            content = trafilatura.extract(
                downloaded,
                include_comments=False,
                include_tables=True,
                favor_recall=True
            )

            if not content or len(content) < 100:
                return None

            # Extract title separately
            soup = BeautifulSoup(downloaded, 'html.parser')
            title = soup.find('title')
            title_text = title.get_text() if title else ''

            return ScrapingResult(content, title_text, 'trafilatura')
        except Exception:
            return None

class RequestsScraper(Scraper):
    """HTTP requests with rotating user agents."""

    USER_AGENTS = [
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36',
    ]

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        import random

        headers = {
            'User-Agent': random.choice(self.USER_AGENTS),
            'Accept': 'text/html,application/xhtml+xml',
            'Accept-Language': 'en-US,en;q=0.9',
        }

        try:
            response = requests.get(url, headers=headers, timeout=30)
            response.raise_for_status()

            soup = BeautifulSoup(response.text, 'html.parser')

            # Remove script/style elements
            for element in soup(['script', 'style', 'nav', 'footer', 'aside']):
                element.decompose()

            # Find main content
            main = soup.find('main') or soup.find('article') or soup.find('body')
            content = main.get_text(separator='\n', strip=True) if main else ''

            title = soup.find('title')
            title_text = title.get_text() if title else ''

            if len(content) < 100:
                return None

            return ScrapingResult(content, title_text, 'requests')
        except Exception:
            return None

class PlaywrightScraper(Scraper):
    """Heavy JavaScript rendering with stealth mode for anti-bot bypass."""

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        try:
            with sync_playwright() as p:
                browser = p.chromium.launch(headless=True)
                context = browser.new_context(
                    viewport={'width': 1920, 'height': 1080},
                    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                )
                page = context.new_page()

                # Apply stealth to avoid detection
                stealth_sync(page)

                page.goto(url, wait_until='networkidle', timeout=60000)

                # Wait for content to load
                page.wait_for_timeout(2000)

                # Extract content
                content = page.evaluate('''() => {
                    const article = document.querySelector('article, main, .content, #content');
                    return article ? article.innerText : document.body.innerText;
                }''')

                title = page.title()

                browser.close()

                if len(content) < 100:
                    return None

                return ScrapingResult(content, title, 'playwright')
        except Exception:
            return None

class PlaywrightScraperAsync:
    """Async Playwright scraper for Jupyter notebooks (.ipynb files).
    
    Jupyter notebooks run their own event loop, so sync Playwright won't work.
    Use this async version with `await` in notebook cells.
    """

    async def fetch(self, url: str) -> Optional[ScrapingResult]:
        try:
            async with async_playwright() as p:
                browser = await p.chromium.launch(headless=True)
                context = await browser.new_context(
                    viewport={'width': 1920, 'height': 1080},
                    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                )
                page = await context.new_page()

                # Note: playwright-stealth async version
                # from playwright_stealth import stealth_async
                # await stealth_async(page)

                await page.goto(url, wait_until='networkidle', timeout=60000)

                # Wait for content to load
                await page.wait_for_timeout(2000)

                # Extract content
                content = await page.evaluate('''() => {
                    const article = document.querySelector('article, main, .content, #content');
                    return article ? article.innerText : document.body.innerText;
                }''')

                title = await page.title()

                await browser.close()

                if len(content) < 100:
                    return None

                return ScrapingResult(content, title, 'playwright_async')
        except Exception:
            return None

# Usage in Jupyter notebook cells:
# scraper = PlaywrightScraperAsync()
# result = await scraper.fetch('https://example.com')

class ScrapingCascade:
    """Try multiple scrapers in order until one succeeds."""

    def __init__(self):
        self.scrapers = [
            TrafilaturaCscraper(),
            RequestsScraper(),
            PlaywrightScraper(),
        ]

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        for scraper in self.scrapers:
            result = scraper.fetch(url)
            if result:
                return result
        return None

アンチボット環境(2026年5月現在)

上記のカスケード(requeststrafilatura → Playwright + playwright-stealth)は、プレーンHTMLと軽度に保護されたJavaScriptサイトに対応します。最新のアンチボットスタック(Cloudflare Bot Management / Turnstile、DataDome、Akamai Bot Manager、PerimeterX)は複数の検出シグナルを層状に配置します:TLS / HTTP-2フィンガープリント、ブラウザフィンガープリント、JavaScript実行証明、住宅用IP評判、セッション動作。単一のツールですべてに対抗することはできません。

playwright-stealth(2.0以上、現在)は明らかな検出ベクトル — navigator.webdriverchrome.runtime、プラグイン列挙、言語設定、WebGLフィンガープリント — にパッチを当てます。これをスタートラインとして見なし、天井ではありません。ターゲットがTLSフィンガープリントを行うか、Turnstileを実行する場合、ステルスだけでは合格しません。

ツール対応レイヤー注釈
curl_cffiTLS / HTTP-2フィンガープリントrequestsの代替品で、Chrome/Safari/Edge JA3+ALPNをミミック。JavaScriptは実行不可 — JavaScriptが不要な場合は、パースされたHTML抽出器と組み合わせます。
playwright-stealth 2.xJavaScript実行時フィンガープリントPlaywright/Chromiumのスタートライン。ボットスタックより更新が遅れます。ローテーションとの組み合わせを予想します。
CamoufoxC++レベルでのJS + ブラウザフィンガープリントFirefoxベースのステルスブラウザ。JavaScriptサイドチェックが見抜けないほど低いレベルでフィンガープリント値をなりすまします。Chromiumベースのステルスが検出された場合に使用します。
SeleniumBase UC ModeTurnstile + ブラウザフィンガープリント2026年のワンショットTurnstileソルバーに最も近いものですが、playwright-stealthより重いです。
住宅用プロキシプールIP評判データセンターIP(DigitalOcean、AWS)は最初のリクエストで課題が出されます。住宅用プールはコストが高いですが、防御の最も安価な層を迂回します。

**動作する最も軽いツールを使用します。**積極的な防御のないターゲットはCamoufoxやプロキシプールを必要としません — curl_cffiとスリープで通常は十分です。Turnstile チャレンジまたはDataDomeインタースティシャルを明示的に配信するサイトのために、より重いツールを予約します。

未文書化API

未文書化APIの検出

ブラウザ開発者ツールを使用してAPIを検出します:

  1. 開発者ツールを開く(右クリック → 検査、またはF12)
  2. ネットワークタブに移動してすべてのリクエストを監視
  3. Fetch/XHRでフィルターしてAPIコールのみを表示
  4. アクションをトリガー(検索、スクロール、クリック)
  5. レスポンスを分析 — 通常はキーバリューペアを持つJSON
  6. cURLとしてコピー(リクエストを右クリック)
  7. コードに変換curlconverter.comを使用)

APIリクエストの削減

開発者ツールからcURLをコピーする場合、多くのパラメータが含まれます。削減するには:

  1. 不要なクッキーを削除 — まずそれなしでテスト
  2. 認証トークンを保持(必要な場合)
  3. 変更可能な入力パラメータを識別(検索用語のprefixなど)
  4. パラメータ値をテスト — 期限切れのものもあるため、定期的に確認

例:オートコンプリートAPIのリバースエンジニアリング

import requests
import time

def search_suggestions(keyword: str) -> dict:
    """
    Get autocompleted search suggestions from an undocumented API.
    Stripped down from browser dev tools capture.
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0',
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Accept-Language': 'en-US,en;q=0.5',
    }

    params = {
        'prefix': keyword,
        'suggestion-type': ['WIDGET', 'KEYWORD'],
        'alias': 'aps',
        'plain-mid': '1',
    }

    response = requests.get(
        'https://completion.amazon.com/api/2017/suggestions',
        params=params,
        headers=headers
    )
    return response.json()

# Collect suggestions for multiple keywords
keywords = ['a', 'b', 'cookie', 'sock']
data = []

for keyword in keywords:
    suggestions = search_suggestions(keyword)
    suggestions['search_word'] = keyword  # track seed keyword
    time.sleep(1)  # rate limit yourself
    data.extend(suggestions.get('suggestions', []))

出典:Leon Yin、"Finding Undocumented APIs," Inspect Element、2023

毒薬ピル検出

ペイウォール、アンチボットページ、その他の失敗を検出します:

from dataclasses import dataclass
from enum import Enum
import re

class PoisonPillType(Enum):
    PAYWALL = 'paywall'
    CAPTCHA = 'captcha'
    RATE_LIMIT = 'rate_limit'
    CLOUDFLARE = 'cloudflare'
    LOGIN_REQUIRED = 'login_required'
    NOT_FOUND = 'not_found'
    NONE = 'none'

@dataclass
class PoisonPillResult:
    detected: bool
    type: PoisonPillType
    confidence: float
    details: str

class PoisonPillDetector:
    PATTERNS = {
        PoisonPillType.PAYWALL: [
            r'subscribe to continue',
            r'subscription required',
            r'become a member',
            r'sign up to read',
            r'you\'ve reached your limit',
            r'article limit reached',
        ],
        PoisonPillType.CAPTCHA: [
            r'verify you are human',
            r'captcha',
            r'robot verification',
            r'prove you\'re not a robot',
        ],
        PoisonPillType.RATE_LIMIT: [
            r'too many requests',
            r'rate limit exceeded',
            r'slow down',
            r'429',
        ],
        PoisonPillType.CLOUDFLARE: [
            r'checking your browser',
            r'cloudflare',
            r'ddos protection',
            r'please wait while we verify',
        ],
        PoisonPillType.LOGIN_REQUIRED: [
            r'sign in to continue',
            r'log in required',
            r'create an account',
        ],
    }

    PAYWALL_DOMAINS = {
        'nytimes.com': PoisonPillType.PAYWALL,
        'wsj.com': PoisonPillType.PAYWALL,
        'washingtonpost.com': PoisonPillType.PAYWALL,
        'ft.com': PoisonPillType.PAYWALL,
        'bloomberg.com': PoisonPillType.PAYWALL,
    }

    def detect(self, url: str, content: str, status_code: int = 200) -> PoisonPillResult:
        # Check status code
        if status_code == 429:
            return PoisonPillResult(True, PoisonPillType.RATE_LIMIT, 1.0, 'HTTP 429')
        if status_code == 403:
            return PoisonPillResult(True, PoisonPillType.CLOUDFLARE, 0.8, 'HTTP 403')
        if status_code == 404:
            return PoisonPillResult(True, PoisonPillType.NOT_FOUND, 1.0, 'HTTP 404')

        # Check known paywall domains
        from urllib.parse import urlparse
        domain = urlparse(url).netloc.replace('www.', '')
        for paywall_domain, pill_type in self.PAYWALL_DOMAINS.items():
            if paywall_domain in domain:
                # Check if content is suspiciously short (paywall truncation)
                if len(content) < 500:
                    return PoisonPillResult(True, pill_type, 0.9, f'Short content from {domain}')

        # Pattern matching
        content_lower = content.lower()
        for pill_type, patterns in self.PATTERNS.items():
            for pattern in patterns:
                if re.search(pattern, content_lower):
                    return PoisonPillResult(True, pill_type, 0.7, f'Pattern match: {pattern}')

        return PoisonPillResult(False, PoisonPillType.NONE, 0.0, '')

ソーシャルメディアスクレイピング

yt-dlpを使用したYouTube

import yt_dlp
from pathlib import Path

def download_video_metadata(url: str) -> dict:
    """Extract metadata without downloading video."""
    ydl_opts = {
        'skip_download': True,
        'quiet': True,
        'no_warnings': True,
    }

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(url, download=False)
        return {
            'title': info.get('title'),
            'description': info.get('description'),
            'duration': info.get('duration'),
            'upload_date': info.get('upload_date'),
            'view_count': info.get('view_count'),
            'channel': info.get('channel'),
            'thumbnail': info.get('thumbnail'),
        }

def download_video(url: str, output_dir: Path, audio_only: bool = False) -> Path:
    """Download video or audio."""
    output_template = str(output_dir / '%(title)s.%(ext)s')

    ydl_opts = {
        'outtmpl': output_template,
        'quiet': True,
    }

    if audio_only:
        ydl_opts['format'] = 'bestaudio/best'
        ydl_opts['postprocessors'] = [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
        }]

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(url, download=True)
        filename = ydl.prepare_filename(info)
        if audio_only:
            filename = filename.rsplit('.', 1)[0] + '.mp3'
        return Path(filename)

def get_transcript(url: str) -> list[dict]:
    """Extract auto-generated or manual subtitles."""
    ydl_opts = {
        'skip_download': True,
        'writesubtitles': True,
        'writeautomaticsub': True,
        'subtitleslangs': ['en'],
        'quiet': True,
    }

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(url, download=False)

        # Check for subtitles
        subtitles = info.get('subtitles', {})
        auto_captions = info.get('automatic_captions', {})

        # Prefer manual subtitles over auto-generated
        subs = subtitles.get('en') or auto_captions.get('en')
        if not subs:
            return []

        # Get the vtt or json format
        for sub in subs:
            if sub['ext'] in ['vtt', 'json3']:
                # Download and parse subtitle file
                # ... implementation depends on format
                pass

        return []

instaloaderを使用したInstagram

import instaloader
from pathlib import Path

class InstagramScraper:
    def __init__(self, username: str = None, session_file: str = None):
        self.loader = instaloader.Instaloader(
            download_videos=True,
            download_video_thumbnails=False,
            download_geotags=False,
            download_comments=False,
            save_metadata=True,
            compress_json=False,
        )

        if session_file and Path(session_file).exists():
            self.loader.load_session_from_file(username, session_file)

    def get_profile_posts(self, username: str, limit: int = 50) -> list[dict]:
        """Get recent posts from a profile."""
        profile = instaloader.Profile.from_username(self.loader.context, username)
        posts = []

        for i, post in enumerate(profile.get_posts()):
            if i >= limit:
                break

            posts.append({
                'shortcode': post.shortcode,
                'url': f'https://instagram.com/p/{post.shortcode}/',
                'caption': post.caption,
                'timestamp': post.date_utc.isoformat(),
                'likes': post.likes,
                'comments': post.comments,
                'is_video': post.is_video,
                'video_url': post.video_url if post.is_video else None,
            })

        return posts

    def download_post(self, shortcode: str, output_dir: Path):
        """Download a single post's media."""
        post = instaloader.Post.from_shortcode(self.loader.context, shortcode)
        self.loader.download_post(post, target=str(output_dir))

yt-dlpを使用したTikTok

def scrape_tiktok_profile(username: str, output_dir: Path, limit: int = 50) -> list[dict]:
    """Scrape TikTok profile videos."""
    profile_url = f'https://tiktok.com/@{username}'

    ydl_opts = {
        'quiet': True,
        'extract_flat': True,  # Don't download, just get info
        'playlistend': limit,
    }

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(profile_url, download=False)
        videos = []

        for entry in info.get('entries', []):
            videos.append({
                'id': entry.get('id'),
                'title': entry.get('title'),
                'url': entry.get('url'),
                'timestamp': entry.get('timestamp'),
                'view_count': entry.get('view_count'),
            })

        return videos

def download_tiktok_video(url: str, output_dir: Path) -> Path:
    """Download a single TikTok video."""
    ydl_opts = {
        'outtmpl': str(output_dir / '%(id)s.%(ext)s'),
        'quiet': True,
    }

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(url, download=True)
        return Path(ydl.prepare_filename(info))

リクエストパターン

ユーザーエージェントとヘッダーのローテーション

import random
from fake_useragent import UserAgent

class RequestManager:
    def __init__(self):
        self.ua = UserAgent()
        self.session = requests.Session()

    def get_headers(self) -> dict:
        return {
            'User-Agent': self.ua.random,
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
            'Accept-Encoding': 'gzip, deflate, br',
            'DNT': '1',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
        }

    def fetch(self, url: str, retry_count: int = 3) -> requests.Response:
        for attempt in range(retry_count):
            try:
                response = self.session.get(
                    url,
                    headers=self.get_headers(),
                    timeout=30
                )
                response.raise_for_status()
                return response
            except requests.RequestException as e:
                if attempt == retry_count - 1:
                    raise
                time.sleep(2 ** attempt)  # Exponential backoff

遅延を伴う配慮あるスクレイピング

import time
import random
from urllib.parse import urlparse

class PoliteRequester:
    def __init__(self, min_delay: float = 1.0, max_delay: float = 3.0):
        self.min_delay = min_delay
        self.max_delay = max_delay
        self.last_request_per_domain = {}

    def wait_for_domain(self, url: str):
        domain = urlparse(url).netloc
        last_request = self.last_request_per_domain.get(domain, 0)

        elapsed = time.time() - last_request
        delay = random.uniform(self.min_delay, self.max_delay)

        if elapsed < delay:
            time.sleep(delay - elapsed)

        self.last_request_per_domain[domain] = time.time()

倫理、robots.txt、および法的状況

スクレイピングは技術的には単純ですが、倫理的には複雑で、法的には移動中です。米国の現在の状態(2026年):

コンピュータ詐欺および濫用法(CFAA)。 Van Buren v. United States(2021年)およびhiQ Labs v. LinkedIn(2022年)はCFAAを狭め、スクレイピング公開、非認証ページは「不正アクセス」を構成しません。ログイン(または認証情報の使用)、技術的アクセス制御の迂回、明示的な中止要求後のスクレイピングは法的に微妙です。州の同等物(例えば、カリフォルニア州CDAFA)は時々連邦法より踏み込みます。

利用規約。 多くのサイトのToSはスクレイピングを禁止しています。ToSは契約であり、刑事法令ではなく — 違反は民事請求(契約違反、違法な干渉、一部の管轄権での不動産への不法侵入)を招きますが、刑務所ではありません。リスクプロファイルはCFAAと大きく異なります。

robots.txtは丁寧なリクエストであり、法的義務ではありません。無視することは犯罪責任をもたらしませんが、裁判所はそれを意図の証拠として引用しています。公益のためのジャーナリズムの場合、その意図は防御可能です。商業的使用の場合、それはより難しいです。

EU GDPR / UK DPA。 スクレイピングがEU/UK住民の個人データを取得する場合、スクレイパーを実行する場所に関係なく、GDPR/DPAが適用されます。公開可用性は個人データをこれらの制度から除外しません — Lloyd v. Google(英国最高裁2021年)およびSchrems II系列のCJEU、スクレイピング個人データは合法的基盤なしでは現実的責任です。

実用的な基準:

  • 常にrobots.txtを読む。クロール遅延を尊重。Disallow:を尊重。
  • レート制限を尊重; ジッターを追加; 429でバックオフ。
  • 明示的な許可がない限り、認証の背後でスクレイピングしない。
  • 合法的基盤なしに個人データ(名前、メール、写真)をスクレイピングしない。
  • 大量クロール時は説明的なUser-Agentと連絡先URLで自分自身を識別。
  • 冗長なリクエストを避けるために積極的にキャッシュ。
  • 中止要求または明示的なブロッキング信号を受け取ったら停止 — その先に進行することが、民事紛争をCFAA事件に変える動きです。

特定のプラットフォームに関する注釈。 InstagramのinstaloaderおよびTikTok via yt-dlpスクレイピングは今日機能しますが、頻繁に壊れます — MetaとTikTokは月ごとにアンチボット更新を展開します。使用した認証情報のアカウント禁止は一般的です。ジャーナリズムの場合、公式API(Meta Content Library、TikTok Research API)は遅いですがより持続可能です。

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
jamditis
リポジトリ
jamditis/claude-skills-journalism
ライセンス
MIT
最終更新
不明

Source: https://github.com/jamditis/claude-skills-journalism / ライセンス: MIT

関連スキル

汎用ソフトウェア開発⭐ リポ 39,967

doubt-driven-development

重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 1,175

apprun-skills

TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。

by yysun
OpenAIソフトウェア開発⭐ リポ 797

desloppify

コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。

by Git-on-my-level
汎用ソフトウェア開発⭐ リポ 39,967

debugging-and-error-recovery

テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

test-driven-development

テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

incremental-implementation

変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。

by addyosmani
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: jamditis · jamditis/claude-skills-journalism · ライセンス: MIT