项目更多开发细节参见:
- Flask+Vue+Scrapy+Pandas+Echarts商品数据分析与可视化(一) 项目概述、动图演示
- 商品数据分析与可视化之 Scrapy 数据爬取记录(二)
- 商品数据分析与可视化之 flask 开发记录(三)
- 商品数据分析与可视化之 Vue 项目记录(四)
文章目录
1、执行 pipenv install xxx
命令安装包时报错
问题描述:
创建完虚拟环境后,执行 pipenv install flask
后出现报错。
报错如下:
RuntimeError: Failed to lock Pipfile.lock!
期间通过上网查找相关资料,尝试以下方式解决,均无效:
(1) 尝试修改 Pipfile
文件中的 url
:
[[source]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
verify_ssl = true
name = "tsinghua"
(2) 尝试在安装时添加国内镜像源:
pipenv install flask -i http://mirrors.aliyun.com/pypi/simple/
(3) 尝试在安装前删除 Pipfile.lock
文件。
—
结论:
初步推测问题为网络问题,可能需要借助代理或VPN来下载。
解决方案(待优化): 借助代理后成功下载依赖包。
2、执行 flask run
命令后报错
问题描述:
将文件名从 app.py
修改为 hello.py
后无法启动内置服务器。
报错如下:
Error: Could not locate a Flask application.You did not provide the "FLASK_APP" environment variable, and a "wsgi.py" or "app.py" module was not found in the current directory.
报错原因:
Flask 内部存在自动探测程序实例,会根据环境变量FLASK_APP
对应的值寻找对应的文件名(默认为 app
)。
因为修改了文件名,所以环境变量 FLASK_APP
对应的值也得一并修改。
解决方案:
(1) 方法一:手动设置环境变量
set FLASK_APP=hello
执行 flask run
命令后可成功启动内置服务器:
存在的问题: 每次打开项目都得设置该环境变量,且在 vscode 中无法成功设置。
(2) 方法二:借助 python-dotenv
设置环境变量 (推荐)
python-dotenv: Flask的自动发现程序实例机制如果发现安装了 python-dotenv
,则会在使用 flask run
或其他命令时自动从 .flaskenv
文件和 .env
文件中加载环境变量。
.flaskenv
文件(手动创建):用来存储和Flask相关的公开环境变量。
.env
文件(手动创建):用来存储包含敏感信息的环境变量。
当安装了python-dotenv时,Flask在加载环境变量的优先级是:手动设置的环境变量 > .env中设置的环境变量 > .flaskenv设置的环境变量。
① 安装 python-dotenv
:
pipenv install python-dotenv
② 在项目根目录下创建一个 .flaskenv
文件,文件内容如下:
FLASK_APP = hello
③ 现在重新执行 flask run
命令即可启动服务
3、Vue项目打包部署到服务器出现报错 net::ERR_ABORTED 404 (NOT FOUND)
问题描述:
vue项目执行打包命令 yarn run build
将项目打包后,生成 dist
文件夹,将该文件夹中的文件移动至 flask项目中的 static
文件夹中,并添加相应加载代码启动服务后访问 http://localhost:5000
出现报错。
flask项目中的 app.py
文件内容如下:
from flask import Flask,render_template
app = Flask(__name__,template_folder="static")
@app.route('/')
def index():
return render_template('index.html')
浏览器报错截图:
报错原因:
对应资源路径不完整导致的报错,经测试发现只需在 static/index.html
文件中在相应资源的路径前添加 static
即可拼接成完整路径。虽说此时访问 http://localhost:5000
已经不会出现以上报错,已经可以加载出基本框架,但文件中的图片,字体图标等资源的路径依然不完整,相应资源也无法成功加载,所以使用手动修改路径的方式并不合适。
手动修改路径:
<link rel="icon" href="static/favicon.ico">
<script defer="defer" src="static/js/chunk-vendors.b8da2c66.js"></script>
<script defer="defer" src="static/js/app.fbf03e7f.js"></script>
<link href="static/css/app.089e31f9.css" rel="stylesheet">
解决方案:
修改vue项目中的 vue.config.js
文件,并添加以下代码,重新打包项目,并将相应资源移动至 flask项目的 static
文件夹中即可。
module.exports = defineConfig({
// 检测当前环境 如果是生产环境,则追加 static
publicPath: process.env.NODE_ENV == "production" ? "static" : "/",
})
参考链接(转载):https://blog.csdn.net/qq_41115965/article/details/102678246
4、img标签中的图片无法正常显示
问题描述:
图片链接可以正常访问,但是写入img标签的src后无法正常显示。
浏览器控制台报错如下:
GET https://qny.smzdm.com/202311/16/65560e0d237903818.jpg_d200.jpg 403
报错原因:
原因未知,推测可能是目标网站设置的安全措施,以往使用其他网站的图片链接时并未出现该问题。
参考链接(转载):https://imququ.com/post/referrer-policy.html
解决方案:
在 public
文件夹中的 index.html
文件的 <head></head>
标签内写入如下内容:
<!DOCTYPE html>
<html lang="">
<head>
...
<meta name="referrer" content="no-referrer">
...
</head>
<body>
...
</body>
</html>
经测试,图片可正常加载,但是不知为何,即便将以上语句注释,图片依然可以正常显示,甚至打开新项目不追加以上语句也可以显示图片。
参考链接(转载):https://blog.csdn.net/weixin_43869439/article/details/106621186
5、使用 vue-socket.io 出现跨域问题
项目环境:
前端:Vue2
后端:Flask
问题描述:
为了实现前后端的双向数据通信,我在前端安装了 vue-socket.io
,后端安装 flask_socketio
(1) 前端安装 vue-socket.io
:(此处安装当前最新版本)
yarn add vue-socket.io@latest
(2) 后端安装 flask_socketio
:
pipenv install flask_socketio
安装后对应版本:
vue-socket.io
:3.0.10
flask_socketio
:5.3.6
前端组件内代码如下:
<template>
<button @click="startSpiderBtn">获取数据</button>
<div>{{ output }}</div>
</template>
<script>
export default {
name: "SpiderGetData",
data() {
return {
output: '',
}
},
sockets: {
},
mounted() {
this.sockets.subscribe('console_output', (msg) => {
this.output += '\n' + msg.data;
})
},
methods: {
startSpiderBtn() {
this.$socket.emit("start_spider","start");
},
}
}
</script>
前端 main.js
文件部分代码如下:
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
Vue.use(new VueSocketIO({
debug: true,
connection: "http://localhost:5000",
}))
后端代码:
from flask import Flask,request
from flask_socketio import SocketIO
app = Flask(__name__)
socketio = SocketIO(app,cors_allowed_origins='*')
@socketio.on('start_spider')
def start_spider(msg):
print(msg)
socketio.emit("console_output",{"data": "test"})
if __name__ == "__main__":
socketio.run(app,debug=True)
报错截图如下:
浏览器报错截图:
后端报错截图:
尝试解决:
期间通过上网查找各种资料,尝试以下方法解决,均无效:
(1) 安装 flask_cors
,进行后端的跨域配置
pipenv install flask_cors
更新后端代码,app.py
文件追加以下代码:
from flask_cors import CORS
CORS(app,cors_allowed_origins="*")
(2) 前端修改 vue.config.js
配置文件
修改内容如下:
module.exports = defineConfig({
devServer: {
proxy: {
'/socket.io': {
target: 'http://localhost:5000',
ws: true,
changeOrigin: true
},
'/sockjs-node': {
target: 'http://localhost:5000',
ws: false,
changeOrigin: true
},
}
}
})
(3) 查看浏览器响应标头,如图所示,表明跨域相关的配置修改成功,但是控制台却依然报错
浏览器响应标头:
报错原因:
虽说浏览器报错显示的问题为跨域的问题,但是期间通过各种方式进行相关的配置均无效,因为主要问题在 vue-socket.io
相关的依赖包与 flask_socketio
包中的 SocketIO 版本不兼容。
突破口在于后端的报错:
The client is using an unsupported version of the Socket.IO or Engine.IO protocols (further occurrences of this error will be logged with level INFO)
一开始以为是 EngineIO 版本太低的问题,企图去修改 node_modules/socket.io-client/package.json 中的 engine.io-client
版本,之后执行 yarn install
,但遗憾的是此方法无效,最后无意间找到了网友分享的一篇博客才得知需升级 socket.io-client
包的版本(参考链接详见底部)。
安装 vue-socket.io
时对应依赖包的版本,如图所知,socket.io-client
包的版本太低,需升级至3.x.x版本才可兼容。
各版本兼容性:
各版本兼容性参见:https://flask-socketio.readthedocs.io/en/latest/intro.html#version-compatibility
解决方案:
方法一:
更新 socket.io-client
:
yarn add socket.io-client@3
执行以上命令下载3.x.x版本,经实践发现更新完 socket.io-client
包版本后,engine.io-client
包版本自动升级到了4.x.x版本。
方法二:
修改 node_modules/socket.io-client/package.json 中的 socket.io-client
版本,如图所示:
之后执行命令更新:
yarn install
在升级完依赖包后,修改前端 main.js
文件中的内容如下:
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
import SocketIO from 'socket.io-client'
Vue.use(new VueSocketIO({
debug: true,
connection: SocketIO("http://localhost:5000"),
}))
-------------
后端 app.py
完整代码如下:
from flask import Flask,request
from flask_socketio import SocketIO
from flask_cors import CORS
app = Flask(__name__)
CORS(app,cors_allowed_origins="*")
socketio = SocketIO(app,cors_allowed_origins='*')
@socketio.on('start_spider')
def start_spider(msg):
print(msg)
socketio.emit("console_output",{"data": "test"})
if __name__ == "__main__":
socketio.run(app,debug=True)
再次感谢网友分享的解决方法,详情参考链接(转载):https://www.cnblogs.com/code1992/p/14336803.html
6、使用 pipenv 创建的虚拟环境下调用 scrapy 创建的爬虫项目出现 python 环境不同导致的报错
项目环境:
前端:Vue2
后端:Flask(运行在 pipenv
创建的 python 虚拟环境下)
项目结构:
项目背景:
我使用 scrapy 命令提前创建了两个爬虫项目,并复制到 flask 项目中,期望通过 flask 项目调用这两个爬虫项目。
在 pipenv
创建的虚拟环境下,我安装了 flask
、pandas
等包。
在 scrapy 创建的项目中,我使用的主要的包有 scrapy
、pandas
等。
问题描述:
在 flask 项目中调用 Scrapy 创建的爬虫项目时,出现某个包不存在的报错,但该包明明已经安装在虚拟环境中。
报错如下:
ModuleNotFoundError: No module named 'pandas'
尝试解决:
我感到疑惑,明明已经在虚拟环境中安装了 pandas
,并且已经配置了 flask 项目使用的 python 环境为 pipenv
创建的虚拟环境。
尝试进入 scrapy_basic 项目查看所运行的环境:
如图可知,程序已经处于虚拟环境下。
后续再次执行还是报错,但是留意到了报错的文件路径居然是全局环境下的 python,并非虚拟环境下的 python。
File "c:\users\asus\appdata\local\programs\python\python39-32\lib\importlib\__init__.py",line 127,in import_module
return _bootstrap._gcd_import(name[level:], package, level)
报错原因:
由于我是在全局环境下通过 scrapy 命令创建的项目,所以使用的是全局环境下的 python,但是全局环境下我并没有安装 pandas
,所以才会报错。此时,只需要在全局环境下也安装 pandas
,即可解决,但是这并不是一个好方法,我希望 scrapy_basic 项目使用的也是 pipenv
创建的 python 虚拟环境,而不是全局的 python 环境。
解决方案:
现在的问题是怎么把 scrapy_basic 项目所使用的环境转为 pipenv
创建的 python 虚拟环境。
我在 pipenv
创建的 python 虚拟环境下安装 scrapy
、pandas
:
pipenv install scrapy
pipenv install pandas
再次通过 flask 项目调用 scrapy_basic 项目爬取后发现,环境居然自动转为 pipenv
创建的 python 虚拟环境,推测可能是因为之前虚拟环境中没有安装 scrapy
,所以自动向上查找了,并使用了全局环境下的 scrapy
。
不过又出现了新的报错:
ImportError: DLL load failed while importing _sqlite3: 找不到指定的模块。
通过上网查找资料,得知可在 D:\Anaconda\Library\bin
目录下复制一份 sqlite3.dll
文件至虚拟环境下 D:\桌面\GoodsDataAnalysis\GoodsFlask\PIPENV_VENV_IN_PROJECT\GoodsFlask-S_qAB8d7\Scripts
即可解决。
期间我尝试复制全局环境下的 sqlite3.dll
文件似乎无法解决,后复制 Anaconda
下的 sqlite3.dll
文件才最早解决。
参考链接+其他解决方案(转载):https://blog.csdn.net/sayWhat_sayHello/article/details/114464801
接着,又出现了新的报错(可能因为我在爬虫程序中将数据写入 excel 文件,所以才出现以下报错,程序其实已经可以正常爬取了):
ModuleNotFoundError: No module named 'openpyxl'
这个问题比较好处理,只需在虚拟环境下安装 openpyxl
即可解决:
pipenv install openpyxl
7、在 flask 项目中使用 axios 上传文件时出现的问题
项目环境:
前端:Vue2
后端:Flask
问题描述:
前端在使用 axios
上传文件时,服务端出现415报错。
前端代码如下:
<template>
<div class="data_shape_tools_bar">
<ul>
<input type="file" ref="file_input" hidden @change="fileUploadData">
<!-- 上传文件 -->
<li @click="fileUpload">上传</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "DataShapeToolsBar",
data() {
return {
uploadFiles: null,
}
},
methods: {
// 点击input框,弹出文件选择对话框
fileUpload() {
this.$refs.file_input.click();
},
// 获取文件信息
fileUploadData(e) {
this.uploadFiles = e.target.files[0];
},
// 点击按钮上传文件
uploadFile() {
let formData = new FormData();
formData.append('file', this.uploadFiles);
// 打印文件信息,检查是否添加成功
console.log(formData.get('file'));
axios.post('http://localhost:8080/api/fileUpload', formData,
{
headers: {
'Content-Type': 'multipart/form-data'
},
}
).then(
response => {
console.log(response.data);
},
error => {
console.log(error);
}
)
},
}
}
</script>
后端代码如下:
from flask import Flask,request
from flask_cors import CORS
app = Flask(__name__)
CORS(app,cors_allowed_origins="*")
# 文件上传
@app.route('/api/fileUpload',methods=['POST'])
def file_upload():
print(request.headers.get('Content-Type'))
print(request.json)
file = request.files.get('file')
print(file)
return '1'
报错截图:
尝试解决:
上网查找资料得知该报错的主要原因是前端发送的资源的类型与后端所要求接收的资源类型不一致。
尝试修改 axios
请求中的 Content-Type
:
...
axios.post('http://localhost:8080/apifileUpload', formData,
{
headers: {
// 'Content-Type': 'multipart/form-data'
'Content-Type': 'application/json'
},
}
).then(...)
再次上传文件后发现,确实不报错了,但是上传的文件为空,后端根本没接收到。
后端控制台打印信息如下:
application/json
{'file': {}}
None
报错原因:
在多番尝试解决均无效的情况下,反复检查了前端代码,感觉并无任何问题,猜测问题可能出现在后端,注释掉其他代码后,只进行 return
语句的简单返回,发现并无报错,更加坚定了自己的看法,最终,发现了问题所在。
解决方案:
修改后端代码如下:
from flask import Flask,request
from flask_cors import CORS
app = Flask(__name__)
CORS(app,cors_allowed_origins="*")
# 文件上传
@app.route('/api/fileUpload',methods=['POST'])
def file_upload():
print(request.headers.get('Content-Type'))
# 注释或删除该打印语句!!!
# print(request.json)
file = request.files.get('file')
print(file)
return '1'
原来问题出在语句 print(request.json)
,推测可能这样写了之后,后端会去获取请求中的 json 类型的数据,而上传的文件并非 json 类型的数据,所有才导致报错。
还有一点,关于前端 axios
上传文件时是否需要添加 headers: {'Content-Type': 'multipart/form-data'}
的问题,经测试,可写可不写均可上传文件。
8、后端发送至前端的 json 数据变成 string 类型的数据
项目环境:
前端:Vue2
后端:Flask
问题描述:
我在前端通过 axios.post
上传文件至后端,希望后端读取该文件内的数据并对数据做适当的处理后再将其返回至前端,现在有一个 smzdm.xlsx
文件和 scrapy_smzdm.xlsx
两个文件。但让我感到不解的是,我上传文件 smzdm.xlsx
至后端,处理后可正常返回 json 类型的数据,但是 scrapy_smzdm.xlsx
返回的却是 string 类型的数据,明明两个数据的格式都是一致的,只是内容不一样。
前后端部分代码:
前端文件上传部分代码如下:
...
let formData = new FormData();
formData.append('file', this.uploadFiles);
// 打印文件信息
console.log(formData.get('file'));
axios.post('http://localhost:8080/api/fileUpload', formData).then(
response => {
// 查看响应的数据类型
console.log(typeof response.data);
},
error => {
console.log(error);
}
)
后端代码如下:
# 文件上传
@app.route('/api/fileUpload',methods=['POST'])
def file_upload():
file = request.files.get('file')
# 读取上传的文件
df = pd.read_excel(file)
# 存储所有关于上传的文件的信息
uploadFileInfoObj = {}
# 将其转换为字典类型的数据
upload_file_data_list = df.to_dict(orient='records')
# df.to_dict 会将数据中的列表类型的数据转换成字符串类型的数据,
# 故需使用 eval() 再做进一步处理,此处省略 ......
uploadFileInfoObj["upload_file_data_list"] = upload_file_data_list
return jsonify(uploadFileInfoObj)
文件对比:
smzdm.xlsx
文件上传后,后端响应的内容(数据类型符合预期,为 json 类型的数据):
scrapy_smzdm.xlsx
文件上传后,后端响应的内容(数据类型为 string 类型,不符合预期):
两个文件上传后,对应的文件信息及后端响应的数据类型:
查看浏览器响应体中 Content-Type:application/json
,服务端返回的是 json 类型数据,应该是没错的才对:
报错原因:
报错原因未知。两个文件的主要区别在于内容,其次是文件大小、名称,不清楚是什么因素影响的,但好在还有解决方案。
解决方案:
通过上网寻求解决方案得知,可使用 JSON.parse()
进行转换,但是在使用的过程中,可能因为响应的数据可能不是有效的 json 字符串,导致了报错:
针对非有效的 json 字符串,最终使用 eval()
方法成功转换,前端代码如下:
axios.post('http://localhost:8080/api/fileUpload', formData).then(
response => {
console.log(typeof response.data);
let response_data = response.data;
if(typeof response_data == 'string') {
// 通过该方法,成功转化数据
response_data = eval("(" + response_data + ")");
}
console.log(response_data);
},
error => {
console.log(error);
}
)