i am making a book recommendation app for korean people, they type query and see the book recommendations, make the frontend very very very beautiful and aesthetic
To upload files, please first save the app
import streamlit as st
import pandas as pd
import requests
import json
from PIL import Image
import io
import base64
# Set page config for a wider layout and custom title/icon
st.set_page_config(
page_title="μ±
μΆμ² | Book Recommender",
page_icon="π",
layout="wide",
initial_sidebar_state="collapsed"
)
# Custom CSS for a more aesthetic look
def add_bg_from_url():
st.markdown(
f"""
<style>
.stApp {{
background-image: url("https://images.unsplash.com/photo-1507842217343-583bb7270b66?ixlib=rb-1.2.1&auto=format&fit=crop&w=1200&q=80");
background-attachment: fixed;
background-size: cover;
background-position: center;
}}
.main-container {{
background-color: rgba(255, 255, 255, 0.85);
border-radius: 20px;
padding: 2rem;
backdrop-filter: blur(5px);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
margin: 1rem;
}}
.book-card {{
background-color: white;
border-radius: 10px;
padding: 1.5rem;
margin-bottom: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}}
.book-card:hover {{
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}}
.book-title {{
font-family: 'Nanum Gothic', sans-serif;
font-weight: 700;
font-size: 1.2rem;
margin-bottom: 0.5rem;
color: #333;
}}
.book-author {{
font-family: 'Nanum Gothic', sans-serif;
font-style: italic;
color: #666;
margin-bottom: 0.5rem;
}}
.book-description {{
font-family: 'Nanum Gothic', sans-serif;
font-size: 0.9rem;
color: #444;
line-height: 1.5;
}}
.search-container {{
background-color: rgba(255, 255, 255, 0.9);
padding: 2rem;
border-radius: 15px;
margin-bottom: 2rem;
backdrop-filter: blur(10px);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
}}
.stTextInput > div > div > input {{
background-color: white;
padding: 1rem;
font-size: 1.1rem;
border-radius: 10px;
border: 1px solid #ddd;
box-shadow: none !important;
}}
h1, h2, h3 {{
font-family: 'Nanum Gothic', sans-serif;
color: #333;
}}
.korean-text {{
font-family: 'Nanum Gothic', sans-serif;
}}
.stButton>button {{
background-color: #5D6D7E;
color: white;
font-family: 'Nanum Gothic', sans-serif;
font-weight: 500;
padding: 0.5rem 2rem;
border-radius: 10px;
border: none;
transition: all 0.3s;
}}
.stButton>button:hover {{
background-color: #34495E;
transform: scale(1.03);
}}
.tag {{
display: inline-block;
background-color: #EBF5FB;
color: #2E86C1;
padding: 0.3rem 0.8rem;
border-radius: 15px;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
font-size: 0.8rem;
font-family: 'Nanum Gothic', sans-serif;
}}
</style>
<link href="https://fonts.googleapis.com/css2?family=Nanum+Gothic:wght@400;700;800&display=swap" rel="stylesheet">
""",
unsafe_allow_html=True,
)
# Add Korean font and background
add_bg_from_url()
# Create main container for content
st.markdown('<div class="main-container">', unsafe_allow_html=True)
# Logo and Title
col1, col2, col3 = st.columns([1, 3, 1])
with col2:
st.title("π μ±
μΆμ² μμ€ν
| Book Recommender")
st.markdown(
"<p class='korean-text' style='font-size: 1.2rem; margin-bottom: 2rem; text-align: center;'>"
"μ½κ³ μΆμ μ±
μ μ£Όμ , μκ° λλ μ₯λ₯΄λ₯Ό μ
λ ₯νμΈμ<br>"
"Enter a topic, author or genre you're interested in reading about"
"</p>",
unsafe_allow_html=True
)
# Search container
st.markdown('<div class="search-container">', unsafe_allow_html=True)
query = st.text_input("", placeholder="μ: ννμ§ μμ€, μκΈ°κ³λ°, νλ£¨ν€ λ¬΄λΌμΉ΄λ―Έ...", key="search_query")
search_col1, search_col2 = st.columns([5, 1])
with search_col2:
search_button = st.button("κ²μ | Search")
st.markdown('</div>', unsafe_allow_html=True)
# Sample book data (in lieu of an actual API)
def get_book_recommendations(query):
# This would be replaced with an actual API call
# In a real app, you'd call an external book recommendation API
# Sample data based on common Korean and international books
books = [
{
"title": "82λ
μ κΉμ§μ",
"title_en": "Kim Ji-young, Born 1982",
"author": "μ‘°λ¨μ£Ό (Cho Nam-joo)",
"image_url": "https://image.aladin.co.kr/product/11429/85/cover500/8937473135_1.jpg",
"description": "λνλ―Όκ΅μ νλ²ν μ¬μ±μ΄ κ²ͺλ μ±μ°¨λ³κ³Ό μΌμμ μ΄λ €μμ λ¬μ¬ν μμ€λ‘, νκ΅ μ¬νμ μ λ λ¬Έμ λ₯Ό λ€λ£¨κ³ μμ΅λλ€.",
"description_en": "A novel that depicts the gender discrimination and everyday difficulties experienced by an ordinary woman in South Korea.",
"tags": ["νλ―Έλμ¦", "μ¬νλΉν", "νλμμ€"]
},
{
"title": "μ§μ
μΌλ‘μμ μμ€κ°",
"title_en": "Novelist as a Profession",
"author": "무λΌμΉ΄λ―Έ νλ£¨ν€ (Haruki Murakami)",
"image_url": "https://image.aladin.co.kr/product/8921/99/cover500/8954637604_1.jpg",
"description": "μ λͺ
μκ° λ¬΄λΌμΉ΄λ―Έ ν루ν€κ° μμ€κ°λ‘μμ μμ μ κ²½νκ³Ό μ°½μ κ³Όμ μ λν΄ μ΄μΌκΈ°νλ μμΈμ΄ λͺ¨μμ§μ
λλ€.",
"description_en": "A collection of essays in which famous author Haruki Murakami talks about his experiences and creative process as a novelist.",
"tags": ["μμΈμ΄", "μκ°λ‘ ", "μ°½μλ‘ "]
},
{
"title": "νμΉμ½",
"title_en": "Pachinko",
"author": "μ΄λ―Όμ§ (Min Jin Lee)",
"image_url": "https://image.aladin.co.kr/product/17047/61/cover500/8954653472_1.jpg",
"description": "1900λ
λ μ΄λΆν° 80λ
λκΉμ§, μ¬μΌ νκ΅μΈ κ°μ‘± 4λμ κ±ΈμΉ μμ¬λ₯Ό κ·Έλ¦° λνμμ€μ
λλ€. μ΄λ―Ό, μ 체μ±, κ°μ‘±μ μ΄μΌκΈ°λ₯Ό λ΄κ³ μμ΅λλ€.",
"description_en": "An epic novel depicting the story of a Korean family in Japan spanning four generations from the early 1900s to the 1980s.",
"tags": ["μμ¬μμ€", "μ΄λ―Ό", "κ°μ‘±"]
},
{
"title": "μ°λ¦¬κ° λΉμ μλλ‘ κ° μ μλ€λ©΄",
"title_en": "If We Cannot Move at the Speed of Light",
"author": "κΉμ΄μ½ (Cho-yeop Kim)",
"image_url": "https://image.aladin.co.kr/product/19509/18/cover500/k102636743_1.jpg",
"description": "κ³Όνμ μμλ ₯κ³Ό μΈκ°μ κ°μ±μ κ²°ν©ν SF λ¨νΈμ§μΌλ‘, νκ΅ SF λ¬Ένμ μλ‘μ΄ μ§νμ μ΄μλ€λ νκ°λ₯Ό λ°λ μνμ
λλ€.",
"description_en": "A collection of SF short stories that combines scientific imagination with human emotions, praised for opening new horizons in Korean SF literature.",
"tags": ["SF", "λ¨νΈμ§", "μμλ ₯"]
},
{
"title": "μ¬νΌμμ€",
"title_en": "Sapiens: A Brief History of Humankind",
"author": "μ λ° νλΌλ¦¬ (Yuval Noah Harari)",
"image_url": "https://image.aladin.co.kr/product/6574/87/cover500/8934972815_1.jpg",
"description": "μΈλ₯μ μμ¬λ₯Ό κ±°μμ κ΄μ μμ λΆμν μ±
μΌλ‘, μΈκ°μ΄ μ΄λ»κ² μ§κ΅¬μμμ μ§λ°°μ μΈ μμΉμ μ€λ₯΄κ² λμλμ§ νꡬν©λλ€.",
"description_en": "A book that analyzes human history from a macro perspective, exploring how humans came to dominate the planet.",
"tags": ["μμ¬", "μΈλ₯ν", "κ΅μ"]
},
{
"title": "μλͺ¬λ",
"title_en": "Almond",
"author": "μμν (Won-pyung Sohn)",
"image_url": "https://image.aladin.co.kr/product/17048/35/cover500/k402535150_1.jpg",
"description": "κ°μ μ λλΌμ§ λͺ»νλ μλ
μ€μ¬μ λΆλ
Έλ‘ κ°λ μ°¬ μλ
κ³€μ΄μ λ§λ¨μ ν΅ν΄ κ°μ κ³Ό 곡κ°μ μλ―Έλ₯Ό νμνλ μ±μ₯μμ€μ
λλ€.",
"description_en": "A coming-of-age novel exploring the meaning of emotions and empathy through the meeting of Yoon Jae, a boy who cannot feel emotions, and Gon, a boy filled with anger.",
"tags": ["μ±μ₯μμ€", "μ²μλ
", "κ°μ "]
}
]
# Very simple filtering based on query
if not query:
return books
filtered_books = []
query_lower = query.lower()
for book in books:
# Check if query appears in title, author, description or tags
if (query_lower in book["title"].lower() or
query_lower in book["author"].lower() or
query_lower in book["description"].lower() or
any(query_lower in tag.lower() for tag in book["tags"])):
filtered_books.append(book)
return filtered_books if filtered_books else books
# Function to display book cards
def display_book_cards(books):
for i in range(0, len(books), 3):
cols = st.columns(3)
for j in range(3):
if i + j < len(books):
book = books[i + j]
with cols[j]:
st.markdown(f'<div class="book-card">', unsafe_allow_html=True)
# Book image
st.image(book["image_url"], width=200)
# Title and English title
st.markdown(f'<div class="book-title">{book["title"]}</div>', unsafe_allow_html=True)
st.markdown(f'<div class="book-title" style="font-size: 0.9rem; color: #666;">{book["title_en"]}</div>', unsafe_allow_html=True)
# Author
st.markdown(f'<div class="book-author">{book["author"]}</div>', unsafe_allow_html=True)
# Tags
tag_html = ""
for tag in book["tags"]:
tag_html += f'<span class="tag">{tag}</span>'
st.markdown(f'<div>{tag_html}</div>', unsafe_allow_html=True)
# Description
st.markdown(f'<div class="book-description">{book["description"]}</div>', unsafe_allow_html=True)
st.markdown(f'<div class="book-description" style="font-style: italic; color: #777; margin-top: 0.5rem;">{book["description_en"]}</div>', unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
# Process search when button is clicked or when query is entered
if search_button or query:
if query:
books = get_book_recommendations(query)
st.markdown(
f'<h2 class="korean-text" style="margin-bottom: 1.5rem;">"{query}" κ²μ κ²°κ³Ό</h2>',
unsafe_allow_html=True
)
display_book_cards(books)
else:
st.info("κ²μμ΄λ₯Ό μ
λ ₯ν΄μ£ΌμΈμ (Please enter a search term)")
else:
# Show featured books when no search is performed
st.markdown(
'<h2 class="korean-text" style="margin-bottom: 1.5rem;">μΆμ² λμ | Featured Books</h2>',
unsafe_allow_html=True
)
featured_books = get_book_recommendations("")
display_book_cards(featured_books)
# Footer
st.markdown(
"""
<div style="text-align: center; margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ddd; color: #666;">
<p class="korean-text">Β© 2023 μ±
μΆμ² μμ€ν
| Book Recommender</p>
<p style="font-size: 0.8rem;">Created with Streamlit β’ Books data for demonstration purposes only</p>
</div>
""",
unsafe_allow_html=True
)
# Close main container
st.markdown('</div>', unsafe_allow_html=True)
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?