{"product_id":"tom-tắt-trang-web-với-claude-haiku-nhanh-va-rẻ","title":"Tóm tắt trang web với Claude Haiku — Nhanh và rẻ","description":"\n\u003cp\u003eBạn cần đọc 50 bài báo mỗi ngày để theo dõi tin tức ngành? Hay cần extract thông tin từ hàng trăm trang web? Claude Haiku — model nhanh nhất và rẻ nhất của Anthropic — hoàn hảo cho tác vụ này. Chi phí chỉ khoảng \u003cstrong\u003e$0.001 mỗi bài viết\u003c\/strong\u003e.\u003c\/p\u003e\n\n\u003ch2\u003eTech Stack\u003c\/h2\u003e\n\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003erequests:\u003c\/strong\u003e Fetch HTML từ URL\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eBeautifulSoup:\u003c\/strong\u003e Parse và clean HTML\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eClaude Haiku:\u003c\/strong\u003e Tóm tắt content (nhanh, rẻ)\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003cpre\u003e\u003ccode\u003epip install anthropic requests beautifulsoup4 lxml\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eBước 1: Fetch và clean HTML\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport requests\nfrom bs4 import BeautifulSoup\nimport re\n\ndef fetch_webpage(url: str) -\u0026gt; str:\n    \"\"\"\n    Fetch HTML từ URL và trả về clean text.\n    \"\"\"\n    headers = {\n        \"User-Agent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36\",\n        \"Accept\": \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8\",\n        \"Accept-Language\": \"vi-VN,vi;q=0.9,en;q=0.8\",\n    }\n\n    try:\n        response = requests.get(url, headers=headers, timeout=10)\n        response.raise_for_status()\n        response.encoding = response.apparent_encoding\n        return response.text\n    except requests.RequestException as e:\n        raise ValueError(f\"Không thể fetch URL: {e}\")\n\ndef clean_html(html: str) -\u0026gt; str:\n    \"\"\"\n    Extract text từ HTML, loại bỏ noise (ads, nav, footer).\n    \"\"\"\n    soup = BeautifulSoup(html, \"lxml\")\n\n    # Xóa các elements không cần thiết\n    for tag in soup.find_all([\"script\", \"style\", \"nav\", \"header\", \"footer\",\n                               \"aside\", \"advertisement\", \"iframe\"]):\n        tag.decompose()\n\n    # Cũng xóa theo class\/id phổ biến của ads\n    for tag in soup.find_all(class_=re.compile(r\"ad|banner|sidebar|popup|cookie\",\n                                                re.IGNORECASE)):\n        tag.decompose()\n\n    # Thử tìm main content area\n    main_content = (\n        soup.find(\"article\") or\n        soup.find(\"main\") or\n        soup.find(class_=re.compile(r\"content|article|post|entry\", re.IGNORECASE)) or\n        soup.find(\"body\")\n    )\n\n    if main_content:\n        text = main_content.get_text(separator=\"\n\", strip=True)\n    else:\n        text = soup.get_text(separator=\"\n\", strip=True)\n\n    # Clean whitespace\n    lines = [line.strip() for line in text.splitlines() if line.strip()]\n    text = \"\n\".join(lines)\n\n    # Giới hạn độ dài (tránh vượt context window)\n    max_chars = 50000  # Khoảng 12.5k tokens\n    if len(text) \u0026gt; max_chars:\n        text = text[:max_chars] + \"\n\n[Nội dung bị cắt bớt do quá dài...]\"\n\n    return text\n\n# Test\nhtml = fetch_webpage(\"https:\/\/vnexpress.net\/\")\ncontent = clean_html(html)\nprint(f\"Extracted {len(content)} characters\")\nprint(content[:500])\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eBước 2: Summarize với Claude Haiku\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport anthropic\n\nclient = anthropic.Anthropic()\n\nSUMMARIZE_SYSTEM_PROMPT = \"\"\"Bạn là trợ lý tóm tắt nội dung web.\nTóm tắt ngắn gọn, súc tích, và chính xác bằng tiếng Việt.\nTập trung vào thông tin quan trọng nhất.\"\"\"\n\ndef summarize_webpage(url: str, summary_style: str = \"brief\") -\u0026gt; dict:\n    \"\"\"\n    Tóm tắt nội dung một trang web.\n\n    summary_style:\n    - \"brief\": Tóm tắt 2-3 câu\n    - \"bullet\": Các điểm chính dạng bullet\n    - \"detailed\": Tóm tắt chi tiết 200-300 từ\n    - \"tldr\": Chỉ 1 câu\n    \"\"\"\n    # Fetch content\n    html = fetch_webpage(url)\n    content = clean_html(html)\n\n    prompts = {\n        \"brief\": \"Tóm tắt nội dung trang web này trong 2-3 câu ngắn gọn:\",\n        \"bullet\": \"Liệt kê 5 điểm chính nhất từ trang web này:\",\n        \"detailed\": \"Viết tóm tắt chi tiết (200-300 từ) về nội dung trang web này:\",\n        \"tldr\": \"Tóm tắt toàn bộ nội dung trong ĐÚNG 1 câu:\",\n    }\n\n    prompt = prompts.get(summary_style, prompts[\"brief\"])\n\n    response = client.messages.create(\n        model=\"claude-haiku-4-5\",\n        max_tokens=500,\n        system=SUMMARIZE_SYSTEM_PROMPT,\n        messages=[{\n            \"role\": \"user\",\n            \"content\": f\"{prompt}\n\nNội dung:\n{content}\"\n        }],\n        temperature=0.3,\n    )\n\n    return {\n        \"url\": url,\n        \"style\": summary_style,\n        \"summary\": response.content[0].text,\n        \"content_length\": len(content),\n        \"tokens_used\": response.usage.input_tokens + response.usage.output_tokens,\n    }\n\n# Test\nresult = summarize_webpage(\"https:\/\/techcrunch.com\/\", \"bullet\")\nprint(f\"URL: {result['url']}\")\nprint(f\"Tokens used: {result['tokens_used']}\")\nprint(f\"\nTóm tắt:\n{result['summary']}\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eBatch summarizer — Xử lý nhiều URLs\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003efrom concurrent.futures import ThreadPoolExecutor, as_completed\nimport time\n\ndef batch_summarize(urls: list, style: str = \"brief\", max_workers: int = 5) -\u0026gt; list:\n    \"\"\"Tóm tắt nhiều URLs song song.\"\"\"\n    results = []\n    errors = []\n\n    def process_url(url):\n        try:\n            time.sleep(0.5)  # Rate limiting nhẹ\n            return summarize_webpage(url, style)\n        except Exception as e:\n            return {\"url\": url, \"error\": str(e), \"summary\": None}\n\n    print(f\"Đang tóm tắt {len(urls)} URLs với {max_workers} workers...\")\n\n    with ThreadPoolExecutor(max_workers=max_workers) as executor:\n        future_to_url = {executor.submit(process_url, url): url for url in urls}\n\n        for future in as_completed(future_to_url):\n            result = future.result()\n            if result.get(\"error\"):\n                errors.append(result)\n                print(f\"  ERROR: {result['url'][:50]}... - {result['error'][:50]}\")\n            else:\n                results.append(result)\n                print(f\"  OK: {result['url'][:50]}... ({result['tokens_used']} tokens)\")\n\n    print(f\"\nHoàn thành: {len(results)}\/{len(urls)} thành công\")\n    return results, errors\n\n# News aggregator ví dụ\ntech_news_urls = [\n    \"https:\/\/techcrunch.com\/\",\n    \"https:\/\/theverge.com\/\",\n    \"https:\/\/wired.com\/\",\n    \"https:\/\/arstechnica.com\/\",\n]\n\nresults, errors = batch_summarize(tech_news_urls, style=\"bullet\")\n\nfor r in results:\n    print(f\"\n=== {r['url']} ===\")\n    print(r[\"summary\"])\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eNews Monitor với topic filtering\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eTOPIC_FILTER_PROMPT = \"\"\"Đọc tóm tắt tin tức sau và đánh giá mức độ liên quan đến chủ đề: \"{topic}\"\n\nTóm tắt: {summary}\n\nTrả về:\n- relevance_score: 0-10 (10 = rất liên quan)\n- is_relevant: true\/false (relevant nếu score \u0026gt;= 6)\n- reason: lý do ngắn gọn\n\nChỉ trả về JSON.\"\"\"\n\nimport json\n\ndef filter_by_topic(summaries: list, topic: str) -\u0026gt; list:\n    \"\"\"Lọc các bài viết theo chủ đề.\"\"\"\n    relevant = []\n\n    for item in summaries:\n        if not item.get(\"summary\"):\n            continue\n\n        prompt = TOPIC_FILTER_PROMPT.format(\n            topic=topic,\n            summary=item[\"summary\"]\n        )\n\n        response = client.messages.create(\n            model=\"claude-haiku-4-5\",\n            max_tokens=150,\n            messages=[\n                {\"role\": \"user\", \"content\": prompt},\n                {\"role\": \"assistant\", \"content\": \"{\"}\n            ],\n            temperature=0.0,\n        )\n\n        try:\n            data = json.loads(\"{\" + response.content[0].text)\n            if data.get(\"is_relevant\", False):\n                item[\"relevance_score\"] = data.get(\"relevance_score\", 0)\n                item[\"relevance_reason\"] = data.get(\"reason\", \"\")\n                relevant.append(item)\n        except json.JSONDecodeError:\n            pass\n\n    # Sắp xếp theo relevance\n    relevant.sort(key=lambda x: x.get(\"relevance_score\", 0), reverse=True)\n    return relevant\n\n# Lọc bài về AI\nai_articles = filter_by_topic(results, \"artificial intelligence và machine learning\")\nprint(f\"Tìm thấy {len(ai_articles)} bài liên quan đến AI\")\nfor art in ai_articles:\n    print(f\"\n[{art['relevance_score']}\/10] {art['url']}\")\n    print(f\"Reason: {art['relevance_reason']}\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eỨng dụng thực tế: Daily Digest\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003efrom datetime import datetime\n\ndef generate_daily_digest(rss_urls: list, topic: str = None) -\u0026gt; str:\n    \"\"\"\n    Tạo daily digest từ danh sách URLs.\n    \"\"\"\n    print(f\"Generating daily digest for {len(rss_urls)} sources...\")\n\n    # Tóm tắt tất cả\n    summaries, _ = batch_summarize(rss_urls, style=\"bullet\")\n\n    # Filter theo topic nếu có\n    if topic:\n        summaries = filter_by_topic(summaries, topic)\n\n    # Tạo digest\n    today = datetime.now().strftime(\"%d\/%m\/%Y\")\n    digest = f\"# Daily Digest - {today}\n\n\"\n\n    if topic:\n        digest += f\"**Chủ đề:** {topic}\n\n\"\n\n    digest += f\"**Tổng cộng:** {len(summaries)} nguồn\n\n---\n\n\"\n\n    for i, item in enumerate(summaries, 1):\n        digest += f\"## {i}. [{item['url']}]({item['url']})\n\n\"\n        digest += f\"{item['summary']}\n\n\"\n        if item.get(\"relevance_score\"):\n            digest += f\"*Relevance: {item['relevance_score']}\/10*\n\n\"\n        digest += \"---\n\n\"\n\n    # Tóm tắt tổng thể\n    all_summaries = \"\n\n\".join([s[\"summary\"] for s in summaries])\n    overall_response = client.messages.create(\n        model=\"claude-haiku-4-5\",\n        max_tokens=300,\n        messages=[{\n            \"role\": \"user\",\n            \"content\": f\"Từ các tóm tắt tin tức sau, viết 3 xu hướng chính nhất hôm nay:\n\n{all_summaries[:3000]}\"\n        }],\n    )\n\n    digest = \"## Xu hướng hôm nay\n\n\" + overall_response.content[0].text + \"\n\n---\n\n\" + digest\n\n    return digest\n\n# Tạo digest\ndigest = generate_daily_digest(\n    rss_urls=tech_news_urls,\n    topic=\"AI và automation\"\n)\n\n# Lưu thành file\nwith open(f\"digest_{datetime.now().strftime('%Y%m%d')}.md\", \"w\", encoding=\"utf-8\") as f:\n    f.write(digest)\nprint(f\"Digest saved!\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eChi phí ước tính\u003c\/h2\u003e\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n\u003cth\u003eQuy mô\u003c\/th\u003e\n\u003cth\u003eTokens\/bài\u003c\/th\u003e\n\u003cth\u003eChi phí\/bài (Haiku)\u003c\/th\u003e\n\u003cth\u003eChi phí 1000 bài\u003c\/th\u003e\n\u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n\u003ctd\u003eTin ngắn (~500 chữ)\u003c\/td\u003e\n\u003ctd\u003e~800\u003c\/td\u003e\n\u003ctd\u003e$0.0002\u003c\/td\u003e\n\u003ctd\u003e$0.20\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eBài dài (~2000 chữ)\u003c\/td\u003e\n\u003ctd\u003e~3000\u003c\/td\u003e\n\u003ctd\u003e$0.0008\u003c\/td\u003e\n\u003ctd\u003e$0.80\u003c\/td\u003e\n\u003c\/tr\u003e\n    \u003ctr\u003e\n\u003ctd\u003eBài rất dài (~5000 chữ)\u003c\/td\u003e\n\u003ctd\u003e~7000\u003c\/td\u003e\n\u003ctd\u003e$0.0018\u003c\/td\u003e\n\u003ctd\u003e$1.80\u003c\/td\u003e\n\u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003cp\u003eClaude Haiku là lựa chọn tối ưu cho web summarization — đủ thông minh để tóm tắt tốt, đủ nhanh để batch nhiều URLs, và đủ rẻ để chạy production scale. Kết hợp với \u003ca href=\"\/collections\/nang-cao\"\u003eBatch Processing API\u003c\/a\u003e để tối ưu hóa throughput thêm nữa.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47721833070804,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/tom-t_t-trang-web-v_i-claude-haiku-nhanh-va-r.jpg?v=1774521732","url":"https:\/\/claude.vn\/products\/tom-t%e1%ba%aft-trang-web-v%e1%bb%9bi-claude-haiku-nhanh-va-r%e1%ba%bb","provider":"CLAUDE.VN","version":"1.0","type":"link"}