mcpField
B
beautifulsoup-parsing

beautifulsoup-parsing

github.com/mindrallyデータ・データベース287883 回インストール3 週間前に更新
リポジトリ

beautifulsoup-parsing について

BeautifulSoup で HTML・XML を解析し、データ抽出ワークフローを構築する Python 開発者向けスキルです。要素検索、DOM の走査、堅牢な抽出、解析効率を扱います。

主な機能

  1. 01HTML・XML の解析
  2. 02CSS セレクターと検索
  3. 03DOM の走査
  4. 04テキスト・属性の抽出
  5. 05不正な HTML の処理
  6. 06メモリ効率のよい解析

活用シーン

  1. 01Web ページからの項目抽出
  2. 02表やリストの解析
  3. 03構造が不揃いな HTML 文書の処理
  4. 04再利用可能なスクレイピング関数の構築

インストール

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

$npx skills add https://github.com/mindrally/skills --skill beautifulsoup-parsing

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

詳細

BeautifulSoupによるHTML解析

BeautifulSoup、PythonによるHTML/XML解析、DOMナビゲーション、ウェブスクレイピング向けの効率的なデータ抽出パイプライン構築に精通しています。

主な専門知識

  • BeautifulSoup APIと解析メソッド
  • CSSセレクターと検索メソッド
  • DOMの走査とナビゲーション
  • さまざまなパーサーを使ったHTML/XML解析
  • requestsライブラリとの連携
  • 不正な形式のHTMLへの適切な対処
  • データ抽出のパターンとベストプラクティス
  • メモリー効率の高い処理

主な原則

  • 正確なPythonの例を用いて、簡潔で技術的なコードを記述します
  • 可読性、効率性、保守性を優先します
  • 共通の抽出タスクには、モジュール化された再利用可能な関数を使用します
  • 適切なデフォルト値を設定し、データが欠落していても適切に処理します
  • PEP 8のスタイルガイドラインに従います
  • 堅牢なスクレイピングを実現するため、適切なエラー処理を実装します

基本設定

pip install beautifulsoup4 requests lxml
HTMLの読み込み
from bs4 import BeautifulSoup
import requests

# From string
html = '<html><body><h1>Hello</h1></body></html>'
soup = BeautifulSoup(html, 'lxml')

# From file
with open('page.html', 'r', encoding='utf-8') as f:
    soup = BeautifulSoup(f, 'lxml')

# From URL
response = requests.get('https://example.com')
soup = BeautifulSoup(response.content, 'lxml')
パーサーのオプション
# lxml - Fast, lenient (recommended)
soup = BeautifulSoup(html, 'lxml')

# html.parser - Built-in, no dependencies
soup = BeautifulSoup(html, 'html.parser')

# html5lib - Most lenient, slowest
soup = BeautifulSoup(html, 'html5lib')

# lxml-xml - For XML documents
soup = BeautifulSoup(xml, 'lxml-xml')

要素の検索

タグによる検索
# First matching element
soup.find('h1')

# All matching elements
soup.find_all('p')

# Shorthand
soup.h1  # Same as soup.find('h1')
属性による検索
# By class
soup.find('div', class_='article')
soup.find_all('div', class_='article')

# By ID
soup.find(id='main-content')

# By any attribute
soup.find('a', href='https://example.com')
soup.find_all('input', attrs={'type': 'text', 'name': 'email'})

# By data attributes
soup.find('div', attrs={'data-id': '123'})
CSSセレクター
# Single element
soup.select_one('div.article > h2')

# Multiple elements
soup.select('div.article h2')

# Complex selectors
soup.select('a[href^="https://"]')  # Starts with
soup.select('a[href$=".pdf"]')      # Ends with
soup.select('a[href*="example"]')   # Contains
soup.select('li:nth-child(2)')
soup.select('h1, h2, h3')           # Multiple
関数を使った検索
import re

# By regex
soup.find_all('a', href=re.compile(r'^https://'))

# By function
def has_data_attr(tag):
    return tag.has_attr('data-id')

soup.find_all(has_data_attr)

# String matching
soup.find_all(string='exact text')
soup.find_all(string=re.compile('pattern'))

データの抽出

テキストコンテンツ
# Get text
element.text
element.get_text()

# Get text with separator
element.get_text(separator=' ')

# Get stripped text
element.get_text(strip=True)

# Get strings (generator)
for string in element.stripped_strings:
    print(string)
属性
# Get attribute
element['href']
element.get('href')  # Returns None if missing
element.get('href', 'default')  # With default

# Get all attributes
element.attrs  # Returns dict

# Check attribute exists
element.has_attr('class')
HTMLコンテンツ
# Inner HTML
str(element)

# Just the tag
element.name

# Prettified HTML
element.prettify()

DOMナビゲーション

親要素/祖先要素
element.parent
element.parents  # Generator of all ancestors

# Find specific ancestor
for parent in element.parents:
    if parent.name == 'div' and 'article' in parent.get('class', []):
        break
子要素
element.children      # Direct children (generator)
list(element.children)

element.contents      # Direct children (list)
element.descendants   # All descendants (generator)

# Find in children
element.find('span')  # Searches descendants
兄弟要素
element.next_sibling
element.previous_sibling

