StreamLit中的多个函数

0 投票
1 回答
39 浏览
提问于 2025-04-12 23:30

我正在尝试创建一个网络爬虫应用,它可以从某个房地产网站获取数据,并返回一个包含价格和城市的数据集。在返回数据集后,脚本应该包含一个滑块,让我可以设置价格并进行筛选。但是,每当我稍微移动滑块时,StreamLit都会重新运行脚本,导致网络爬虫、数据清理和结构化的过程重新开始。我尝试使用session_state,但感觉我还是缺少了什么,因为它仍然会重新加载整个内容。我该怎么办呢?

starter = st.checkbox('Begin Data Gathering')
if starter:
    def dataframe_creation(linker):
      link = linker
      br = requests.get(link)
      page = br.content
      soup = bs(page, 'html.parser')
      #PRICES
      advert = soup.find_all("div", attrs={"class": "advert__content-header"})
      price_list = [] ##################FINAL COLUMN
      import re

      html_code = str(advert)
      matches = re.findall(r'<b>(€)</b>(.*?)</span>', html_code)

      for match in matches:
          euro = match[0]
          number = match[1]
          number = number.split(" ")[0]
          number22 = number.replace(".", "")
          price_list.append(number22)
      # CITY LOCATION
      city = soup.find_all("div", attrs={"class": "advert__content-place"})
      city2 = str(city)
      city3 = re.findall(r'>(.*?)</div>', city2)
      city_list = [] #### THIS DATA
      for z in city3:
        kols = z.split(",")[0]
        city_list.append(kols)
      finale1 = pd.DataFrame({
             "Prices": price_list,
             "Cities": city_list})
      finale1['Prices'] = finale1['Prices'].astype(int)
      finale1
slider = st.slider("Price in Euros", min_value=200, max_value=5000, step=10)
finale1[finale1['Prices'] <= int(slider)]

1 个回答

0

你缺少的部分叫做缓存。正如你所说,每当你的应用状态发生变化,比如你在Streamlit中放置的任何组件,Streamlit就会在服务器上重新渲染页面,然后把它发送回客户端(浏览器)。这是正常的行为。

为了提高这些重新渲染的效率,Streamlit有两种机制,类似于某种记忆功能,分别是会话状态和缓存。简单来说:

  • 会话状态保存了已知的信息。它就像一个字典,你可以在里面存储一些变量,这些变量在重新渲染之间会保持不变,比如按钮的状态、计数器等等。
  • 缓存保存了已经完成的操作。它使用一个装饰器和一种叫做记忆化的常见技术,来知道哪些代码部分已经执行过且没有变化,从而避免重复执行。这里有相关文档 https://docs.streamlit.io/library/advanced-features/caching

举个例子:如果你有一个Streamlit页面,里面有读取非常大csv文件的代码,每次你通过操作Streamlit元素改变页面状态时,它都会从头到尾执行页面代码,重新读取csv,这样会让你的应用变得很慢。

import pandas as pd

<page layout>
df = pd.read_csv("some.csv") # Rexecuted on every action.
<page layout>

为了纠正这种行为,我们把这个耗时的过程封装在一个函数里,并添加一个装饰器(就这样)。新的代码看起来会是这样的。

import pandas as pd

<page layout>
@st.cache_data
def csv_reader(path):
    return pd.read_csv("some.csv")

df = csv_reader(path="some.csv")
<page layout>

虽然代码稍微多了一点,但效果会好很多。现在第一次渲染页面时,csv_reader函数会被执行。执行结束后,会生成一个包含函数名称和传入值的哈希值,并将这个(哈希,函数结果)存储在一个内部字典中作为键值对。下次页面重新渲染时,Streamlit调用这个函数时,会先在内部字典中查找是否有用过这些参数调用过这个函数,如果有,它会直接从字典中取出结果,而不需要再次执行这个函数。这样你就避免了再次执行这个耗时的部分。

这种方法在函数总是用不同参数调用或者有随机元素时就不太有用了,但我相信在你的情况下并不是这样。我没有你完整的代码来测试,但我相信你的问题可以通过用缓存装饰器包装你的函数,并明确返回数据框来解决,比如:

@st.cache_data
def dataframe_creation(linker):
    ...
    return finale1

finale1 = dataframe_creation(linker=some_data)

撰写回答