目录

1.什么是websocket

2.如何在aiohttp中使用websocket

3.如何在vue中使用websocket


1.什么是websocket

对于传统的web开发而言,是前台发送请求,后端接口返回对应的数据。但是在爬虫网页项目中当用户在页面上发送爬取请求之后,这个时候如果是传统的ajax请求,则前台就会阻塞,并且我们需要后台实时的通知用户现在爬虫的爬取进度。为了完成这一系列的交互功能,我们就需要使用websocket。websocket本质上就是一种协议,这种协议可以允许服务端主动给客户端客户端推送数据,在与vue相结合之后可以实现实时的数据刷新与展示效果。

2.如何在aiohttp中使用websocket

aiohttp中自带了websocket,并且是异步实现。这就意味着可以实时向客户端推送最新的数据。在flask中如何想实现这样功能的websocket则需要使用celery任务队列来完成。对于aiohttp来说使用websocket只需要以下的代码

async def websocket_handler(request):

    ws = web.WebSocketResponse()  
    await ws.prepare(request)  # 等待websocket连接

    async for msg in ws:
        if msg.type == aiohttp.WSMsgType.TEXT:
            if msg.data == 'close':
                await ws.close()
            else:
                await ws.send_str(msg.data + '/answer')
        elif msg.type == aiohttp.WSMsgType.ERROR:
            print('ws connection closed with exception %s' %
                  ws.exception())

    print('websocket connection closed')

    return ws

也可以使用ws.send_json来直接向前台发送json格式的数据,需要注意的是如果含有中文的话需要将ensure_ascii设置为False

3.如何在vue中使用websocket

在vue中使用websocket可以使用原始的websocket,也可以使用websocketio,这里我后台使用的是aiohttp的websocket,所以在vue中直接使用的是websocket。为了便于websocket的统一管理,我将websocket放到了vuex中进行全局管理,具体的大致芦逻辑如下,以登陆电影界面为例:

(1) 在用户登陆之后默认连接所有的websocket,用户退出之后默认断开所有的websocket(因为此时可能还有其他的爬虫任务正在后台运行,需要后台实时向前台推送数据)

(2)在进入需要使用websocket的界面之前检查websocket的状态,如果没有连接则连接之

(3)在离开需要使用websocket的界面之后,检查对应的websocket的任务状态,如果没有任务在运行则断开

(4)综上,将websocket放在vuex中进行管理,具体代码如下

store中的代码

import restFulUrl from "@/utils/config"

const state = {
  // movie websocket的连接状态管理
  movieSocket: {
    socket: '',  // websocket连接
    taskState:'' // 当前websocket的任务状态
  },

}

const mutations = {
  CONNECT_Movie: (state) =>{
    state.movieSocket.socket = new WebSocket(restFulUrl().replace("http", "ws") + "/search_movie") // 连接websocket
},
  DisConnect_Movie: (state) =>{
    state.movieSocket.socket.send(JSON.stringify({'state': 'close'}))
    state.movieSocket.socket = ''  // 断开连接
    state.movieSocket.taskState = ''  // 还原任务状态
  },
  Set_Movie_Task_State: (state, taskState)=>{
    state.movieSocket.taskState = taskState  // 设置任务状态
}
}

const actions = {
  connectMovie({commit}){
    commit('CONNECT_Movie')
  },
  disconnectMovie({commit}){
    commit('DisConnect_Movie')
  },
  setMovieTaskState({commit}, taskState){
    commit('Set_Movie_Task_State', taskState)
  }
}

export default
{
  namespaced: true,
  state,
  actions,
  mutations
}

permission.js

router.afterEach((to, from) => {
  // finish progress bar
  switch (from.path.split('/').pop()) {
    case 'movie':
      // 如果是电影界面
      if (!store.getters.movieSocket.taskState) {
        // 如果没有任务正在执行则断开连接
        store.dispatch('socket/disconnectMovie').then(()=>{
        })
      }
      break
  }
  NProgress.done()
})

index.js

{
        path: 'movie',
        name: 'Movie',
        beforeEnter(to, from, next) {
          if (!store.getters.movieSocket.socket) {
            // 如果没有建立websocket连接则建立连接
            connect.connectMovieSocket()
          }
          next()
        },
        component: () => import('@/views/movie/index'),
        meta: {title: '电影', icon: 'el-icon-film'}
      },

movie/index.vue

created() {
      this.$store.getters.movieSocket.socket.onmessage = (response) => {
        this.responseData = JSON.parse(response.data)
        this.code = this.responseData.code
        switch (this.code) {
          case 200:
            this.buttonContent = '日志查看'
            this.$store.getters.movieSocket.taskState = ''
            this.searchResult = this.responseData.data.result
            this.total = this.responseData.data.total
            this.loadText = "正在从数据库中获取结果"
            this.loading = false
            break
          case 204:
            this.$store.getters.movieSocket.taskState = 1  // 更新websocket的任务状态
            this.loading = false
            this.buttonContent = '当前任务'  // 切换按钮的text
            this.currentLog = this.responseData.data.result.join("\n") // 显示当前爬虫日志
            this.log = this.currentLog
            // this.ref$['taskButton'].text = '任务查看'
            // this.loadText = "正在从网页重新爬取结果"
            // this.loading ? '' : this.loading = true
            break
          case 500:
            this.loading = false
            this.$message({
              message: "请求出错",
              type: "danger"
            })
            break
        }
      }
      this.getList()
    },

 

这样在每次进入电影界面之前就会自动连接websocket.效果如下:

详细的代码和注册可以查看我的github: https://github.com/JustKeepSilence/DownLoad,欢迎大家交流和讨论~

Logo

前往低代码交流专区

更多推荐