element.next_siblings      # Generator
element.previous_siblings  # Generator

# Next/previous element (skips whitespace)
element.next_element
element.previous_element

データ抽出のパターン

安全な抽出
def safe_text(element, selector, default=''):
    """Safely extract text from element."""
    found = element.select_one(selector)
    return found.get_text(strip=True) if found else default

def safe_attr(element, selector, attr, default=None):
    """Safely extract attribute from element."""
    found = element.select_one(selector)
    return found.get(attr, default) if found else default
テーブルの抽出
def extract_table(table):
    """Extract table data as list of dictionaries."""
    headers = [th.get_text(strip=True) for th in table.select('th')]

    rows = []
    for tr in table.select('tbody tr'):
        cells = [td.get_text(strip=True) for td in tr.select('td')]
        if cells:
            rows.append(dict(zip(headers, cells)))

    return rows
リストの抽出
def extract_items(soup, selector, extractor):
    """Extract multiple items using a custom extractor function."""
    return [extractor(item) for item in soup.select(selector)]

# Usage
def extract_product(item):
    return {
        'name': safe_text(item, '.name'),
        'price': safe_text(item, '.price'),
        'url': safe_attr(item, 'a', 'href')
    }

products = extract_items(soup, '.product', extract_product)

URLの解決

from urllib.parse import urljoin

def resolve_url(base_url, relative_url):
    """Convert relative URL to absolute."""
    if not relative_url:
        return None
    return urljoin(base_url, relative_url)

# Usage
base_url = 'https://example.com/products/'
for link in soup.select('a'):
    href = link.get('href')
    absolute_url = resolve_url(base_url, href)
    print(absolute_url)

不正な形式のHTMLへの対処

# lxml parser is lenient with malformed HTML
soup = BeautifulSoup(malformed_html, 'lxml')

# For very broken HTML, use html5lib
soup = BeautifulSoup(very_broken_html, 'html5lib')

# Handle encoding issues
response = requests.get(url)
response.encoding = response.apparent_encoding
soup = BeautifulSoup(response.text, 'lxml')

スクレイピングの完全な例

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import time

class ProductScraper:
    def __init__(self, base_url):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (compatible; MyScraper/1.0)'
        })

    def fetch_page(self, url):
        """Fetch and parse a page."""
        response = self.session.get(url, timeout=30)
        response.raise_for_status()
        return BeautifulSoup(response.content, 'lxml')

    def extract_product(self, item):
        """Extract product data from a card element."""
        return {
            'name': self._safe_text(item, '.product-title'),
            'price': self._parse_price(item.select_one('.price')),
            'rating': self._safe_attr(item, '.rating', 'data-rating'),
            'image': self._resolve(self._safe_attr(item, 'img', 'src')),
            'url': self._resolve(self._safe_attr(item, 'a', 'href')),
            'in_stock': not item.select_one('.out-of-stock')
        }

    def scrape_products(self, url):
        """Scrape all products from a page."""
        soup = self.fetch_page(url)
        items = soup.select('.product-card')
        return [self.extract_product(item) for item in items]

    def _safe_text(self, element, selector, default=''):
        found = element.select_one(selector)
        return found.get_text(strip=True) if found else default

    def _safe_attr(self, element, selector, attr, default=None):
        found = element.select_one(selector)
        return found.get(attr, default) if found else default

    def _parse_price(self, element):
        if not element:
            return None
        text = element.get_text(strip=True)
        try:
            return float(text.replace('$', '').replace(',', ''))
        except ValueError:
            return None

    def _resolve(self, url):
        return urljoin(self.base_url, url) if url else None


# Usage
scraper = ProductScraper('https://example.com')
products = scraper.scrape_products('https://example.com/products')
for product in products:
    print(product)

パフォーマンスの最適化

# Use SoupStrainer to parse only needed elements
from bs4 import SoupStrainer

only_articles = SoupStrainer('article')
soup = BeautifulSoup(html, 'lxml', parse_only=only_articles)

# Use lxml parser for speed
soup = BeautifulSoup(html, 'lxml')  # Fastest

# Decompose unneeded elements
for script in soup.find_all('script'):
    script.decompose()

# Use generators for memory efficiency
for item in soup.select('.item'):
    yield extract_data(item)

主な依存関係

  • beautifulsoup4
  • lxml(高速なパーサー)
  • html5lib(寛容なパーサー)
  • requests
  • pandas(データ出力用)

ベストプラクティス

  1. パフォーマンスを最大限に高めるため、常にlxmlパーサーを使用します
  2. 要素が見つからない場合は、デフォルト値を使って処理します
  3. CSSセレクターにはselect()とselect_one()を使用します
  4. テキストをきれいに抽出するにはget_text(strip=True)を使用します
  5. 相対URLを絶対URLに解決します
  6. 抽出したデータの型を検証します
  7. リクエスト間にレート制限を設けます
  8. 適切なUser-Agentヘッダーを使用します
  9. 文字エンコーディングを適切に処理します
  10. 大きなドキュメントにはSoupStrainerを使用します
  11. robots.txtとウェブサイトの利用規約に従います
  12. リクエストに失敗した場合のリトライ処理を実装します

コメント