如何实现R Shiny按钮跳转至新页面展示yfinance季度财务表?
解决方案:在R Shiny中实现多页面展示财务数据
核心修改方向
- 用
shiny.router实现应用内页面跳转,满足"新页面展示数据"的需求 - 重构数据获取逻辑,用响应式变量存储财务数据,替代原代码的全局环境赋值方式
- 修复原代码中的SQL注入风险、变量定义顺序问题
完整可运行代码
先确保安装所需依赖包:
install.packages(c("shiny", "DT", "RSQLite", "reticulate", "shiny.router", "shinyautocomplete"))
代码实现
library(shiny) library(DT) library(RSQLite) library(reticulate) library(shiny.router) library(shinyautocomplete) # 提前加载美股代码列表,避免UI加载时找不到变量 us_tickers <- stockSymbols()$Symbol # 预初始化Python环境,避免重复导入库 py_run_string(" import yfinance as yf import pandas as pd pd.set_option('display.max_columns', None) ") # ---------------------- 页面UI定义 ---------------------- # 主页面(Ticker管理) main_page <- fluidPage( titlePanel(h1("Securities Analytical Intelligence", align = "center")), sidebarLayout( sidebarPanel( autocomplete_input("new_ticker", "Enter Ticker", us_tickers, placeholder = "Type to search..."), actionButton("add_ticker", "Add Ticker"), actionButton("create_datablock", "Create Datablock") ), mainPanel( DTOutput("ticker_table") ) ) ) # 数据展示页面 data_page <- fluidPage( titlePanel(h1("Quarterly Financial Data", align = "center")), uiOutput("financial_tables") ) # 配置路由规则 router <- make_router( route("/", main_page), route("/data", data_page) ) # 应用总UI ui <- fluidPage( router$ui ) # ---------------------- 服务器逻辑 ---------------------- server <- function(input, output, session) { # 初始化路由 router$server(input, output, session) # 数据库连接(应用关闭时自动断开) con <- dbConnect(RSQLite::SQLite(), "tickers.db") onStop(function() { dbDisconnect(con) }) # 创建tickers表(添加UNIQUE约束避免重复插入) dbExecute(con, "CREATE TABLE IF NOT EXISTS tickers (ticker TEXT UNIQUE)") # 响应式变量存储应用数据 app_data <- reactiveValues( tickers = data.frame(ticker = character()), financial_data = list() ) # 从数据库加载ticker列表 load_tickers <- function() { app_data$tickers <- dbGetQuery(con, "SELECT * FROM tickers") } # 初始加载数据 load_tickers() # 添加Ticker(用参数化查询避免SQL注入) observeEvent(input$add_ticker, { new_ticker <- trimws(input$new_ticker) if (nchar(new_ticker) > 0) { tryCatch({ dbExecute(con, "INSERT INTO tickers (ticker) VALUES (?)", params = list(new_ticker)) load_tickers() updateAutocompleteInput(session, "new_ticker", value = "") # 清空输入框 }, error = function(e) { showNotification("该Ticker已存在或输入无效", type = "error") }) } }) # 删除Ticker observeEvent(input$ticker_table_cell_clicked, { info <- input$ticker_table_cell_clicked if (!is.null(info$value) && info$col == 1) { dbExecute(con, "DELETE FROM tickers WHERE ticker = ?", params = list(info$value)) load_tickers() } }) # 渲染Ticker表格 output$ticker_table <- renderDT({ datatable( app_data$tickers, options = list(dom = 't', paging = FALSE), colnames = c("Ticker"), selection = "none", rownames = FALSE ) }) # 获取财务数据(返回列表存储,避免全局赋值) get_financial_data <- function(tickers) { financial_list <- list() for (ticker in tickers) { py_run_string(paste0( "ticker_obj = yf.Ticker('", ticker, "')\n", "financial_data = ticker_obj.quarterly_financials" )) # 转成R数据框并添加Ticker标识 r_financial <- py$financial_data r_financial$Ticker <- ticker financial_list[[ticker]] <- r_financial } return(financial_list) } # 点击Create Datablock后的逻辑 observeEvent(input$create_datablock, { tickers <- app_data$tickers$ticker if (length(tickers) == 0) { showNotification("请先添加至少一个Ticker", type = "warning") return() } # 获取并存储财务数据 app_data$financial_data <- get_financial_data(tickers) # 跳转到数据展示页面 navigate_to("/data") }) # 在数据页面生成所有财务表格的UI output$financial_tables <- renderUI({ if (length(app_data$financial_data) == 0) { return(tags$p("暂无财务数据,请先在主页面创建Datablock")) } # 为每个Ticker生成独立表格 table_outputs <- lapply(names(app_data$financial_data), function(ticker) { tagList( h3(paste("Ticker:", ticker)), DTOutput(paste0("table_", ticker)) ) }) do.call(tagList, table_outputs) }) # 渲染每个财务表格 observe({ lapply(names(app_data$financial_data), function(ticker) { output[[paste0("table_", ticker)]] <- renderDT({ datatable( app_data$financial_data[[ticker]], options = list(scrollX = TRUE, dom = 'ltip'), rownames = TRUE ) }) }) }) } # 运行应用 shinyApp(ui, server)
关键改进说明
- 页面跳转:通过
shiny.router实现两个页面的切换,点击按钮后用navigate_to("/data")直接跳转,符合"新页面展示"的需求。 - 数据管理:用
reactiveValues$financial_data统一存储所有Ticker的财务数据,替代原代码的assign操作,确保Shiny能跟踪数据变化,正确渲染UI。 - SQL安全:将原代码的字符串拼接SQL改为参数化查询,彻底避免SQL注入风险。
- 效率优化:提前初始化Python环境并导入yfinance,避免每次循环重复导入库,提升运行效率。
- 用户体验:添加重复Ticker提示、空Ticker提示、清空输入框等细节优化。
内容的提问来源于stack exchange,提问作者Adin
相关产品推荐
相关产品推荐

