构建开源问卷
问卷调查是社会科学中最常用的研究方法之一,可以帮助研究者快速且有效地了解人群的观点、态度。同时,问卷还可以用于实验研究中被试信息的收集,例如使用问卷题项测量被试在实验前后的观念变化。在现有的计算社会科学研究技术条件下,基于在线问卷还可以实现在线实验。
研究中最常使用的在线问卷一般是基于商业化平台的,例如Qualtrics、Credamo、问卷星(Sojump)等。这些平台虽然能提供简单高效的问卷开发工具,但是在内容自由度上做了一定限制。如果希望在线实验中包含较复杂的任务,这些平台就很难满足开发需求。或者开发成本会变得很高昂,例如Qualtrics开启Javascript脚本需要购买300磅一个月的高级权限。
为了实现更多样的研究目的,也为了降低研究成本,基于开源工具搭建问卷就成了一个非常现实的解决方案。
通常,开发一个网站需要开发者具备实现前端界面设计、后端逻辑控制以及硬件服务器部署的能力。全面掌握这些能力绝对不是一页笔记能解决的,有没有什么上手简单、功能全面、部署容易的实现方法呢?
有的兄弟,有的,这么好的方案当然是不止一个。本文介绍基于Python中的Streamlit库搭建开源问卷。(部分材料来自于Streamlit文档,文档永远是学习开发的最好材料~)
使用以下命令安装本文所需的库。
pip install streamlit
Streamlit的原理及常用控件
Streamlit是一个基于Python的轻量web开发工具,它的优点是简单快捷,适合于小团队内部工具的开发或者交付客户的系统demo。
调查问卷具有并发访问数少,总访问次数有限的特点,因此Streamlit能够满足一个开源问卷的搭建。
Streamlit的运行原理可以总结为顺序执行+按需要进行缓存。具体来说:
- Streamlit app 本质是一个按从上到下顺序执行的 Python 脚本。
- 每当用户通过浏览器选项卡访问应用程序时,系统都会启动一个新会话(session),并从头开始执行脚本。
- 在脚本执行过程中,Streamlit 会实时将输出内容渲染到浏览器界面。
- 当用户与界面中的任何交互部件(widget)进行操作时,都会触发脚本的重新执行,同时 Streamlit 会相应地更新浏览器中的渲染内容。
- 通过缓存机制,应用程序可以避免重复计算开销较大的函数,从而确保状态更新保持高效。
- 会话状态(session state)功能支持在脚本重新运行期间(例如组件被点击时)保存关键信息。
- Streamlit 支持多页面设计,这些页面既可以作为独立脚本存放在
pages
文件夹中,也可以在主脚本中以函数形式进行定义。
Streamlit app 遵循Server-Client的架构,Server是Python后端,通过streamlit run APP.py
启动,Client是浏览器前端。
因此,这意味着部署app的服务器承担全部数据的计算和存储任务,需要谨慎考虑并发请求数量;app也无法主动读取用户设备上的文件,需要依赖st.file_uploader
来实现上传;这也意味着app无法在用户的设备上打开/跳转指定的程序。但是我们可以在服务器上部署数据库存储用户答卷,或者在app中嵌入远程数据库连接的功能。
Streamlit app 使用会话状态来存储变量,只要本次会话还未结束,那么这些变量就不会丢失,这是我们能用Streamlit开发开源问卷的基础条件。
在本项目中,我们会用到以下几个组件:
st.button
,按钮,可以用于实现翻页以及提交功能。st.radio
,单选,用于实现选择题。st.selectbox
,选项框,在选项数量多或字数多时可以用该组件代替st.radio
,默认是折叠状态,界面更整洁。st.slider
,滑动条,用于输入数值,有变体st.select_slider
。st.text_input
,单行的文本输入框。st.text_area
,多行的文本输入区域。
控件组合实现简单问卷
页面基本设置
导入Streamlit并做一些页面参数的设置:
import streamlit as st
# 页面基本信息,page_title对应浏览器上标签页的标题,page_icon则是logo,可以用:emoji:或单个emoji字符
st.set_page_config(page_title="Survey", page_icon=":happy:")
我们想实现一个可以翻页的问卷,可以考虑Streamlit原生支持的多页app。但这有一点不好,页面切换时url会发生变化,这不利于我们控制作答流程。
因此,可以使用一个鲁莽的方式,我们在页面状态上定义页数,在app主体上使用if语句来决定在指定页上出现的内容。
if "page_num" not in st.session_state:
st.session_state.page_num = 0
if st.session_state.page_num == 0:
# 这里是页面呈现信息
pass
if st.session_state.page_num == 1:
# 这里是页面呈现信息
pass
# ...
加入控件
测试...
几行代码几次点击部署为web应用
更深入的开发...
- 复杂实验交互
- 远程数据库连接
- 自定义CSS样式
- 复杂控制逻辑