最近学习了SpringBoot、Vue和Python爬虫,想着做一个小项目连连手,于是就打算做一个查询录取信息的项目。正好师弟师妹们也要查询录取通知书快递单号,本项目可以用来给新生查询录取信息。做这个项目用了2天时间,遇到的问题也不少,就当是一次学习吧。
一、编写爬虫获取录取信息
这一部分要用到Python来实现,具体使用的技术是requests和xpath,通过模拟考生的考生号和姓名向学校查询录取信息的接口发送请求,将返回的数据进行解析。将解析到的有效的数据放入一个字典中,再转换为json字符串输出到控制台上,输出到控制台上的原因是,后面要通过Java代码来调用这个爬虫程序,我们要在Java程序中读取控制台的输出信息来做进一步处理。
爬虫的具体细节我就不描述了,我做这个只是为了方便他人查询,而不是用作什么非法用途。
具体实现代码在这里:admission_query.py
二、编写前端页面
最近学了Vue,于是打算使用Vue构建录取信息查询页面,为了使页面不要过于丑陋,添加了Bootstarp依赖来美化页面。同时使用Axios插件,将用户输入的考生号和姓名以异步请求的方式传递给后端来查询录取信息。下面对具体步骤做一个描述:
1.在src/interface/index.js中配置后台请求地址,其中windows_api是开发环境的后台地址,linux_api是生产环境的后台地址,项目在开发过程中使用开发环境,真正上线给用户使用时,使用生产环境,因此在项目打包上线之前需要及时修改后台地址为生产环境地址,否则上线后会无法访问后台。
//配置全局访问接口地址 let windows_api = "http://localhost:8100";//开发环境 let linux_api = "http://luqu.anonyeast.top";//生产环境 let api = { windows_api,linux_api } export default api
2.在src/main.js中导入整个项目要用到的依赖,在后期直接调用
import Vue from 'vue' import App from './App.vue' import router from './router' import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/js/bootstrap.min' import axios from "axios" import api from "./interface/index" import FastClick from 'fastclick' Vue.config.productionTip = false Vue.prototype.$http = axios; //配置ajax请求发送工具 Vue.prototype.$api = api.linux_api; //配置后台地址,开发环境选windows,生产环境选linux FastClick.attach(document.body); //解决移动端单击延迟 new Vue({ router, render: h => h(App) }).$mount('#app')
3.在src/views创建录取信息查询组件Form.vue,并在src/router/index.js中进行路由绑定,用于用户访问。
import Vue from 'vue' import VueRouter from 'vue-router' import Form from "../views/Form"; import Touch from "../views/Touch"; Vue.use(VueRouter) const routes = [ { path: '/', name: 'Form', component: Form } ] const router = new VueRouter({ routes }) export default router
4.从BootStarp4中文文档网站的组件-表单中获取合适的表单代码粘贴在Form.vue中。
5.对复制的表单进行修改,做成查询录取信息需要的样子,并通过v-model属性对文本输入框的内容与Vue对象中的data进行双向绑定。
6.在Form.vue的methods中编写查询录取信息的请求,与查询按钮的click(单击)事件进行绑定,使用axios发送请求到后台,并将返回结果赋值给data中的录取信息(result_info)。
7.对Form.vue的标题、导航栏、查询次数等元素和方法进行完善,造就了以下的页面效果。
8.在src/views中创建Info.vue组件,用于展示查询结果。该组件要嵌套到Form.vue中,并且共享Form.vue中的查询结果(result_info)数据,因此要在Form.vue中注册Info组件,并传递数据。
1.在Form.vue中进行注册并嵌套组件 <!--Info组件,当查询有结果时显示。传递result_info给该组件--> <Info :queryResult="result_info" v-show="showInfo"/> //在父组件注册子组件 components: { Info } 2.在Info.vue中获取录取信息数据 <script> export default { name: "Info", props:['queryResult'] } </script>
9.从BootStarp4中文文档网站的组件-列表组中获取合适的列表页面效果粘贴在Info.vue中,并通过插值表达式显示出录取信息。
10.以上两个文件的具体实现代码:Form.vue Info.vue
三、编写后端服务
后端使用SpringBoot框架,采用了标准的三层架构(控制层、业务层、持久层)开发。
后端用到的所有依赖坐标:pom.xml
1.实体类(pojo):将录取结果信息定义为一个实体类——Admission类,在数据传递的过程中直接以对象形式传递。使用了Lombok免去setter/getter等方法的书写。
@Data @NoArgsConstructor @AllArgsConstructor @TableName("njtc_admission") public class Admission { @TableId(type = IdType.AUTO) private Integer id;//id private String name;//姓名 private String id_card;//身份证 private String gender;//性别 private String nationality;//民族 private String admission_time;//报道时间 private String professional;//录取专业 private String stu_type;//考生类型 private String express;//录取通知书EMS单号 }
2.控制层(controller):编写前端控制器,接收用户点击查询按钮后,通过axios发送到后台的请求,请求当中携带了请求地址和用户输入的数据(考生号、姓名、考生号类型),根据请求地址的不同进入到不同的处理器方法中,将拿到的数据传递给服务层的相关方法进行录取信息查询。
@Resource(name = "admissionService") private AdmissionService admissionService; /** * 查询并保存 * @param candidateNumber 考生号 * @param studentName 姓名 * @param searchType 考生号类型 * @return 录取信息 */ @PostMapping("/searchAndSave") public Admission findAll(String candidateNumber, String studentName, Integer searchType) { Admission admission = admissionService.findAll(candidateNumber, studentName, searchType); if (admission != null) { admissionService.addToDB(admission); } admissionService.queryCount(); return admission; }
3.服务层(service):编写查询录取信息的业务逻辑。这里比较麻烦,因为获取录取信息是使用Python写的,也就是文章开头写到的编写爬虫获取录取信息。要在Java中调用Python代码,就得用到Runtime
类
的exec
方法,用于通过命令直接执行Python程序,并将Python程序输出的内容读取出来。
具体思路是:先获取python代码的路径,然后封装查询参数,执行命令,通过输入流获取python程序输出的信息,将这些信息封装为Admission实体对象,返回给控制层。
但是我在这一层遇到了很多的麻烦,基本上都是与python调用相关的,将在最后进行总结。
服务层的完整实现代码:AdmissionServiceImpl.java
4.持久层(dao):将查询结果存储到数据库中。本层使用了MyBatis-Plus作为持久层核心技术,由于MyBatis-Plus已经高度封装了很多功能,所以不需要我们写任何代码,只需要定义一个接口并继承BaseMapper
类即可。
@Repository public interface AdmissionDao extends BaseMapper<Admission> {}
至此,本项目就算是写完了,同时启动前端和后端的服务,就可以在本机电脑上进行查询了,但如果要让别人可以访问,就必须将项目部署到服务器上,在讲解部署到服务器之前,我先将整个过程遇到的不少问题进行总结。
四、前端遇到的问题
1.在组件中,明明定义了data,但是在v-model处进行双向绑定时,代码不高亮。如图所示:
在上图中,按理说"query_info"应该也显示成紫色才对,但这里是黑色的,并提示Unresolved variable or type query_info(没有找到query_info这个变量或类型)。经过一番摸索后发现,我在data的返回值中,将数据定义在了return语句的小括号里面,导致了v-model不识别我定义的数据。正确的做法是去掉下面这一对框起来的小括号。
2.axios发送post或put请求时会自动将请求头的Content-Type设置为"application/json",这会导致考生号、姓名、查询类型这三个参数被封装为1个json对象传递给后台,但我在后台并没有对这三个参数封装为一个实体类,而是分开写的(如下),这样后台接收参数时就只能接收到一个json对象的参数,无法分别为这3个参数赋值,导致直接报错。
public Admission findAll(String candidateNumber, String studentName, Integer searchType)
解决方法是,使用js的URLSearchParams
对象来封装参数,将这个对象作为axios的post请求参数传递,代码如下:
query() {//查询方法 //定义查询参数 let params = new URLSearchParams(); params.append('candidateNumber', this.query_info.candidateNumber); params.append('studentName', this.query_info.studentName); params.append('searchType', this.query_info.searchType); //向后台发送post请求查询录取信息 this.$http.post(this.$api + "/searchAndSave", params)
3.项目部署到服务器后,无法访问后台的问题。
这个问题的原因是,项目部署之前我在本地运行,后台地址使用的是windows_api,这样的话当用户访问时,一点击查询按钮,访问的将是localhost:8100,然而在用户的电脑上怎么可能有在localhost:8100部署后台,导致无法连接到后台,查询不到任何结果。
解决方法,在项目打包上线之前,将main.js中的Vue.prototype.$api设置为linux_api,这样用户访问时,访问的才是服务器的项目后台。
五、后端在java调用python代码遇到的问题
1.如何获取当前操作系统?
使用System.getProperty("os.name")
获取当前操作系统的名称,根据不同的操作系统做出不同的行为。
2.如果想要通过Class对象的getResource方法动态获取python爬虫代码的路径,需要对获取到的路径进行处理。
具体表现在:Windows系统(开发环境)中,获取到的文件路径是这样的
/D:/Development/Developer/Java/Java-Projects/SpringbootWebProjects/AdmissionQuery/target/classes/top/anonyeast/service/impl/admission_query.py
这样的路径是无法在控制台执行的,必须要去除字符串开头的第一个"/"才是正确的Windows路径格式。
而在Linux系统(生产环境)中,获取到的文件路径是这样的
file:/www/wwwroot/luqu.anonyeast.top/admission_query.py
这样的路径同样是不规范的,必须要去除字符串开头的"file:"前缀,才是正确的格式。
去除前缀方法可以使用String类的replaceFirst
方法。
3.在Windows系统中接收Python程序输出的内容出现中文乱码的问题
由于Windows版本的Python在控制台中的默认编码是GBK,但Java程序是UTF-8编码,导致了输出中文乱码。
解决方法:获取进程字节输入流时,指定编码格式为GBK
BufferedReader bfr = new BufferedReader(new InputStreamReader(process.getInputStream(), "gbk"));
4.Runtime的exec方法在linux无法正常执行python命令
在linux系统中使用exec方法执行python命令需要写完整命令,并且需要root权限,否则无法执行。
因此需要判断系统是不是linux,如果是则修改参数中的python为完整的python路径。
六、项目部署上线到服务器
1.前端页面打包并放在后端静态资源目录
(1)先修改main.js中的后台api地址为生产环境地址。
(2)运行npm run build
打包
(3)将打包后的文件放入后端src/main/resources/static中。
2.重新启动SpringBoot项目。
3.在maven控制面板运行package命令,将SpringBoot项目进行打包
4.在target目录找到打包好的jar包
5.接下来将jar包放在服务器上运行即可。具体运行方法由你自己选择。
2 Comments
您好,请问这个可以通过Eclipse导入吗?
@匿名 您好,这个项目用的IDEA开发。直接导入eclipse需要做一定的修改才能正常使用