""" Amazon Competitor Analyzer Skill =============================== Scrapes Amazon product data from ASINs using BrowserAct API and performs surgical competitive analysis. Author: OpenCode Version: 1.0.0 """ import os import sys import json import time import csv import requests from datetime import datetime from typing import Dict, List, Optional, Any from pathlib import Path # Try to load .env file if python-dotenv is available try: from dotenv import load_dotenv # Load .env from same directory as script script_dir = Path(__file__).parent env_file = script_dir / ".env" if env_file.exists(): load_dotenv(env_file) except ImportError: # If python-dotenv not available, check .env manually script_dir = Path(__file__).parent env_file = script_dir / ".env" if env_file.exists(): with open(env_file) as f: for line in f: line = line.strip() if line and not line.startswith('#') and '=' in line: key, value = line.split('=', 1) os.environ.setdefault(key, value.strip()) # Configuration BROWSERACT_API_KEY = os.getenv("BROWSERACT_API_KEY", "") WORKFLOW_TEMPLATE_ID = "77814333389670716" API_BASE_URL = "https://api.browseract.com/v2/workflow" class AmazonCompetitorAnalyzer: """Main class for Amazon competitive analysis""" def __init__(self, api_key: str = None, workflow_template_id: str = None): """Initialize the analyzer with API credentials""" self.api_key = api_key or BROWSERACT_API_KEY self.workflow_template_id = workflow_template_id or WORKFLOW_TEMPLATE_ID self.headers = { "Authorization": f"Bearer {self.api_key}" } def validate_asin(self, asin: str) -> bool: """Validate ASIN format""" return len(asin) == 10 and asin.isalnum() def extract_asins_from_text(self, text: str) -> List[str]: """Extract ASINs from user input text""" import re # Match 10-character alphanumeric strings starting with B0 or similar asin_pattern = r'\b[B0][A-Z0-9]{9}\b' asins = re.findall(asin_pattern, text.upper()) return list(set(asins)) # Remove duplicates def submit_task(self, asin: str) -> Optional[str]: """Submit scraping task for a single ASIN""" if not self.validate_asin(asin): print(f"Invalid ASIN: {asin}") return None data = { "workflow_template_id": self.workflow_template_id, "input_parameters": [{"name": "ASIN", "value": asin}] } try: response = requests.post( f"{API_BASE_URL}/run-task-by-template", json=data, headers=self.headers, timeout=30 ) if response.status_code == 200: result = response.json() task_id = result.get("id") return task_id else: print(f"Failed to submit task for {asin}: {response.json().get('msg', 'Unknown error')}") return None except Exception as e: print(f"Error submitting task for {asin}: {e}") return None def wait_for_task(self, task_id: str, timeout: int = 300) -> bool: """Wait for task completion""" start_time = time.time() while time.time() - start_time < timeout: try: response = requests.get( f"{API_BASE_URL}/get-task-status?task_id={task_id}", headers=self.headers, timeout=10 ) if response.status_code == 200: status = response.json().get("status") if status == "finished": return True elif status in ["failed", "canceled"]: return False time.sleep(3) except Exception: time.sleep(5) return False def get_results(self, task_id: str) -> Optional[Dict]: """Get task results""" try: response = requests.get( f"{API_BASE_URL}/get-task?task_id={task_id}", headers=self.headers, timeout=30 ) if response.status_code == 200: return response.json() except Exception as e: print(f"Error getting results: {e}") return None def scrape_product(self, asin: str, wait_timeout: int = 300) -> Optional[Dict]: """Scrape a single product""" # Submit task task_id = self.submit_task(asin) if not task_id: return None # Wait for completion if not self.wait_for_task(task_id, wait_timeout): return None # Get results results = self.get_results(task_id) return results def scrape_multiple_products(self, asins: List[str], delay: int = 5) -> Dict[str, Any]: """Scrape multiple products""" results = {} for asin in asins: print(f"Processing: {asin}") data = self.scrape_product(asin) results[asin] = data if delay > 0 and asin != asins[-1]: time.sleep(delay) return results def analyze_competitive_position(self, products: Dict[str, Any]) -> Dict[str, Any]: """Analyze competitive positioning""" analysis = { "price_analysis": {}, "rating_analysis": {}, "market_leaders": {}, "opportunities": [] } prices = [] ratings = [] for asin, data in products.items(): if data: try: product = data.get('results', {}).get('products', [{}])[0] price = product.get('pricing', {}).get('current_price', 0) rating = product.get('reviews', {}).get('average_rating', 0) reviews = product.get('reviews', {}).get('total_count', 0) brand = product.get('product_info', {}).get('brand', asin) prices.append((asin, price, brand)) ratings.append((asin, rating, reviews, brand)) except Exception: pass # Sort by price prices.sort(key=lambda x: x[1]) if prices: analysis["price_analysis"]["lowest"] = prices[0] analysis["price_analysis"]["highest"] = prices[-1] analysis["price_analysis"]["range"] = prices[-1][1] - prices[0][1] # Sort by rating ratings.sort(key=lambda x: x[1], reverse=True) if ratings: analysis["rating_analysis"]["top_rated"] = ratings[0] analysis["rating_analysis"]["by_volume"] = sorted(ratings, key=lambda x: x[2], reverse=True) return analysis def generate_csv_report(self, products: Dict[str, Any], output_path: str): """Generate CSV report""" with open(output_path, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow([ 'ASIN', 'Product Title', 'Brand', 'Price ($)', 'Original Price ($)', 'Discount (%)', 'Rating', 'Reviews Count', 'Weight', 'Features' ]) for asin, data in products.items(): if data: try: product = data.get('results', {}).get('products', [{}])[0] writer.writerow([ asin, product.get('product_info', {}).get('title', 'N/A')[:100], product.get('product_info', {}).get('brand', 'N/A'), product.get('pricing', {}).get('current_price', 'N/A'), product.get('pricing', {}).get('original_price', 'N/A'), product.get('pricing', {}).get('discount_percent', 'N/A'), product.get('reviews', {}).get('average_rating', 'N/A'), product.get('reviews', {}).get('total_count', 'N/A'), product.get('specifications', {}).get('weight', 'N/A'), ', '.join(product.get('specifications', {}).get('features', [])[:5]) ]) except Exception: writer.writerow([asin, 'Error', '', '', '', '', '', '', '', '']) else: writer.writerow([asin, 'Failed', '', '', '', '', '', '', '', '']) print(f"CSV report saved: {output_path}") def generate_markdown_report(self, products: Dict[str, Any], output_path: str): """Generate comprehensive markdown report""" with open(output_path, 'w', encoding='utf-8') as f: f.write("# Amazon Competitive Analysis Report\n\n") f.write(f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n") f.write(f"**Products Analyzed:** {len(products)}\n\n") # Summary table f.write("## Data Summary\n\n") f.write("| ASIN | Brand | Price | Rating | Reviews |\n") f.write("|------|-------|-------|--------|---------|\n") for asin, data in products.items(): if data: try: product = data.get('results', {}).get('products', [{}])[0] info = product.get('product_info', {}) pricing = product.get('pricing', {}) reviews = product.get('reviews', {}) f.write(f"| {asin} | {info.get('brand', 'N/A')} | " f"${pricing.get('current_price', 'N/A')} | " f"{reviews.get('average_rating', 'N/A')}/5 | " f"{reviews.get('total_count', 'N/A'):,} |\n") except Exception: f.write(f"| {asin} | Error | - | - | - |\n") else: f.write(f"| {asin} | Failed | - | - | - |\n") # Detailed analysis f.write("\n## Detailed Analysis\n\n") competitive_analysis = self.analyze_competitive_position(products) f.write("### Price Positioning\n") if competitive_analysis.get("price_analysis"): pa = competitive_analysis["price_analysis"] if "lowest" in pa: f.write(f"- Lowest Price: {pa['lowest'][2]} at ${pa['lowest'][1]}\n") if "highest" in pa: f.write(f"- Highest Price: {pa['highest'][2]} at ${pa['highest'][1]}\n") f.write("\n### Rating Leaders\n") if competitive_analysis.get("rating_analysis"): ra = competitive_analysis["rating_analysis"] if "top_rated" in ra: f.write(f"- Highest Rated: {ra['top_rated'][3]} at {ra['top_rated'][1]}/5\n") f.write("\n---\n") f.write(f"*Generated by Amazon Competitor Analyzer*\n") print(f"Markdown report saved: {output_path}") def generate_json_report(self, products: Dict[str, Any], output_path: str): """Generate JSON report""" report_data = { "generated_at": datetime.now().isoformat(), "products_analyzed": len(products), "products": products, "analysis": self.analyze_competitive_position(products) } with open(output_path, 'w', encoding='utf-8') as f: json.dump(report_data, f, indent=2, ensure_ascii=False) print(f"JSON report saved: {output_path}") def analyze_asins(asins: List[str], output_dir: str = None) -> Dict[str, Any]: """Main function to analyze multiple ASINs""" analyzer = AmazonCompetitorAnalyzer() print(f"Analyzing {len(asins)} ASINs...") # Scrape products products = analyzer.scrape_multiple_products(asins) # Generate reports if output directory specified if output_dir: os.makedirs(output_dir, exist_ok=True) base_path = os.path.join(output_dir, "amazon_analysis") analyzer.generate_csv_report(products, f"{base_path}.csv") analyzer.generate_markdown_report(products, f"{base_path}.md") analyzer.generate_json_report(products, f"{base_path}.json") return products def main(): """CLI entry point""" import argparse parser = argparse.ArgumentParser(description="Amazon Competitor Analyzer") parser.add_argument("asins", nargs="+", help="ASINs to analyze") parser.add_argument("-o", "--output", default=".", help="Output directory") parser.add_argument("-k", "--api-key", help="BrowserAct API key") args = parser.parse_args() # Run analysis api_key = args.api_key or BROWSERACT_API_KEY analyzer = AmazonCompetitorAnalyzer(api_key=api_key) print(f"Analyzing {len(args.asins)} ASINs...") products = analyzer.scrape_multiple_products(args.asins) if args.output: os.makedirs(args.output, exist_ok=True) base_path = os.path.join(args.output, "amazon_analysis") analyzer.generate_csv_report(products, f"{base_path}.csv") analyzer.generate_markdown_report(products, f"{base_path}.md") analyzer.generate_json_report(products, f"{base_path}.json") print(f"\nAnalysis complete! Analyzed {len(args.asins)} products.") if __name__ == "__main__": main()