最新消息:本站持续更新中,请注意添加收藏夹。搜索关键词时,多换一个同义词。比如要搜索界面,可以尝试页面,画面,PER档等词汇。善于搜索,将大大提高你的查找效率。

[day15] IMPORT 3 利用http与XML套件取 Web资源

后端代码 bron1984 3954浏览

文章来自:iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天 (ithome.com.tw)

[day15] IMPORT 3 利用http与XML套件取 Web资源

没有人能一次做好所有的事情,也不可能有一套系统收尽所有数据。既然如此,如何适当且适时的抓取外部数据做为己用,是一件很重要的事情。

近年来除许多数据以 OPEN DATA形式提供公众使用;编程也纷纷将程序打散,做得更细更专,推行『微服务』化。今天篇章先不论怎么写出服务,而先探讨如何调取别人的服务。

一般外部数据提供有透过 API取用或实体档案抓取,实体档案可透过 wget 取回后用前述章节的channel解读。API的部分,通常在回传值还可区分为CSV / XML / JSON等等格式。今天范例取用日常生活可能会用到的『中油油价表』作为范例:
图形用户界面, 文本, 应用程序 描述已自动生成
参考网址 https://data.gov.tw/dataset/6339

从范例中可得知,调用后会以 XML 格式回传,因此依循Genero 方式拆解。所以为了阅读方便,程序拆成两段

  1. 取用Service的 FetchCPC 段落
  2. 取回数据拆解 XML 的段落(主程序)

取用Service应调用 HTTPRequest与HTTPResponse物件

Genero FGL要串接外部现成已经提供的服务并不困难,我们这次的目标是取回下列格式数据
图形用户界面, 文本, 应用程序 描述已自动生成

从最简单的调用范例来解读:

 

PUBLIC FUNCTION FetchCPCPrice()
  DEFINE ok       BOOLEAN
  DEFINE req      com.HttpRequest  #取用WebService主要对象
  DEFINE resp     com.HttpResponse #分析回传数据使用
  DEFINE resptype STRING
  DEFINE doc      xml.DomDocument  #抓出的XML Document
  DEFINE node     xml.DomNode      #在Genero中基本转为DomNode可用的method较多
  DEFINE str      STRING
  DEFINE ind      INTEGER
  DEFINE baseURL  STRING

  #中油牌价服务路径
  LET baseURL = "https://vipmember.tmtd.cpc.com.tw/opendata/",
                        "ListPriceWebService.asmx/getCPCMainProdListPrice_XML"

  LET req = com.HttpRequest.Create(baseURL)      #准备连结
  CALL req.setMethod("GET")                      #使用 GET 方法 (还有POST.DELETE..等)
  CALL req.doRequest()                           #做
  LET resp = req.getResponse()                   #将响应写入Response对象, 等待分析
  IF resp.getStatusCode()==200 THEN                 #状态代码检查
    LET resptype = resp.getHeader("Content-Type")   #准备确认一下形式是否正确
    IF resptype.getIndexOf("/xml",1) THEN           #检查是否为 XML
       LET doc = resp.getXmlResponse()              #使用XML获取数据 
       LET node = doc.getDocumentElement()          #转换为Genero xml.Document格式
    END IF
    LET ok = TRUE
  ELSE
    LET ok = FALSE
  END IF
  RETURN ok, str, node
END FUNCTION

 

Genero为了不让套件过大,将回传分析的功能(method)放进了HTTPResponse,所以在取用 Web Service之后,再调取Response处理就好。

com.HTTPRequest

分类:使用前,在程序最前端需 IMPORT com
功能:创建链接对象,并依照提供服务端的要求,可对本次发起的服务做好行前准备。如告知使用方法、放入参数、设定封包表头、设定TIMEOUT时间、与Cookie, proxy 等。接着就可依照不同的方式,做 Request(一般) / FileRquest / DataRequest / XmlRequest 等等。
Method List
特别注意:

  1. 由于范例中的『中油油价表』,发出时并未带任何参数,所以适用于一般的 .doRequest
  2. 创建时要传入URL,若带入的方法需要附加参数,则须在此步骤前完成组装所有的参数

com.HTTPResponse

分类:使用前,在程序最前端需 IMPORT com
功能:完成 WebService调用后,取回的状态与成果分析使用
Method List
特别注意:

  1. 在HTTPRequest阶段已完成 Web Service的连结调用并已断开,此段落主要在进行回传数据分析处理,不具备额外联机的作用
  2. 取回的数据,仍有对应数据格式的议题。范例中使用的是一般Response回传。Method中的 XMLResponse系指串流式回复的XML格式(Streaming XML),必须在以 StaxReader对象进行分析处理,与范例不同

