Cron + Claude API tự động viết blog mỗi ngày: setup từ A đến Z

Chia sẻ bài viết

Mục lục
TL;DR
  • Pipeline tự động viết blog: cron trigger script Python, gọi Claude API generate outline, viết bài 2000+ từ, gen ảnh cover, post lên WordPress REST API.
  • Chi phí ~0.3-0.5 USD/bài với Claude Sonnet 4.7, ~0.1 USD nếu dùng Claude Haiku 4.
  • Tốn VPS Cloud VPS 20 (199k/tháng) cho script chạy, không cần backend phức tạp.
  • Cấu trúc: keyword bank YAML, prompt template Jinja2, output Markdown, convert HTML Gutenberg, post API WordPress.
  • 1 năm có 365 bài SEO mới với chi phí ~50 USD AI + 2.4 triệu VPS, rẻ hơn thuê writer 10 lần.

Content marketing là chiến lược SEO tốt nhất nhưng tốn thời gian khủng khiếp. Mỗi bài 2000 từ chuẩn SEO mất 4-6 giờ research và viết. Thuê writer 200-500k/bài, năm 365 bài tốn 70-180 triệu. Claude API giải bài này: 1 bài chỉ tốn 0.3-0.5 USD, 1 năm khoảng 1.2-1.8 triệu, viết được trong 2 phút. Quality không thay được human expert, nhưng đủ tốt cho long-tail keyword và brand awareness.

Bài này hướng dẫn full pipeline: cron schedule, Python script orchestrate, Claude API gọi đúng cách, ảnh AI tạo bằng Nano Banana, post WordPress REST API. Mình đang chạy setup này cho tnd.vn, vibecoder.dev, 5 blog niche khác, tổng output 800+ bài/năm với chi phí AI dưới 30 triệu.

1. Kiến trúc pipeline tổng thể

cron (08:00 daily)
  -> bash trigger
    -> python3 generate.py
      -> 1. Load keyword bank (YAML)
      -> 2. Pick today's topic (sequential or random)
      -> 3. Claude API: generate outline (Haiku, ~0.01 USD)
      -> 4. Claude API: write full article 2500 words (Sonnet, ~0.2 USD)
      -> 5. Higgsfield/DALL-E: generate cover image (~0.04 USD)
      -> 6. Convert MD to Gutenberg HTML
      -> 7. WP REST API: create post, upload image, set featured
      -> 8. Log result to PostgreSQL
      -> 9. Slack notification: "Posted: {title}"

Modular hoá: mỗi step 1 file Python riêng (research.py, generate.py, image.py, publish.py). Pipeline orchestrator chỉ call thứ tự. Dễ debug khi 1 step fail.

2. Cài đặt VPS và dependency

# Cloud VPS 20 (199k/tháng) đủ cho pipeline đơn giản
dnf install python3 python3-pip git tmux -y

mkdir -p /opt/blogbot
cd /opt/blogbot
python3 -m venv venv
source venv/bin/activate

pip install anthropic pyyaml jinja2 markdown beautifulsoup4 
    requests python-slugify python-dotenv pillow openai

File .env chứa API key:

# /opt/blogbot/.env
ANTHROPIC_API_KEY=sk-ant-...
HIGGSFIELD_TOKEN=...
WP_BASE_URL=https://your-domain.com
WP_USER=blogbot
WP_APP_PASSWORD=xxxx xxxx xxxx xxxx
SLACK_WEBHOOK=https://hooks.slack.com/...

chmod 600 .env. Tạo Application Password trong WordPress: User profile, scroll xuống Application Passwords, generate cho user blogbot có role Author.

3. Keyword bank YAML

# /opt/blogbot/keywords.yaml
- slug: setup-meilisearch-vps
  title: "Setup MeiliSearch trên VPS"
  focus_kw: "meilisearch vps self host"
  intent: tutorial
  cluster: backend
  scene: "MeiliSearch magnifying glass instant search UI"
  scheduled: "2026-06-12 12:50"
- slug: cron-claude-api-blog
  title: "Cron Claude API tự động viết blog"
  focus_kw: "cron claude api auto blog"
  intent: tutorial
  cluster: ai-automation
  scene: "Clock cron Claude API blog automation"
  scheduled: "2026-06-15 11:31"
# ... 365 entries

Pre-plan keyword bank cho 1 năm. Dùng Keyword Tool/Ahrefs research, hoặc nhờ Claude generate 1000 topic ideas từ 1 niche seed.

4. Python script main pipeline

# /opt/blogbot/generate.py
import os, yaml, sys
from datetime import datetime
from anthropic import Anthropic
from slugify import slugify
from dotenv import load_dotenv

load_dotenv('/opt/blogbot/.env')
client = Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])

def pick_topic():
    with open('/opt/blogbot/keywords.yaml') as f:
        topics = yaml.safe_load(f)
    now = datetime.now()
    for t in topics:
        sched = datetime.strptime(t['scheduled'], '%Y-%m-%d %H:%M')
        if sched.date() == now.date() and not is_published(t['slug']):
            return t
    return None

def generate_outline(topic):
    prompt = f"""Bạn là expert SEO blogger Việt Nam. Generate outline chi tiết cho bài blog:
- Title: {topic['title']}
- Focus keyword: {topic['focus_kw']}
- Intent: {topic['intent']}
- Length: 2500-3500 từ
Output JSON với keys: tldr_bullets (5 items), sections (12 items với h2 và bullet points), faq (5 Q/A)."""
    msg = client.messages.create(
        model="claude-haiku-4-20260101",
        max_tokens=4000,
        messages=[{"role": "user", "content": prompt}]
    )
    return msg.content[0].text

def write_article(topic, outline):
    prompt = f"""Viết bài blog tiếng Việt 2500-3500 từ theo outline. Tone: technical mentor, concise.
Format: Gutenberg blocks (wp:html, wp:paragraph, wp:heading, wp:preformatted, wp:list, wp:table).
NO em-dash, NO en-dash, chỉ dùng hyphen.
NO inline script tag, FAQ schema sẽ save vào post_meta.
TL;DR box ở đầu (wp:html dark navy #0B1230).
CTA card cuối link /vps/ (dark gradient).
Topic: {topic['title']}
Outline: {outline}"""
    msg = client.messages.create(
        model="claude-sonnet-4-7-20260520",
        max_tokens=16000,
        messages=[{"role": "user", "content": prompt}]
    )
    return msg.content[0].text

def main():
    topic = pick_topic()
    if not topic:
        print("No topic for today")
        sys.exit(0)
    print(f"Generating: {topic['title']}")
    outline = generate_outline(topic)
    article_html = write_article(topic, outline)
    cover_url = generate_cover(topic['scene'])
    post_id = publish_wp(topic, article_html, cover_url)
    log_to_db(topic, post_id)
    slack_notify(f"Posted: {topic['title']} (id {post_id})")

if __name__ == "__main__":
    main()

5. Generate ảnh cover bằng Nano Banana hoặc DALL-E

# image.py
import os, requests, time

def generate_cover(scene):
    # Higgsfield Nano Banana 2 (rẻ và đẹp)
    prompt = f"""Dark tech minimal blog cover, 16:9 landscape, deep navy #0B1230 background with subtle dotted grid. Center: {scene}. Glowing neon accents #2B4CFE blue and #7A5BFF purple. Editorial pro composition, no people, no readable brand names, 4K sharp."""

    r = requests.post(
        "https://api.higgsfield.ai/v1/generations",
        headers={"Authorization": f"Bearer {os.environ['HIGGSFIELD_TOKEN']}"},
        json={"model": "nano_banana_2", "prompt": prompt, "aspect_ratio": "16:9", "count": 1}
    )
    job_id = r.json()['id']

    # Poll until done
    for _ in range(30):
        status = requests.get(
            f"https://api.higgsfield.ai/v1/generations/{job_id}",
            headers={"Authorization": f"Bearer {os.environ['HIGGSFIELD_TOKEN']}"}
        ).json()
        if status['status'] == 'completed':
            return status['results']['rawUrl']
        time.sleep(5)
    raise Exception("Image generation timeout")

6. Publish lên WordPress REST API

# publish.py
import os, requests
from requests.auth import HTTPBasicAuth

WP = os.environ['WP_BASE_URL']
auth = HTTPBasicAuth(os.environ['WP_USER'], os.environ['WP_APP_PASSWORD'])

def upload_image(image_url, filename):
    img_data = requests.get(image_url).content
    r = requests.post(
        f"{WP}/wp-json/wp/v2/media",
        auth=auth,
        headers={"Content-Disposition": f'attachment; filename="{filename}"',
                 "Content-Type": "image/png"},
        data=img_data
    )
    return r.json()['id']

def publish_wp(topic, html_content, cover_url):
    media_id = upload_image(cover_url, f"cover-{topic['slug']}.png")
    r = requests.post(
        f"{WP}/wp-json/wp/v2/posts",
        auth=auth,
        json={
            "title": topic['title'],
            "slug": topic['slug'],
            "content": html_content,
            "status": "future",
            "date": topic['scheduled'],
            "featured_media": media_id,
            "excerpt": topic.get('excerpt', ''),
            "meta": {
                "_yoast_wpseo_focuskw": topic['focus_kw']
            }
        }
    )
    return r.json()['id']

7. Setup cron trigger

# crontab -e
# Chạy 8h sáng hằng ngày, log vào file
0 8 * * * /opt/blogbot/venv/bin/python3 /opt/blogbot/generate.py >> /var/log/blogbot.log 2>&1

# Hoặc 2 bài/ngày 8h và 16h
0 8,16 * * * /opt/blogbot/venv/bin/python3 /opt/blogbot/generate.py >> /var/log/blogbot.log 2>&1

Verify cron chạy: tail -f /var/log/blogbot.log. Xem timezone server: timedatectl. Set Asia/Ho_Chi_Minh nếu cần.

8. Quality control: review trước khi publish

Auto-publish rủi ro spam Google. Smarter: cron tạo post status=draft, gửi link Slack cho admin review, click "Publish" sau khi confirm:

# Trong publish_wp, đổi status thành "draft" thay vì "future"
"status": "draft",

# Slack notification thêm link
slack_notify(f"Draft ready: {WP}/wp-admin/post.php?post={post_id}&action=edit")

Mất 5 phút/bài để review. Nếu nội dung OK 95% lần, có thể chuyển sang full auto-publish.

9. Tracking metric: post nào lên Google nhanh

Log mọi post vào PostgreSQL, sau đó join Google Search Console API để track impression/click theo từng URL. Schema mẫu:

CREATE TABLE auto_posts (
  id SERIAL PRIMARY KEY,
  wp_post_id INT NOT NULL,
  slug VARCHAR(200),
  focus_kw VARCHAR(200),
  published_at TIMESTAMP DEFAULT NOW(),
  ai_cost_usd NUMERIC(6,4),
  indexed_at TIMESTAMP,
  first_impression_at TIMESTAMP,
  current_clicks INT DEFAULT 0,
  current_impressions INT DEFAULT 0
);

CREATE INDEX idx_published ON auto_posts(published_at);

Cron khác chạy mỗi ngày sync GSC API, update clicks/impressions. Dashboard Metabase show ROI: chi phí AI vs traffic organic mang về.

10. Tối ưu prompt cho output chất lượng cao

  • Cung cấp context VN: "Viết cho dev Việt Nam, dùng tiếng Việt tự nhiên, thỉnh thoảng từ English chuyên ngành."
  • Sample article reference: paste sample bài quality cao vào system prompt, Claude follow style.
  • Constraint rõ: word count 2500-3500, structure 12 H2, FAQ 5 câu, không em-dash.
  • Persona: "Bạn là tech lead 10 năm kinh nghiệm, viết casual nhưng technical sâu."
  • Output format: yêu cầu Gutenberg block syntax explicit, dễ paste vào WP không cần convert.

11. Chi phí thực tế từng bài

StepModelInput tokensOutput tokensCost (USD)
OutlineClaude Haiku 450015000.015
Full articleClaude Sonnet 4.7200080000.246
Image coverNano Banana 2-1 image0.04
FAQ extractClaude Haiku 440005000.012
Slack notify---0
Tổng0.313

~0.3 USD/bài, 365 bài/năm ~110 USD ~2.7 triệu VND. Cộng VPS 2.4 triệu/năm = 5.1 triệu/năm cho 365 bài SEO. Quá rẻ so với thuê writer.

12. Risk và mitigation

  1. Google phạt AI content: Google đã clarify chỉ phạt thin/spammy content, AI quality cao vẫn rank. Mitigation: viết thật helpful, có insight, không spin keyword.
  2. Hallucination AI: Claude có thể fabric fact. Mitigation: prompt yêu cầu cite source, review draft mode trước publish.
  3. Quality giảm theo thời gian: model có thể down quality, monitor metric clicks/impressions hằng tháng.
  4. API rate limit: Anthropic rate limit hợp lý cho 1 bài/ngày, nhưng nếu scale 100 bài/ngày cần upgrade tier.
  5. WP server down: pipeline fail silent nếu không có alert. Setup Slack notification cho cả success và fail.

13. Mở rộng: multi-blog từ 1 pipeline

Mình chạy 7 blog từ cùng pipeline, config khác nhau:

# /opt/blogbot/blogs.yaml
- name: tnd-vn
  wp_url: https://www.tnd.vn
  wp_user: blogbot
  wp_password: $ENV:TND_WP_PASS
  keywords_file: keywords-tnd.yaml
  persona: "Tech mentor cho dev và startup Việt Nam"

- name: vibecoder
  wp_url: https://vibecoder.dev
  wp_user: bot
  wp_password: $ENV:VIBECODER_PASS
  keywords_file: keywords-vibecoder.yaml
  persona: "Indie hacker writing in English"

Cron pick blog round-robin hoặc theo schedule riêng, 1 VPS chạy được 10+ blog với chi phí AI dưới 50 USD/tháng.

14. Bài học sau 1 năm chạy auto blog

  1. Quality > Quantity. 100 bài tốt thắng 365 bài thường về traffic dài hạn.
  2. Long-tail keyword dễ rank hơn head keyword nhiều. AI viết long-tail rất tốt.
  3. Internal linking quan trọng. Auto add link tới 3-5 bài liên quan (dùng vector similarity).
  4. Update bài cũ quan trọng. Cron khác chạy mỗi tháng pick 5 bài cũ refresh content.
  5. Image quality ảnh hưởng CTR. Đầu tư prompt cover ảnh kỹ.

15. Pipeline alternative: n8n workflow visual

Không muốn code Python? Dùng n8n self-host trên VPS, kéo thả node Cron, HTTP Request (Claude API), HTTP Request (WP API). UI visual, dễ debug step by step. Trade-off: ít linh hoạt hơn Python code, memory n8n ăn 400MB+.

16. Production-grade: queue và retry

Khi scale lên 50+ bài/ngày, thay cron bằng queue Redis/BullMQ + worker. Failed job auto retry 3 lần, dead-letter queue cho job vẫn fail. Combine với observability stack Sentry để track error.

Cloud VPS cho vibe coder

VPS chạy pipeline auto blog cho indie hacker

Cloud VPS TND sẵn AlmaLinux 9, Ubuntu 22/24, Debian 12/13. SSD CEPH, snapshot 1-click, backup hằng ngày, network 200Mbps trong nước. Cloud VPS 20 (199k/tháng) đủ chạy pipeline 5-10 blog auto, scale lên Cloud VPS 40 khi cần queue và worker phức tạp.

Xem 8 cấu hình Cloud VPS →

FAQ

Google có phạt bài viết bằng AI không?

Google March 2024 statement: chỉ phạt content "spammy, low value, mass-produced for ranking", không phạt AI per se. Bài AI quality cao, có insight, original angle vẫn rank tốt. Mình có 200+ bài AI rank top 10 cho long-tail keyword tnd.vn.

Claude vs GPT-4o cho viết blog tiếng Việt?

Claude Sonnet 4.7 viết tiếng Việt tự nhiên hơn GPT-4o, ít lỗi grammar, hiểu sắc thái văn hoá Việt tốt hơn. GPT-4o nhanh hơn, rẻ hơn 30% nhưng output đôi khi gượng. Cho blog tiếng Việt, Claude là lựa chọn tốt.

Có cần cron trên VPS không, dùng GitHub Actions được không?

Được. GitHub Actions scheduled workflow chạy free 2000 phút/tháng (private repo) hoặc unlimited (public). Trade-off: ít control hơn, có lúc Actions queue chậm 5-10 phút. Cho hobby project dùng GH Actions, cho production dùng VPS cron ổn định hơn.

Làm sao tránh duplicate content giữa các bài?

Pre-plan keyword bank chuẩn, không 2 bài cùng keyword. Trước khi publish, embed bài mới, query vector DB tìm bài tương tự, nếu similarity > 0.85 thì skip hoặc rewrite. Combine với SurferSEO/Frase để check overlap content.

WordPress REST API có giới hạn gì?

Default no rate limit nhưng nên throttle 5 request/giây để tránh overload server. Application Password an toàn hơn dùng admin password. Upload media giới hạn theo php.ini upload_max_filesize, default 2MB, tăng lên 10MB cho ảnh cover.

17. Tổng kết và roadmap mở rộng

Sau khi pipeline ổn định, mở rộng bằng cách thêm các bước: auto research keyword từ Google Trends, auto distribute lên LinkedIn/Twitter sau khi publish, auto internal link cross-post, auto refresh bài cũ mỗi 6 tháng. Mỗi bước thêm 1 file Python độc lập, không break existing pipeline. Sau 1 năm, content factory của bạn sẽ chạy gần như autopilot, mỗi tháng output 30-60 bài chất lượng đều với chi phí AI dưới 30 USD và VPS 199k/tháng. ROI rất cao nếu blog phục vụ SEO cho SaaS hoặc product có giá trị mỗi lead.

2009
15+ năm vận hành liên tục
10+
tập đoàn lớn tin dùng
100+
doanh nghiệp SMB Việt
30 ngày
đổi key lỗi miễn phí
Phần mềm bản quyền chính hãng chúng tôi cung cấp
Bản quyền chính hãng Hóa đơn VAT đầy đủ Đổi key lỗi 30 ngày Vận hành từ 2009 MST 0200994870 Hotline 0225.999.6666