从Google Maps API的来源和目的地解析大量XML

时间:2019-07-01 11:21:13

标签: excel xml vba xml-parsing

该问题与Get Google Maps XML data, parse it and input to Excel cells部分相关。在上一个问题中,解决方案是遍历每个链接和输入值。我发现,可以调用多个起点和目的地,并从Google Maps获取XML文件中的所有内容。现在,这是一种用于获取数据的巨大XML。我需要获得1 hour 30 mins之类的持续时间,以及103 km之类的距离。这怎么可能?

当我只有5个不同的变体时,为什么还有25个XML响应?

这是我的VBA:

Sub GetMyValuesGoogleAPI()
    Dim URL As String
    Dim originsParam As String
    Dim destinationsParam As String
    Dim APIkey As Range, TravelMode As Range

    Dim xmlDoc As DOMDocument30

    Dim origins(0 To 4) As String
    Dim destinations(0 To 4) As String

    Dim n As Integer

    n = FreeFile()
    Open Environ$("USERPROFILE") & "\Desktop\" & "test.txt" For Output As #n

    origins(0) = "London, UK"
    origins(1) = "Manchester, UK"
    origins(2) = "Liverpool, UK"
    origins(3) = "Bristol, UK"
    origins(4) = "Bath, UK"

    destinations(0) = "Cambridge, UK"
    destinations(1) = "Leeds, UK"
    destinations(2) = "Cambridge, UK"
    destinations(3) = "Norwich, UK"
    destinations(4) = "Brighton, UK"

    Set APIkey = ThisWorkbook.Worksheets("Other Data").Range("CE1")
    Set TravelMode = ThisWorkbook.Worksheets("Other Data").Range("BY3")
    Set xmlDoc = New DOMDocument30

    originsParam = Join(origins, "|")
    destinationsParam = Join(destinations, "|")
    URL = "https://maps.googleapis.com/maps/api/distancematrix/xml?origins=" & Escape(originsParam) & "&destinations=" _
    & Escape(destinationsParam) & "&mode=" & TravelMode & "&key=" & APIkey

    With xmlDoc
        .async = False
        .Load URL
        Debug.Print .XML
        Print #n, .XML
    End With

End Sub

功能:

Public Function Escape(ByVal param As String) As String

    Dim i As Integer, BadChars As String

    BadChars = "%<>=&!@#$^()+{[}]|\;:'"",/?"
    For i = 1 To Len(BadChars)
        param = Replace(param, Mid(BadChars, i, 1), "%" & Hex(Asc(Mid(BadChars, i, 1))))
    Next
    param = Replace(param, " ", "+")
    Escape = param

End Function

这是XML输出:

<?xml version="1.0"?>
<DistanceMatrixResponse>
    <status>OK</status>
    <origin_address>London, UK</origin_address>
    <origin_address>Manchester, UK</origin_address>
    <origin_address>Liverpool, UK</origin_address>
    <origin_address>Bristol, UK</origin_address>
    <origin_address>Bath, UK</origin_address>
    <destination_address>Cambridge, UK</destination_address>
    <destination_address>Leeds, UK</destination_address>
    <destination_address>Cambridge, UK</destination_address>
    <destination_address>Norwich, UK</destination_address>
    <destination_address>Brighton, UK</destination_address>
    <row>
        <element>
            <status>OK</status>
            <duration>
                <value>5420</value>
                <text>1 hour 30 mins</text>
            </duration>
            <distance>
                <value>103024</value>
                <text>103 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>13268</value>
                <text>3 hours 41 mins</text>
            </duration>
            <distance>
                <value>313516</value>
                <text>314 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>5420</value>
                <text>1 hour 30 mins</text>
            </duration>
            <distance>
                <value>103024</value>
                <text>103 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>8674</value>
                <text>2 hours 25 mins</text>
            </duration>
            <distance>
                <value>189805</value>
                <text>190 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>6696</value>
                <text>1 hour 52 mins</text>
            </duration>
            <distance>
                <value>103629</value>
                <text>104 km</text>
            </distance>
        </element>
    </row>
    <row>
        <element>
            <status>OK</status>
            <duration>
                <value>12617</value>
                <text>3 hours 30 mins</text>
            </duration>
            <distance>
                <value>301588</value>
                <text>302 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>3723</value>
                <text>1 hour 2 mins</text>
            </duration>
            <distance>
                <value>71765</value>
                <text>71.8 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>12617</value>
                <text>3 hours 30 mins</text>
            </duration>
            <distance>
                <value>301588</value>
                <text>302 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>15640</value>
                <text>4 hours 21 mins</text>
            </duration>
            <distance>
                <value>336098</value>
                <text>336 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>16712</value>
                <text>4 hours 39 mins</text>
            </duration>
            <distance>
                <value>417143</value>
                <text>417 km</text>
            </distance>
        </element>
    </row>
    <row>
        <element>
            <status>OK</status>
            <duration>
                <value>13457</value>
                <text>3 hours 44 mins</text>
            </duration>
            <distance>
                <value>312942</value>
                <text>313 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>5458</value>
                <text>1 hour 31 mins</text>
            </duration>
            <distance>
                <value>117472</value>
                <text>117 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>13457</value>
                <text>3 hours 44 mins</text>
            </duration>
            <distance>
                <value>312942</value>
                <text>313 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>17245</value>
                <text>4 hours 47 mins</text>
            </duration>
            <distance>
                <value>409544</value>
                <text>410 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>17253</value>
                <text>4 hours 48 mins</text>
            </duration>
            <distance>
                <value>437125</value>
                <text>437 km</text>
            </distance>
        </element>
    </row>
    <row>
        <element>
            <status>OK</status>
            <duration>
                <value>11371</value>
                <text>3 hours 10 mins</text>
            </duration>
            <distance>
                <value>269123</value>
                <text>269 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>12344</value>
                <text>3 hours 26 mins</text>
            </duration>
            <distance>
                <value>333320</value>
                <text>333 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>11344</value>
                <text>3 hours 9 mins</text>
            </duration>
            <distance>
                <value>272045</value>
                <text>272 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>14866</value>
                <text>4 hours 8 mins</text>
            </duration>
            <distance>
                <value>386263</value>
                <text>386 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>10533</value>
                <text>2 hours 56 mins</text>
            </duration>
            <distance>
                <value>254730</value>
                <text>255 km</text>
            </distance>
        </element>
    </row>
    <row>
        <element>
            <status>OK</status>
            <duration>
                <value>11688</value>
                <text>3 hours 15 mins</text>
            </duration>
            <distance>
                <value>264172</value>
                <text>264 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>13467</value>
                <text>3 hours 44 mins</text>
            </duration>
            <distance>
                <value>352919</value>
                <text>353 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>11662</value>
                <text>3 hours 14 mins</text>
            </duration>
            <distance>
                <value>267094</value>
                <text>267 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>15183</value>
                <text>4 hours 13 mins</text>
            </duration>
            <distance>
                <value>381312</value>
                <text>381 km</text>
            </distance>
        </element>
        <element>
            <status>OK</status>
            <duration>
                <value>10850</value>
                <text>3 hours 1 min</text>
            </duration>
            <distance>
                <value>249779</value>
                <text>250 km</text>
            </distance>
        </element>
    </row>
