web-scraper — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited web-scraper (Agent Skill) and scored it 91/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} is the classic direct prompt-injection phrasing. Placed in a skill body that the agent reads as trusted instructions, it tries to make the agent abandon its prior rules and follow whatever comes next — a full system-prompt override.
ignore/disregard/forget … previous instructions sentence.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Extract structured data from websites responsibly. Choose the right tool based on whether the page is static HTML or requires JavaScript execution, then handle pagination, rate limiting, and data cleaning.
| Situation | Tool |
|---|---|
| Static HTML, simple pages | requests + beautifulsoup4 |
| JS-rendered content (SPAs, React/Vue) | playwright or selenium |
| Heavy scraping / crawling | scrapy |
| API available | Use the API - always prefer it |
Before scraping, check:
robots.txt at site.com/robots.txtIf an API exists, use it. It's faster, more reliable, and respectful.
import requests
from bs4 import BeautifulSoup
import time
headers = {
"User-Agent": "Mozilla/5.0 (compatible; research-bot/1.0; +https://example.com/bot)"
}
response = requests.get("https://example.com/products", headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
# Find elements by CSS selector
items = soup.select("div.product-card")
for item in items:
name = item.select_one("h2.product-name").get_text(strip=True)
price = item.select_one("span.price").get_text(strip=True)
link = item.select_one("a")["href"]
print(name, price, link)from playwright.sync_api import sync_playwright
import time
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com/spa-page", wait_until="networkidle")
# Wait for dynamic content
page.wait_for_selector("div.results")
# Extract data
items = page.query_selector_all("div.product-card")
for item in items:
name = item.query_selector("h2").inner_text()
price = item.query_selector("span.price").inner_text()
print(name, price)
browser.close()import requests
from bs4 import BeautifulSoup
results = []
page = 1
while True:
url = f"https://example.com/listings?page={page}"
response = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(response.text, "html.parser")
items = soup.select("div.item")
if not items:
break # No more results
for item in items:
results.append(item.get_text(strip=True))
next_btn = soup.select_one("a.next-page")
if not next_btn:
break
page += 1
time.sleep(1.5) # Respectful delay between requestsimport pandas as pd
import re
df = pd.DataFrame(results)
# Clean price strings
df["price"] = df["price"].str.replace(r"[^\d.]", "", regex=True).astype(float)
# Normalize URLs
df["url"] = df["url"].apply(lambda u: u if u.startswith("http") else f"https://example.com{u}")
df.to_csv("scraped_data.csv", index=False)time.sleep(1) minimum between requestsCrawl-delay in robots.txtUser-Agent with contact infoplaywright with a real browser profile; avoid headless detection signatures~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.