8d382e723f
- Added Walmart scraper to scrape product data from Walmart.com, including category pages and product details. - Introduced a stealth browser module to handle bot protection and improve scraping reliability. - Created a SQLite database for tracking product history, price changes, stock events, and user favorites. - Developed a Discord bot for user interaction, allowing location setting and stock checking at local stores. - Implemented a favorites system to manage priority products and categories with custom notification settings. - Added news aggregation module to fetch and analyze Pokemon TCG news from various sources. - Created tools for API discovery and monitoring, including a backend monitor for detecting new products. - Added unit tests for database operations, product filtering, and API endpoints to ensure functionality. - Enhanced existing modules with improved error handling and logging for better maintainability.
268 lines
7.8 KiB
Python
268 lines
7.8 KiB
Python
"""
|
|
API Discovery Tool for Pokemon Center
|
|
Attempts to find and test API endpoints that could be used instead of browser scraping.
|
|
"""
|
|
|
|
import requests
|
|
import json
|
|
from urllib.parse import urljoin
|
|
|
|
# Common API patterns to try
|
|
BASE_URL = "https://www.pokemoncenter.com"
|
|
|
|
# Headers to mimic a real browser
|
|
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",
|
|
"Accept": "application/json, text/plain, */*",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
"Referer": "https://www.pokemoncenter.com/",
|
|
"Origin": "https://www.pokemoncenter.com",
|
|
}
|
|
|
|
# Common API endpoint patterns for e-commerce sites
|
|
API_PATTERNS = [
|
|
# REST API patterns
|
|
"/api/products",
|
|
"/api/v1/products",
|
|
"/api/v2/products",
|
|
"/api/catalog/products",
|
|
"/api/search",
|
|
"/api/inventory",
|
|
|
|
# GraphQL
|
|
"/graphql",
|
|
"/api/graphql",
|
|
|
|
# Common e-commerce platforms
|
|
"/rest/V1/products", # Magento
|
|
"/_api/products", # Wix
|
|
"/cdn/shop/products.json", # Shopify pattern
|
|
"/products.json", # Shopify
|
|
|
|
# Search APIs
|
|
"/api/search/products",
|
|
"/search/suggest",
|
|
"/api/autocomplete",
|
|
|
|
# Algolia (very common for e-commerce search)
|
|
# Note: Algolia requires app ID and API key from the page
|
|
]
|
|
|
|
# Pokemon TCG specific search terms
|
|
SEARCH_TERMS = ["pokemon", "tcg", "cards", "booster", "etb"]
|
|
|
|
|
|
def test_endpoint(url: str, method: str = "GET", data: dict = None) -> dict:
|
|
"""Test an API endpoint"""
|
|
try:
|
|
if method == "GET":
|
|
response = requests.get(url, headers=HEADERS, timeout=10)
|
|
else:
|
|
response = requests.post(url, headers=HEADERS, json=data, timeout=10)
|
|
|
|
return {
|
|
"url": url,
|
|
"status": response.status_code,
|
|
"content_type": response.headers.get("content-type", ""),
|
|
"size": len(response.content),
|
|
"sample": response.text[:500] if response.status_code == 200 else None
|
|
}
|
|
except Exception as e:
|
|
return {
|
|
"url": url,
|
|
"status": "error",
|
|
"error": str(e)
|
|
}
|
|
|
|
|
|
def discover_apis():
|
|
"""Attempt to discover API endpoints"""
|
|
print("=" * 60)
|
|
print("Pokemon Center API Discovery")
|
|
print("=" * 60)
|
|
print()
|
|
|
|
results = []
|
|
|
|
# Test common patterns
|
|
print("Testing common API patterns...")
|
|
for pattern in API_PATTERNS:
|
|
url = urljoin(BASE_URL, pattern)
|
|
result = test_endpoint(url)
|
|
results.append(result)
|
|
|
|
if result["status"] == 200:
|
|
print(f" [OK] {url}")
|
|
print(f" Content-Type: {result['content_type']}")
|
|
print(f" Size: {result['size']} bytes")
|
|
elif result["status"] != "error" and result["status"] < 500:
|
|
print(f" [{result['status']}] {url}")
|
|
|
|
# Test search with query params
|
|
print()
|
|
print("Testing search endpoints...")
|
|
search_patterns = [
|
|
"/api/search?q=pokemon",
|
|
"/api/products?search=tcg",
|
|
"/api/catalog?category=tcg-cards",
|
|
"/search?q=pokemon+tcg",
|
|
]
|
|
|
|
for pattern in search_patterns:
|
|
url = urljoin(BASE_URL, pattern)
|
|
result = test_endpoint(url)
|
|
results.append(result)
|
|
|
|
if result["status"] == 200:
|
|
print(f" [OK] {url}")
|
|
|
|
# Look for Algolia configuration
|
|
print()
|
|
print("Checking for Algolia search...")
|
|
# Algolia is often exposed in page source
|
|
try:
|
|
response = requests.get(BASE_URL, headers=HEADERS, timeout=15)
|
|
if "algolia" in response.text.lower():
|
|
print(" [!] Algolia detected in page source")
|
|
# Try to extract app ID and search key
|
|
import re
|
|
app_id = re.search(r'["\']?algolia[_-]?app[_-]?id["\']?\s*[:=]\s*["\']([A-Z0-9]+)["\']', response.text, re.I)
|
|
api_key = re.search(r'["\']?algolia[_-]?(?:search[_-]?)?(?:api[_-]?)?key["\']?\s*[:=]\s*["\']([a-f0-9]+)["\']', response.text, re.I)
|
|
|
|
if app_id:
|
|
print(f" App ID: {app_id.group(1)}")
|
|
if api_key:
|
|
print(f" Search Key: {api_key.group(1)}")
|
|
|
|
# Check for other API clues
|
|
if "graphql" in response.text.lower():
|
|
print(" [!] GraphQL detected in page source")
|
|
|
|
if "__NEXT_DATA__" in response.text:
|
|
print(" [!] Next.js detected - may have API routes at /api/*")
|
|
|
|
if "window.__INITIAL_STATE__" in response.text or "window.__PRELOADED_STATE__" in response.text:
|
|
print(" [!] Pre-rendered state detected - data may be in page source")
|
|
|
|
except Exception as e:
|
|
print(f" Error checking main page: {e}")
|
|
|
|
# Summary
|
|
print()
|
|
print("=" * 60)
|
|
print("Summary")
|
|
print("=" * 60)
|
|
|
|
working = [r for r in results if r.get("status") == 200]
|
|
if working:
|
|
print(f"Found {len(working)} potentially working endpoints:")
|
|
for r in working:
|
|
print(f" - {r['url']}")
|
|
else:
|
|
print("No direct API endpoints found.")
|
|
print()
|
|
print("Alternative approaches to consider:")
|
|
print(" 1. Monitor sitemap.xml for new products")
|
|
print(" 2. Use Google Shopping API or similar aggregators")
|
|
print(" 3. Check if they have an RSS feed")
|
|
print(" 4. Use a service like Distill.io for change detection")
|
|
print(" 5. Proxy rotation with residential IPs")
|
|
print(" 6. Lower check frequency + add human-like delays")
|
|
|
|
return results
|
|
|
|
|
|
def check_sitemap():
|
|
"""Check sitemap for product URLs"""
|
|
print()
|
|
print("Checking sitemap...")
|
|
|
|
sitemap_urls = [
|
|
"/sitemap.xml",
|
|
"/sitemap_index.xml",
|
|
"/sitemaps/sitemap.xml",
|
|
"/robots.txt", # Often contains sitemap location
|
|
]
|
|
|
|
for pattern in sitemap_urls:
|
|
url = urljoin(BASE_URL, pattern)
|
|
result = test_endpoint(url)
|
|
|
|
if result["status"] == 200:
|
|
print(f" [OK] {url}")
|
|
if "sitemap" in result.get("sample", "").lower():
|
|
print(f" Contains sitemap references")
|
|
if "product" in result.get("sample", "").lower():
|
|
print(f" Contains product references")
|
|
|
|
|
|
def check_rss():
|
|
"""Check for RSS feeds"""
|
|
print()
|
|
print("Checking for RSS/Atom feeds...")
|
|
|
|
feed_urls = [
|
|
"/feed",
|
|
"/rss",
|
|
"/feed.xml",
|
|
"/rss.xml",
|
|
"/atom.xml",
|
|
"/blog/feed",
|
|
"/news/feed",
|
|
]
|
|
|
|
for pattern in feed_urls:
|
|
url = urljoin(BASE_URL, pattern)
|
|
result = test_endpoint(url)
|
|
|
|
if result["status"] == 200 and ("xml" in result.get("content_type", "") or "rss" in result.get("content_type", "")):
|
|
print(f" [OK] {url}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
discover_apis()
|
|
check_sitemap()
|
|
check_rss()
|
|
|
|
print()
|
|
print("=" * 60)
|
|
print("Next Steps")
|
|
print("=" * 60)
|
|
print("""
|
|
To avoid bot detection, consider these strategies:
|
|
|
|
1. API-BASED MONITORING (if endpoints found):
|
|
- Call API endpoints directly with requests
|
|
- Much faster and less detectable than browser
|
|
- Can check more frequently
|
|
|
|
2. SITEMAP MONITORING:
|
|
- Parse sitemap.xml periodically
|
|
- Detect new product URLs without visiting pages
|
|
- Very low detection risk
|
|
|
|
3. HASH-BASED CHANGE DETECTION:
|
|
- Fetch page, hash content
|
|
- Only alert when hash changes
|
|
- Reduces unnecessary processing
|
|
|
|
4. RESIDENTIAL PROXY ROTATION:
|
|
- Use services like Bright Data, Oxylabs
|
|
- Rotate IPs to avoid blocks
|
|
- More expensive but reliable
|
|
|
|
5. HUMAN-LIKE BEHAVIOR:
|
|
- Random delays between 60-180 seconds
|
|
- Vary user agent strings
|
|
- Add mouse movements and scrolling
|
|
- Use real browser cookies
|
|
|
|
6. THIRD-PARTY ALERTS:
|
|
- Discord servers that track Pokemon Center
|
|
- Stock alert services (NowInStock, etc.)
|
|
- Browser extensions like Distill.io
|
|
|
|
Run this script to see what APIs are available:
|
|
python api_discovery.py
|
|
""")
|