【问题标题】:Export visualization data using Qlik Engine JSON API使用 Qlik Engine JSON API 导出可视化数据
【发布时间】:2020-12-23 16:06:02
【问题描述】:

我们的组织使用 Qlik Sense Enterprise,我们正在寻求自动化用于可视化的数据的下载过程(格式可以是 excel 或 csv),而不是导致以下结果的手动过程(显示截取的屏幕截图):

对于我们的用例,假设只有一个应用程序内部有一张工作表,并且该工作表有 3 个可视化。

我编写了一个当前已连接到 localhost 的 python 脚本,并且我能够使用 Qlik Engine JSON API 检索 app_id、sheet_id 和 3 个图表的 id。代码的工作方式如下:

  1. 获取 doc_list (app_list)
  2. 选择应用,因为我们只有一个应用,所以我们选择索引为 0
  3. 创建会话对象(我看到 Dev Hub 上的 Qlik Engine 表现出这种行为,这就是我执行此步骤的原因)
  4. 获取应用的布局
  5. 选择工作表,因为我们只有一张工作表,所以我们选择索引为 0
  6. 遍历可视化并打印其名称

我提供了以下代码供您参考,pastebin 链接可以访问here

import requests
import websocket, ssl
import json,  csv
import os, time
from pprint import pprint 
 
#Connecting to the server. The lines below will be replaced by the certificates and headers for enterprise usage later
ws = websocket.WebSocket()
ws.connect("ws://localhost:4848/app/")
 
#For getting the doc (app) list
doclist_req = {
    "handle": -1,
    "method": "GetDocList",
    "params": [],
    "outKey": -1,
    "id": 1
}
 
