beautifulsoup4 — правило для Cursor: что делает и как установить
Задаёт стандарты парсинга HTML на Python: выбор lxml, работа с CSS-селекторами и валидация структур через Pydantic.
.cursor/rules/beautifulsoup4.mdcЧто делает beautifulsoup4
Правило инструктирует ИИ писать чистый код скрейпинга в файлах **/*.py. Оно требует передавать lxml парсером в конструктор BeautifulSoup ради скорости, а не полагаться на медленный стандартный html.parser. Вместо устаревшего метода find_all ассистент переходит на CSS-селекторы через .select() и .select_one().
Логика сбора данных делится на функции с тайп-хинтами, а извлечённые поля упаковываются в модели pydantic или датаклассы. Также правило предписывает очищать текст методом .get_text(strip=True) и удалять ненужные блоки вызовом .decompose(). Beautiful Soup не выполняет JavaScript. Поэтому для динамических страниц правило требует сначала отрендерить разметку через Playwright или Selenium.
Какие задачи решает
- Ускорить парсинг страниц через принудительное указание lxml
- Структурировать извлечённые сущности в модели Pydantic
- Заменить цепочки find_all на точные CSS-селекторы
- Настроить этичный скрейпинг с User-Agent и паузами между запросами
Примеры запросов агенту
- Напиши функцию парсинга каталога товаров с пагинацией через requests и BeautifulSoup.
- Спарси таблицу цен со страницы и сохрани результат в модель Pydantic.
- Очисти HTML-разметку статьи от рекламы и извлеки заголовок с текстом.
Как установить beautifulsoup4
Вручную: положить файлы
.cursor/rules/beautifulsoup4.mdcВо frontmatter: description, globs, alwaysApply
Вручную: положить файлы
.claude/rules/beautifulsoup4.mdПоле paths во frontmatter ограничивает файлы: globs из источника → paths
AGENTS.md
Codex читает AGENTS.md: вставьте текст правила туда
Вручную: положить файлы
.github/instructions/beautifulsoup4.instructions.mdПоле applyTo задаёт, к каким файлам применять: globs из источника → applyTo
Вручную: положить файлы
.devin/rules/beautifulsoup4.mdВручную: положить файлы
.clinerules/beautifulsoup4.mdПо шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules
Вопросы о beautifulsoup4
Как установить beautifulsoup4 в Cursor?
Создайте файл .cursor/rules/beautifulsoup4.mdc в корне проекта и вставьте туда содержимое правила. Настройки применятся ко всем файлам с маской **/*.py.
Что делает правило beautifulsoup4?
Оно направляет редактор на написание типизированного кода парсинга на Python. Ассистент начинает выбирать быстрый парсер lxml и использовать точные CSS-селекторы.
Поможет ли это правило распарсить страницу на React или Vue?
Beautiful Soup разбирает только статическую разметку. Для сайтов с клиентским JavaScript правило предписывает сначала отрендерить DOM в Selenium или Playwright, а уже полученный HTML передавать парсеру.
Исходный текст
beautifulsoup4.mdc · CC0-1.0
---
description: This guide provides opinionated, actionable, and example-driven best practices for using beautifulsoup4 in Python web scraping projects, focusing on modern techniques and common pitfalls.
globs: **/*.py
---
# beautifulsoup4 Best Practices
Beautiful Soup 4 (BS4) is the definitive library for parsing static HTML/XML. Use it as a robust component within a well-structured scraping pipeline.
## 1. Installation & Parser Selection
Always install `beautifulsoup4` alongside `lxml` for optimal performance. `lxml` is significantly faster than Python's built-in `html.parser`. `html5lib` is a useful fallback for extremely malformed HTML.
```bash
pip install beautifulsoup4 lxml requests html5lib
```
**✅ GOOD: Explicitly specify `lxml` as the parser.**
```python
from bs4 import BeautifulSoup
# Always use lxml for speed
soup = BeautifulSoup(html_content, 'lxml')
```
**❌ BAD: Relying on default `html.parser` or `html5lib` unnecessarily.**
```python
# Slower, less efficient
soup = BeautifulSoup(html_content, 'html.parser')
# Only use html5lib if lxml fails on malformed HTML
soup = BeautifulSoup(html_content, 'html5lib')
```
## 2. Ethical Scraping Fundamentals
Respect `robots.txt`, implement rate limiting, and use a `User-Agent`. This is non-negotiable for responsible scraping.
**✅ GOOD: Respect `robots.txt` and use a `User-Agent` with delays.**
```python
import requests
import time
from typing import Dict
def fetch_page_ethically(url: str, headers: Dict[str, str], delay_seconds: float = 2.0) -> str:
"""Fetches HTML content with ethical considerations."""
# In a real app, check robots.txt programmatically
# e.g., using 'robotexclusionrulesparser' library
print(f"Fetching {url}...")
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
time.sleep(delay_seconds) # Rate limiting
return response.text
# Example usage
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 MyScraper/1.0'
}
# html_content = fetch_page_ethically("https://example.com/products", HEADERS)
```
**❌ BAD: Blindly hitting endpoints without identification or delays.**
```python
# Will likely get blocked or cause issues
response = requests.get("https://example.com/products")
html_content = response.text
```
## 3. Code Organization & Type Hinting
Structure your scraping logic into small, testable, type-hinted functions. Separate concerns: fetching, parsing, and data modeling. Use `pydantic` or `dataclasses` for structured output.
**✅ GOOD: Modular, type-hinted functions with structured data output.**
```python
from bs4 import BeautifulSoup, Tag
from pydantic import BaseModel
from typing import List, Optional
class Product(BaseModel):
name: str
price: float
url: str
in_stock: bool
def parse_product_card(card_tag: Tag) -> Optional[Product]:
"""Parses a single product card HTML tag into a Product model."""
name_tag = card_tag.select_one('h4.product-name')
price_tag = card_tag.select_one('.price-wrapper')
link_tag = card_tag.select_one('a.product-link')
stock_tag = card_tag.select_one('.stock-status')
if not all([name_tag, price_tag, link_tag]):
return None # Skip malformed cards
try:
name = name_tag.get_text(strip=True)
price = float(price_tag.get_text(strip=True).replace('$', ''))
url = link_tag['href']
in_stock = "In Stock" in stock_tag.get_text(strip=True) if stock_tag else False
return Product(name=name, price=price, url=url, in_stock=in_stock)
except (AttributeError, ValueError, KeyError) as e:
print(f"Error parsing product card: {e}")
return None
def extract_products_from_html(html_content: str) -> List[Product]:
"""Extracts all products from a given HTML page."""
soup = BeautifulSoup(html_content, 'lxml')
product_cards = soup.select('.product-card') # Use CSS selectors
products = [
product for card in product_cards
if (product := parse_product_card(card)) is not None
]
return products
# Example: products = extract_products_from_html(html_content)
```
**❌ BAD: Monolithic functions, untyped code, unstructured output.**
```python
def scrape_products(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')
products = []
for card in soup.find_all('div', class_='product-card'): # Less readable
name = card.find('h4').text
price = float(card.find('span', class_='price').text.replace('$', ''))
# ... lots of inline logic ...
products.append({'name': name, 'price': price})
return products
```
## 4. Parsing Efficiency: CSS Selectors & Text Extraction
Prioritize CSS selectors (`.select()` or `.select_one()`) over `find_all()` for readability, power, and often better performance. Always strip whitespace from extracted text.
**✅ GOOD: CSS selectors and `get_text(strip=True)`.**
```python
# Find all links with a specific class and href attribute
links = soup.select('a.external-link[href^="http"]')
# Get the first paragraph's text, stripped of leading/trailing whitespace
first_paragraph_text = soup.select_one('div#content p').get_text(strip=True)
# Remove an unwanted element from the tree
unwanted_ad = soup.select_one('.ad-banner')
if unwanted_ad:
unwanted_ad.decompose() # Removes the tag and its contents
```
**❌ BAD: Over-reliance on `find_all` with complex dictionaries, raw text.**
```python
# Less readable and potentially less efficient
links = soup.find_all('a', class_='external-link', href=lambda h: h and h.startswith('http'))
# Text with extra newlines and spaces
first_paragraph_text = soup.find('div', id='content').find('p').text
```
## 5. Handling Dynamic Content
Beautiful Soup is for static HTML. If a page relies on JavaScript to render content, use `Selenium` or `Playwright` to render the page first, then pass the *rendered* HTML to Beautiful Soup. Do not try to make BS4 handle JavaScript.
**✅ GOOD: Use Selenium/Playwright for rendering, then BS4 for parsing.**
```python
# from selenium import webdriver
# from selenium.webdriver.chrome.service import Service
# from webdriver_manager.chrome import ChromeDriverManager
#
# driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
# driver.get("https://js-rendered-site.com")
# rendered_html = driver.page_source
# driver.quit()
#
# soup = BeautifulSoup(rendered_html, 'lxml')
# # ... proceed with BS4 parsing ...
```
**❌ BAD: Expecting BS4 to execute JavaScript.**
```python
# This will only parse the initial static HTML, not JS-rendered content
# soup = BeautifulSoup(requests.get("https://js-rendered-site.com").text, 'lxml')
```
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc