Xây dựng Bot "Báo Đêm": Tự động cào tin, viết lại và đăng lên XenForo

AI Hunter

Member
Bạn quản lý một diễn đàn công nghệ/tin tức (XenForo) nhưng quá bận rộn để cập nhật bài viết mỗi ngày? Thuê Content Writer thì tốn kém?

Trong bài viết này, chúng ta sẽ xây dựng một Smart Crawler bằng Python với quy trình tự động hóa 100%:
  1. Săn tin: Quét bài viết mới từ các trang báo lớn.
  2. Tải ảnh: Tự động tải ảnh đại diện về máy.
  3. Xào bài (Spin): Dùng AI viết lại nội dung để tránh bản quyền Google.
  4. Đăng bài: Upload ảnh và bài viết lên XenForo thông qua API.

Xây dựng Bot Báo Đêm Tự động cào tin, viết lại và đăng lên XenForo.jpg

1. Kiến trúc hệ thống​


Chúng ta sẽ không "copy-paste" mù quáng. Bot sẽ hoạt động như một biên tập viên thực thụ: Đọc hiểu -> Viết lại -> Xuất bản.

Yêu cầu chuẩn bị​

  • Python 3.x và các thư viện: `requests`, `beautifulsoup4`, `openai`.
  • XenForo API Key:
    * Vào AdminCP > Setup > API keys.
    * Tạo Key mới, phần User chọn tài khoản Bot.
    * Scopes (Quyền): Bắt buộc phải tích vào `thread:write`, `thread:read`, `attachment:write`.

2. Triển khai Code (Full Source)​


Dưới đây là file `news_bot.py`. Code đã được tối ưu để xử lý cả ảnh đính kèm.

Python:
import requests
import os
import json
from bs4 import BeautifulSoup
from openai import OpenAI

# --- CẤU HÌNH HỆ THỐNG ---
# 1. XenForo Config
XF_DOMAIN = "https://your-forum.com"
XF_API_KEY = "YOUR_SUPER_SECRET_API_KEY"
XF_API_USER_ID = 1      # ID user sẽ đứng tên bài viết
TARGET_NODE_ID = 2      # ID chuyên mục (Node) muốn đăng bài

# 2. OpenAI Config
os.environ["OPENAI_API_KEY"] = "sk-YOUR_OPENAI_KEY"
client = OpenAI()

# 3. Header giả lập trình duyệt (để tránh bị chặn khi cào)
HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}

def download_image(img_url):
    """Tải ảnh từ URL về máy để chuẩn bị upload"""
    if not img_url: return None
    print(f"🖼️ Đang tải ảnh: {img_url}")
    try:
        response = requests.get(img_url, headers=HEADERS, stream=True)
        if response.status_code == 200:
            filename = "temp_cover.jpg"
            with open(filename, 'wb') as f:
                for chunk in response.iter_content(1024):
                    f.write(chunk)
            return filename
    except Exception as e:
        print(f"❌ Lỗi tải ảnh: {e}")
    return None

def upload_attachment_to_xf(file_path):
    """Upload ảnh lên XenForo và lấy Attachment Key"""
    if not file_path: return None
    print("⬆️ Đang upload ảnh lên XenForo...")
   
    endpoint = f"{XF_DOMAIN}/api/attachments/new-key"
    headers = {"XF-Api-Key": XF_API_KEY, "XF-Api-User": str(XF_API_USER_ID)}
   
    # 1. Lấy Key upload tạm
    key_resp = requests.post(endpoint, headers=headers, json={"type": "post"})
    if key_resp.status_code != 200:
        print("❌ Lỗi lấy Key upload:", key_resp.text)
        return None
       
    temp_key = key_resp.json()['key']
   
    # 2. Upload file thật
    upload_endpoint = f"{XF_DOMAIN}/api/attachments"
    files = {'upload': open(file_path, 'rb')}
    data = {'key': temp_key}
   
    upload_resp = requests.post(upload_endpoint, headers=headers, files=files, data=data)
   
    if upload_resp.status_code == 200:
        attachment_id = upload_resp.json()['attachment']['attachment_id']
        print(f"✅ Upload xong. ID: {attachment_id}")
        return attachment_id
    else:
        print("❌ Lỗi upload file:", upload_resp.text)
        return None

def scrape_and_rewrite(url):
    """Cào tin và dùng AI viết lại"""
    print(f"🕷️ Đang xử lý tin: {url}")
   
    # --- PHẦN 1: CÀO TIN (Tùy chỉnh Selector theo trang báo cụ thể) ---
    # Ví dụ mẫu cho một trang tin tức giả định
    resp = requests.get(url, headers=HEADERS)
    soup = BeautifulSoup(resp.text, 'html.parser')
   
    # Lấy tiêu đề (thường là thẻ h1)
    original_title = soup.find('h1').text.strip()
   
    # Lấy nội dung (thường nằm trong div class='content' hoặc 'article-body')
    # Bạn cần F12 trên trình duyệt để tìm đúng class này
    original_content = soup.find('div', class_='detail-content').text.strip()
   
    # Lấy ảnh bìa (thẻ meta og:image thường chứa ảnh nét nhất)
    img_tag = soup.find('meta', property='og:image')
    img_url = img_tag['content'] if img_tag else None

    # --- PHẦN 2: AI VIẾT LẠI (SPIN CONTENT) ---
    print("🧠 AI đang viết lại nội dung...")
    prompt = f"""
    Bạn là Admin diễn đàn công nghệ. Hãy viết lại bài tin tức sau đây.
    Yêu cầu:
    1. Tiêu đề mới hấp dẫn, chuẩn SEO.
    2. Nội dung tóm tắt lại các ý chính, giọng văn chia sẻ, thân thiện.
    3. Định dạng đầu ra JSON: {{"title": "...", "body": "..."}}
   
    Tin gốc:
    Tiêu đề: {original_title}
    Nội dung: {original_content[:2000]}
    """
   
    ai_resp = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": prompt}],
        response_format={ "type": "json_object" }
    )
   
    new_data = json.loads(ai_resp.choices[0].message.content)
    return new_data['title'], new_data['body'], img_url

def post_thread(node_id, title, body, attachment_id=None):
    """Đăng chủ đề mới"""
    print("🚀 Đang đăng bài lên diễn đàn...")
    endpoint = f"{XF_DOMAIN}/api/threads"
    headers = {"XF-Api-Key": XF_API_KEY, "XF-Api-User": str(XF_API_USER_ID)}
   
    # Chèn ảnh vào đầu bài viết nếu có
    final_body = body
    if attachment_id:
        final_body = f"[ATTACH=full]{attachment_id}[/ATTACH]\n\n{body}"
       
    final_body += "\n\n[I][SIZE=3]Nguồn: Tổng hợp từ Internet[/SIZE][/I]"

    data = {
        "node_id": node_id,
        "title": title,
        "message": final_body,
        "tags[]": ["tin-tuc", "auto-bot"]
    }
   
    # Nếu có ảnh, đính kèm ID vào thread để XenForo hiểu
    if attachment_id:
        data["attachment_ids[]"] = attachment_id

    resp = requests.post(endpoint, headers=headers, data=data)
    if resp.status_code == 200:
        print(f"🎉 THÀNH CÔNG! Link bài: {resp.json()['thread']['view_url']}")
    else:
        print(f"❌ Lỗi đăng bài: {resp.text}")

# --- MAIN ---
if __name__ == "__main__":
    # Link bài báo muốn cào (Thay đổi link này)
    TARGET_URL = "https://example.com/bai-viet-cong-nghe-moi"
   
    # 1. Cào & Viết lại
    title, body, img_url = scrape_and_rewrite(TARGET_URL)
   
    # 2. Xử lý ảnh
    local_img = download_image(img_url)
    att_id = upload_attachment_to_xf(local_img) if local_img else None
   
    # 3. Đăng bài
    post_thread(TARGET_NODE_ID, title, body, att_id)
   
    # Dọn dẹp file ảnh tạm
    if local_img and os.path.exists(local_img):
        os.remove(local_img)

3. Lưu ý kỹ thuật quan trọng​


  • Selector (Bộ chọn HTML): Đây là phần khó nhất. Mỗi trang báo (VnExpress, GenK, TinhTe) có cấu trúc HTML khác nhau. Bạn cần dùng chức năng Inspect Element (F12) của trình duyệt để tìm đúng `class` chứa nội dung bài viết và thay vào code `soup.find()`.
  • XenForo Attachment: API XenForo 2.x yêu cầu quy trình upload khá chặt chẽ. Code trên đã xử lý đúng chuẩn: Lấy Key tạm -> Upload file -> Lấy ID -> Gắn vào Thread.
  • Bản quyền: Bot này dùng cho mục đích học tập. Khi chạy thực tế, hãy tôn trọng bản quyền tác giả bằng cách luôn ghi nguồn và chỉ nên tóm tắt lại nội dung (Fair Use).

4. Hướng phát triển​


Bạn có thể nâng cấp bot này thành "Super Bot":
  • Quét RSS: Thay vì nhập link thủ công, hãy cho bot đọc RSS Feed của các trang báo, hễ có bài mới là tự động lấy về.
  • Dịch thuật: Cào tin từ trang tiếng Anh (TheVerge, TechCrunch) -> Dùng AI dịch sang tiếng Việt -> Đăng bài độc quyền.
  • Chạy định kỳ (Cronjob): Cài đặt trên VPS để bot chạy 30 phút/lần, diễn đàn của bạn sẽ luôn sôi động 24/7.
 
Back
Top