import streamlit as st import pandas as pd import sqlite3 import requests import numpy as np # 新增:用於處理 NaN from io import BytesIO from PIL import Image from database import recommend_books import time # 新增:用於進度條 # 標題 st.title("圖書搜尋") @st.cache_data def load_books(): conn = sqlite3.connect('books.db') df = pd.read_sql_query("SELECT * FROM books", conn) conn.close() # 處理必要欄位型態 for col in ['discount_price', 'list_price', 'stock']: if col in df.columns: df[col] = pd.to_numeric(df[col].replace('N/A', np.nan), errors='coerce').fillna(0) return df df = load_books() def run_recommendation(): with st.spinner("正在計算推薦..."): time.sleep(0.5) st.session_state.recs = recommend_books(st.session_state.search_query) st.session_state.selected_display = None if st.session_state.recs.empty: st.warning("無推薦結果,請調整關鍵字。") else: st.success(f"找到 {len(st.session_state.recs)} 本推薦書籍!") if "search_query" not in st.session_state: st.session_state.search_query = "" search_query = st.text_input( "輸入關鍵字(如:靈修、耶穌)", value=st.session_state.search_query, key="search_query", on_change=run_recommendation ) if "recs" not in st.session_state: st.session_state.recs = pd.DataFrame() if st.session_state.recs is None or st.session_state.recs.empty: st.session_state.recs = pd.DataFrame() if "selected_display" not in st.session_state: st.session_state.selected_display = None if not st.session_state.recs.empty: st.subheader("推薦書籍列表(相似度排序)") recs_display = st.session_state.recs[['product_id', 'title', 'author', 'similarity']].copy() import re recs_display['顯示'] = recs_display['title'].apply(lambda x: re.sub(r'<[^>]+>', '', str(x))) recs_display['顯示'] = recs_display['顯示'].apply(lambda x: re.sub(r'~', '~', str(x))) for idx, row in recs_display.iterrows(): if st.button(row['顯示'], key=f"book_{row['product_id']}"): st.session_state.selected_display = row['顯示'] if st.session_state.selected_display: selected_row = None if not st.session_state.recs.empty: recs_display = st.session_state.recs[['product_id', 'title', 'author', 'similarity']].copy() import re recs_display['顯示'] = recs_display['title'].apply(lambda x: re.sub(r'<[^>]+>', '', str(x))) recs_display['顯示'] = recs_display['顯示'].apply(lambda x: re.sub(r'~', '~', str(x))) selected_rows = recs_display[recs_display['顯示'] == st.session_state.selected_display] if not selected_rows.empty: selected_row = selected_rows.iloc[0] if selected_row is not None: book_data = df[df['product_id'] == selected_row['product_id']].iloc[0] def clean_text(val): import re return re.sub(r'~', '~', re.sub(r'<[^>]+>', '', str(val))) col1, col2 = st.columns([1, 2]) with col1: if 'image_url' in book_data and book_data['image_url']: try: response = requests.get(book_data['image_url'], verify=False, timeout=10) response.raise_for_status() img = Image.open(BytesIO(response.content)) img = img.resize((180, 250)) st.image(img, caption=f"{clean_text(book_data['title'])} - 作者:{clean_text(book_data['author'])}", width=180) except Exception as e: st.error(f"圖片載入失敗:{e}(已跳過 SSL 驗證)") with col2: st.header("書籍詳情") product_id = book_data.get('product_id', '') st.markdown(f""" **書名**:{clean_text(book_data.get('title', 'N/A'))} **作者**:{clean_text(book_data.get('author', 'N/A'))} **出版社**:{clean_text(book_data.get('publisher', 'N/A'))} **優惠價**:{book_data.get('discount_price', 'N/A')} **定價**:{book_data.get('list_price', 'N/A')} **購買連結**:前往購買 """, unsafe_allow_html=True) content_intro = book_data.get('content_intro', 'N/A') content_intro = clean_text(content_intro) st.markdown("**內容簡介**:") st.write(content_intro[:500] + "..." if len(content_intro) > 500 else content_intro)