取回 xml.DomNode后使用 XML套件分析处理

先阅读一下范例

 

IMPORT xml
IMPORT com
###需使用到的extension包###

MAIN
   DEFINE str,tagname,value STRING
   DEFINE ok,i,cnt          INTEGER
   DEFINE r,every,child  xml.DomNode
   DEFINE n              xml.DomNodeList

   CALL FetchCPCPrice() RETURNING ok, str, r    #前段WEB Service调用回传
   IF ok THEN
      #  预期要处理的格式如下: 
      #<Table>
      #  <型别名称>汽柴油零售</型别名称>
      #  <产品名称>92无铅汽油</产品名称>
      #  <参考牌价>28.2</参考牌价>
      #</Table>                       #思路: 找出Table节点, 再往下找出型别,产品,牌价等
   
      LET n = r.selectByXPath("Table",NULL)   #利用SELECT BY X(ML) PATH找符合条件的清单 
      LET cnt = n.getCount()                  #确认符合数
      FOR i=1 TO cnt
        LET every = n.getItem(i)              #取用个别的下层结构
        LET child = every.getFirstChild()     #往下一层进入
        WHILE (child IS NOT NULL)
           LET tagname = child.getNodeName()  #只处理对的子项目
           IF tagname = "型别名称" OR tagname = "产品名称" OR tagname = "参考牌价" THEN
              LET value = child.toString()
              LET value = value.subString(value.getIndexOf(">",1)+1,value.getIndexOf("</",1)-1)
              #无法处理中文TAG,所以将 TAGNAME 与 VALUE 剥离出来
              
              IF tagname = "型别名称" THEN         #需求项目分析 (parser)
                 IF value <> "汽柴油零售" THEN
                    LET child = child.getNextSibling()  #此处卷回WHILE,先取下一个,避免重工
                    CONTINUE FOR
                 END IF
              END IF
              DISPLAY tagname,":" ,value.trim()   #呈现需求项目
           END IF
           LET child = child.getNextSibling()     #取用下一个符合的
        END WHILE
      END FOR
   END IF
END MAIN

 

这边有一个状况需要特别说明
**Genero在现行版本无法处理非英语文字的 TAG,要转为 STRING **
**Genero在现行版本无法处理非英语文字的 TAG,要转为 STRING **

这也是在上方案例中,『LET value = child.toString()』主要的用意。转 TAG 为STRING来进行处理,
拨离 TAG 符号,判断是否合规

xml.DomNode

分类:使用前,在程序最前端需 IMPORT xml
功能:处理任何类型的XML文檔操作
Method List
特别注意:

  1. 在 Genero系统中有两组非常类似都可以处理 XML 结构的 DomNode,一组为 om.DomNode (内置built-in功能,不需要IMPORT可创建调用),另一组为 xml.DomNode。简单来说 xml.DomNode对 XML的支持较为全面,后续若有功能异动也会以 xml.DomNode 为主。因此 Genero 也有在文件中提及『可以考虑转移om.DomNode到xml.DomNode 』
  2. DomNode的处理只有 处理本阶 Node + 打开下一阶Node 的功能。所以『递归式的呼叫』在此段落势不可免。需要特别注意 RETURN回到哪一个阶层。
  3. 依据文件所述,这里的数据属性(attributes)为”@char” ,但因为呼叫不易,故许多Service大多已将值放入 TAG 的

xml.DomNodeList

分类:使用前,在程序最前端需 IMPORT xml
功能:一个可以承接 DomNode/DomDocument 查找出来节点(selectByXPath)存放的对象容器,方法很简单:有几个?是谁?
[Method List]
(https://4js.com/online_documentation/fjs-fgl-manual-html/#fgl-topics/c_gws_XmlDomNodeList_methods.html)

透过 XML 对象协助,就可以将取回的资料做好分析,在依据不同的需求进行处理即可。

最终程序运行结果

型别名称:汽柴油零售

产品名称:98无铅汽油

参考牌价:31.6

型别名称:汽柴油零售

产品名称:95无铅汽油

参考牌价:29.6

型别名称:汽柴油零售

产品名称:92无铅汽油

参考牌价:28.1

型别名称:汽柴油零售

产品名称:酒精汽油

参考牌价:29.6

FGL对于外部 WEB 数据的处理,从 1.00版的一片荒漠,到2.00版的支援SOAP,3.00版支援XML/JSON后,终于在 3.20版逐渐的补满补齐。后续的 Genero平台,将不再自外于因特网,而可以取之于网络用之于网络。我们在后续的篇章中介绍『创造 FGL的微服务』

转载请注明:赫非域 » [day15] IMPORT 3 利用http与XML套件取 Web资源