ws.send(json.dumps(doclist_req))
result = ws.recv()
ws.send(json.dumps(doclist_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
 
#For opening the doc (app)
app_req = {
    "jsonrpc": "2.0",
    "method": "OpenDoc",
    "handle": -1,
    "params": [
        #Can iterate if multiple apps are there
        #Since only one app was present we used the index 0
        result_json['result']['qDocList'][0]['qDocId']
    ],
    "outKey": -1,
    "id": 2
}
 
#The first call seems to be for establishing the connection and second to actually send the request body
ws.send(json.dumps(app_req))
result = ws.recv()
ws.send(json.dumps(app_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
app_req_handle = result_json['result']['qReturn']['qHandle']
 
#For creating the session object necessary for fetching dimensions, fields, etc.
session_req = {
    "jsonrpc": "2.0",
    "method": "CreateSessionObject",
    "handle": app_req_handle,
    "params": [
        {
            "qInfo": {
                "qType": "SheetList"
            },
            "qAppObjectListDef": {
                "qType": "sheet",
                "qData": {
                    "title": "/qMetaDef/title",
                    "description": "/qMetaDef/description",
                    "thumbnail": "/thumbnail",
                    "cells": "/cells",
                    "rank": "/rank",
                    "columns": "/columns",
                    "rows": "/rows"
                }
            }
        }
    ],
    "outKey": -1,
    "id": 3
}
 
ws.send(json.dumps(session_req))
result = ws.recv()
ws.send(json.dumps(session_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
session_req_handle = result_json['result']['qReturn']['qHandle']
 
#For fetching the layout of the sheets
layout_req = {
    "jsonrpc": "2.0",
    "method": "GetLayout",
    "handle": session_req_handle,
    "params": [],
    "outKey": -1,
    "id": 4
}
 
ws.send(json.dumps(layout_req))
result = ws.recv()
ws.send(json.dumps(layout_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
 
#Since only one sheet was present we used the index 0
list_of_charts = result_json['result']['qLayout']['qAppObjectList']['qItems'][0]['qData']['cells']
for chart in list_of_charts:
    print(chart['name'])
print()
 
ws.close()

我已经探索了 Qlik 社区以及 SO 和 ExportData 上的很多页面,但我无法为其编写正确的 JSON 请求。作为参考,我在终端中执行 python 脚本时得到的输出如下所示。我对此很陌生,我会非常感谢你们所有人的帮助。

{'jsonrpc': '2.0', 'id': 1, 'result': {'qDocList': [{'qDocName': 'Test_2.qvf', 'qConnectedUsers': 0, 'qFileTime': 44188.190983796296 , 'qFileSize': 851968, 'qDocId': 'C:\Users\mohdm\Documents\Qlik\Sense\Apps\Test_2.qvf', 'qMeta': {'hassectionaccess': False, 'encrypted': False}, 'qLastReloadTime': '2020-12-22T19:12:22.245Z', 'qTitle': 'Test_2', 'qThumbnail': {}}]}}

{'jsonrpc': '2.0', 'id': 2, 'result': {'qReturn': {'qType': 'Doc', 'qHandle': 1, 'qGenericId': 'C:\Users\mohdm\Documents\Qlik\Sense\Apps\ Test_2.qvf'}}, 'change': 1}

{'jsonrpc': '2.0', 'id': 3, 'result': {'qReturn': {'qType': 'GenericObject','qHandle':2,'qGenericType':'SheetList','qGenericId':'4b344780-a350-48db-8b65-27bb5a2c62b2'}},'change':1}

{'jsonrpc': '2.0', 'id': 4, 'result': {'qLayout': {'qInfo': {'qId': '4b344780-a350-48db-8b65-27bb5a2c62b2', 'qType': 'SheetList'}, 'qMeta': {'privileges': ['read', 'update', 'delete', 'exportdata']}, 'qSelectionInfo': {}, 'qAppObje ctList': {'qItems': [{'qInfo': {'qId': '8a0f6a01-ef89-4d65-821f-371c26208dcf', 'qType': 'sheet'}, 'qMeta': {'privileges': [ 'read', 'update', 'delete', 'exportdata'], 'title': 'Sheet_1', 'description': ''}, 'qData': {'rank': None, 'thumbnail': {' qStaticContentUrl': {}}, 'columns': 24, 'rows': 12, 'cells': [{'name': 'kfxNpV', 'type': 'auto-chart', 'col': 0, ' row':0,'colspan':15,'rowspan':6,'bounds':{'y':0,'x':0,'width':62.5,'height':50}},{' name': 'qHzmARQ', 'type': 'qlik-barplus-chart', 'col': 0, 'row': 6, 'colspan': 21, 'rowspan': 6, 'bounds': {'y ': 50, 'x': 0, 'width': 87.5, 'height': 50}}, {'name': 'BXBQmw', 'type': 'auto-chart', 'col': 15, ' row':0,'colspan':9,'rowspan':6,'bounds':{'y':0,'x':62.5,'width':37.5,'height':50}}],'标题': 'Sheet_1', 'description': ''}}]}}}}

kfxNpV
qHzmARQ
BXBQmw

我最初也在Qlik community 发布了这个问题,但没有得到回复。

【问题讨论】:

    标签: javascript python json websocket qliksense


    【解决方案1】:

    对于这种情况,我通常从浏览器“观察”Qlik 的通信。

    (在 Chrome 中)

    • 打开应用
    • 打开浏览器开发工具(按 F12)
    • 导航到“网络”(1)
    • 导航到“WS”(2)
    • 按下所需的套接字会话 (3)
    • 按“消息”(4)
    • 检查套接字中发送/接收的内容

    (如果您在“网络”选项卡中看不到套接字,请刷新页面)

    以下是为一个对象导出数据的 Javascript/Node 代码。 (在我的情况下,我对对象 ID 进行了硬编码)

    const fs = require('fs');
    const axios = require('axios');
    
    const enigma = require('enigma.js');
    const WebSocket = require('ws');
    const schema = require('enigma.js/schemas/12.20.0.json');
    
    const session = enigma.create({
        schema,
        url: 'ws://localhost:9076/app/engineData',
        createSocket: url => new WebSocket(url)
    });
    
    (async function () {
        // open new session
        let global = await session.open();
    
        // open the app
        let doc = await global.openDoc("C:\\Users\\USERNAME\\Documents\\Qlik\\Sense\\Apps\\Consumer_Sales.qvf");
    
        // get the required object
        let qObj = await doc.getObject("MEAjCJ");
    
        // OOXML - export the data in Excel (xlsx) format
        let data = await qObj.exportData("OOXML");
    
        // generate the full download link
        let downloadLink = `http://localhost:4848${data.qUrl}`;
    
        // download and save the file
        await axios.get(downloadLink, { responseType: "stream" })
            .then(response => {
                response.data.pipe(fs.createWriteStream("export.xlsx"));
            });
    })()
    

    ExportData 方法的官方文档是here

    更新 - Python 代码 sn-p

    通过工作表对象的循环可能如下所示:

    id = 5
    
    list_of_charts = result_json['result']['qLayout']['qAppObjectList']['qItems'][0]['qData']['cells']
    for chart in list_of_charts:
    
        obj_req = {
            "jsonrpc": "2.0",
            "method": "GetObject",
            "handle": app_req_handle,
            "params": [ chart["name"] ],
            "outKey": -1,
            "id": id
        }
    
        ws.send(json.dumps(obj_req))
        result = ws.recv()
        ws.send(json.dumps(obj_req))
        result = ws.recv()
        result_json = json.loads(result)
        # print(result_json)
    
        obj_req_handle = result_json['result']['qReturn']['qHandle']
    
        export_req = {
            "jsonrpc": "2.0",
            "method": "ExportData",
            "handle": obj_req_handle,
            "params": [ "OOXML" ],
            "outKey": -1,
            "id": 6
        }
    
        ws.send(json.dumps(export_req))
        result = ws.recv()
        ws.send(json.dumps(export_req))
        result = ws.recv()
        result_json = json.loads(result)
    
        downloadURL = "http://localhost:4848" + result_json["result"]["qUrl"]
        r = requests.get(downloadURL, allow_redirects=True)
        open('export_python_' + chart["name"] + '.xlsx', 'wb').write(r.content)
    
        id += 1
    

    【讨论】:

    • 这真是太棒了,非常感谢@Stefan 的帮助。由于我不知道 node.js 并且很难使用它,因此我在 Python 中编写了与此代码等效的代码。该代码正在处理 JSON 请求,唯一的问题是它为 3 个对象生成 6 个文件夹(我猜基本上运行了两次)。如果这不是问题,您能否查看我用 Python 编写的代码并提供反馈?如果我必须在原始问题或 pastebin 中发布更改后的代码,请告诉我。
    • 我不是 python 人,但我运行了你的代码并添加了导出片段(用 sn-p 更新了我的答案)。就我而言,它运行良好 - 生成了正确数量的文件并且它只运行一次
    • 感谢 Stefan 的回复。我认为原因是在 localhost 机器上生成的 url 指向在“C:\Users\user_name\Documents\Qlik\Sense\Exports”中找到的 Exports 目录,当您在脚本中调用它时它访问该文件使用请求模块。我认为 Qlik Enterprise Server 的工作原理与将文件存储在服务器上的概念相同。
    【解决方案2】:

    我不确定 Python,但我发现通常最困难的部分是获得正确的身份验证。像 JavaScript 那样的库通常会大大简化这一点。就我个人而言,我倾向于将 C# 用于此类小工具,其中 .NET SDK 提供操作员来为您完成管道工作。这是使用这两个 nuget 包的示例:

    此代码下载应用第一张表上所有可视化的 xlsx 文件。

    var location = Location.FromUri(uri);
    location.AsNtlmUserViaProxy();
    var restClient = new RestClient(uri);
    restClient.AsNtlmUserViaProxy();
    
    using (var app = location.App(new AppIdentifier {AppId = appId}))
    {
      var theSheet = app.GetSheets().First();
      var objs = theSheet.GetChildInfos().Select(info => app.GetGenericObject(info.Id));
      foreach (var o in objs)
      {
        var exportResult = o.ExportData(NxExportFileType.EXPORT_OOXML);
        var data = restClient.GetBytes(exportResult.Url);
        using (var writer = new BinaryWriter(new FileStream(o.Id + ".xlsx", FileMode.OpenOrCreate)))
        {
          writer.Write(data);
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-14
      • 2021-09-25
      • 2021-09-05
      • 2011-10-23
      • 1970-01-01
      • 1970-01-01
      • 2022-07-12
      • 1970-01-01
      相关资源
      最近更新 更多