</DistanceMatrixResponse>

1 个答案:

答案 0 :(得分:1)

请考虑XSLT,这是一种专用语言,旨在将XML文件转换为最终使用格式,包括其他XML文件,HTML文件甚至文本文件。在这里,XSLT可以将您的 DistanceMatrixResult 结构转换为CSV格式,并带有需要的标题和数据行,以将其导入Excel电子表格。 Office库MSXML(您已经使用过)可以运行XSLT 1.0脚本。

您收到25个 请求的原因是每个5个原始站点和5个目标站点(5*5)的成对匹配。每个是一对配对(即英国伦敦到所有五个目的地)。在XSLT中相应地映射这些配对。顺便说一句,忘记解析文本,而使用值代替,其中 duration 以秒为单位,距离以米为单位。甚至XSLT都将数学转换转换为十进制小时和十进制公里!

XSLT (另存为.xsl文件,特殊的.xml文件,以便在VBA中读取)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="DistanceMatrixResponse">
    <xsl:text>origin_address,destination_address,duration,distance&#xa;</xsl:text>
      <xsl:apply-templates select="row"/>
  </xsl:template>

  <xsl:template match="row">
      <xsl:variable name="row_pos"><xsl:value-of select="position()"/></xsl:variable>

      <xsl:for-each select="element">
        <xsl:variable name="elem_pos"><xsl:value-of select="position()"/></xsl:variable>
        <xsl:variable name="quot">&quot;</xsl:variable>
        <xsl:value-of select="concat($quot, ancestor::DistanceMatrixResponse/origin_address[position() = $row_pos], $quot, ',',
                                     $quot, ancestor::DistanceMatrixResponse/destination_address[position() = $elem_pos], $quot, ',', 
                                     format-number(duration/value div 3600, '##.####'), ',', 
                                     format-number(distance/value div 100, '##.####'))"/>
        <xsl:text>&#xa;</xsl:text>
      </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Online Demo

VBA

Public Sub RunXSLTtoCSV()
    Dim xmlDoc As New MSXML2.DOMDocument30, xslDoc As New MSXML2.DOMDocument30
    Dim txtOutput As String, csvfile As String

    ' LOAD XML AND XSL
    xmlDoc.Load "C:\Path\To\Google\Maps\API\Response.xml"
    xmlDoc.async = False
    xslDoc.Load "C:\Path\To\XSLT\Script.xsl"
    xslDoc.async = False

    ' TRANSFORM TO TEXT
    txtOutput = xmlDoc.transformNode(xslDoc)

    ' SAVE TO CSV (TO IMPORT INTO EXCEL)
    csvfile = "C:\Path\To\CSV\File.csv"
    Open csvfile For Output As #1
        Print #1, txtOutput
    Close #1

    Set xslDoc = Nothing
    Set xmlDoc = Nothing
End Sub

输出

CSV Output