可以幫我們看看程式碼中有沒有哪裡錯,因為不是本科系,所以救救商科女大生,謝謝大家!
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (
MessageEvent,
TextMessage,
TextSendMessage)
import os
import requests
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime
# 設定 Line Bot API
LINE_TOKEN = os.getenv('LINE_TOKEN')
LINE_SECRET = os.getenv('LINE_SECRET')
if not LINE_TOKEN or not LINE_SECRET:
raise ValueError("請設置 LINE_TOKEN 和 LINE_SECRET 環境變數")
api = LineBotApi(LINE_TOKEN)
handler = WebhookHandler(LINE_SECRET)
app = Flask(__name__)
def get_uniqlo_price_history(product_code):
"""爬取 Uniqlo 商品價格歷史"""
try:
# Uniqlo 台灣官網的 URL 格式
url = f"https://www.uniqlo.com/tw/zh_TW/products/{product_code}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7',
'Connection': 'keep-alive'
}
response = requests.get(url, headers=headers, timeout=10)
if response.status_code != 200:
return None
soup = BeautifulSoup(response.text, 'html.parser')
try:
product_name = soup.select_one('[data-test="product-name"]').text.strip()
current_price = soup.select_one('[data-test="price-value"]').text.strip()
except AttributeError:
return None
return {
'name': product_name,
'current_price': current_price,
'url': url
}
except Exception as e:
print(f"爬蟲錯誤:{str(e)}")
return None
# 加入根路由,確認服務器正常運行
@app.route("/", methods=['GET'])
def hello():
return 'Hello, World!'
# 處理 webhook
@app.route("/webhook", methods=['POST'])
def webhook():
# 取得 X-Line-Signature 表頭電子簽章內容
signature = request.headers.get('X-Line-Signature', '')
# 以文字形式取得請求內容
body = request.get_data(as_text=True)
print(f"收到 webhook 請求:{body}") # 添加日誌
# 比對電子簽章並處理請求內容
try:
handler.handle(body, signature)
except InvalidSignatureError:
print("電子簽章錯誤,請檢查密鑰是否正確?")
abort(400)
except Exception as e:
print(f"發生錯誤:{str(e)}")
abort(500)
return 'OK'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
try:
user_message = event.message.text
print(f"收到用戶訊息:{user_message}")
if len(user_message) == 6 and user_message.isdigit():
# 爬取價格歷史
price_info = get_uniqlo_price_history(user_message)
if price_info:
reply_text = (
f"商品名稱:{price_info['name']}\n"
f"目前售價:{price_info['current_price']}\n"
f"商品連結:{price_info['url']}\n\n"
f"💡 提示:建議等待特價活動時購買"
)
else:
reply_text = "抱歉,找不到此商品資訊,請確認商品編號是否正確。"
else:
reply_text = "請輸入正確的 Uniqlo 商品貨號(6位數字)\n例如:469773"
print(f"準備回覆:{reply_text}")
api.reply_message(
event.reply_token,
TextSendMessage(text=reply_text))
print("回覆訊息成功")
except Exception as e:
print(f"處理訊息時發生錯誤:{str(e)}")
api.reply_message(
event.reply_token,
TextSendMessage(text="抱歉,系統發生錯誤,請稍後再試。"))
if __name__ == "__main__":
print("正在啟動機器人...")
print(f"LINE_TOKEN 是否存在: {bool(os.getenv('LINE_TOKEN'))}")
print(f"LINE_SECRET 是否存在: {bool(os.getenv('LINE_SECRET'))}")
# 測試 LINE Bot API 連接
try:
bot_info = api.get_bot_info()
print(f"機器人名稱: {bot_info.display_name}")
print("LINE Bot API 連接成功")
except Exception as e:
print(f"LINE Bot API 連接失敗: {str(e)}")
port = int(os.environ.get('PORT', 8080))
app.run(host='0.0.0.0', port=port)
我這邊直接把你的程式碼跑起來串我自己的 Linebot,這部分有回應也沒有問題。
至於爬蟲那部分我沒有看得很仔細,但是這個頁面感覺是沒辦法直接抓到他的靜態 html 完整內容,感覺爬蟲那塊可以調整一下。
應該是你的 official account 那邊自動回應功能沒有關閉,這樣訊息就不會轉送到你 webhook 設定的後端,可參考這裡第 5 步驟: