json - Extraer ubicaciones de datos del mapa
vba google-maps (1)
Quiero extraer datos de un mapa y luego obtener y almacenar las ubicaciones de todas las estaciones de carga en un estado específico. (por ejemplo: https://www.plugshare.com/ )
¿Cómo se puede hacer esto? No me importa usar ningún lenguaje de programación, pero ¿cuál es el mejor para esta aplicación?
Puede recuperar los datos directamente desde https://www.plugshare.com/ con XHR. Debe analizar un poco cómo funciona un sitio web para raspar los datos. Para cualquier dato cargado dinámicamente, simplemente inspeccione los XHR que hace la página web, encuentre el que contiene los datos relevantes, haga el mismo XHR (ya sea que el sitio proporcione API o no) y analice la respuesta. Navegue por la página, por ejemplo, en Chrome, luego abra la ventana Herramientas para desarrolladores ( F12 ), pestaña Red, vuelva a cargar la página F5 y examine los XHR en la lista.
Hay una de las solicitudes para URL
https://www.plugshare.com/api/locations/region?...
que devuelve latitud, longitud y otra información para estaciones de carga en un área de ventana rectangular con coordenadas especificadas.
Puede encontrar URL, parámetros de consulta y algunos encabezados necesarios como se muestra a continuación:
La respuesta está en formato JSON:
Debe agregar un encabezado de autorización básico para solicitar.
Para recuperar las credenciales, vaya a la pestaña Fuentes, agregue XHR Breakpoint para que la URL contenga
https://www.plugshare.com/api/locations/region
, vuelva a cargar la página
F5
, cuando la página esté en pausa en XHR, siga el marco Pila de llamadas marco:
Omita cualquier objeto
NREUM
y
nrWrapper
que forme parte de
la
funcionalidad
New Relic
.
Haga clic en pretty-print
{}
para formatear la fuente.
Busque, por ejemplo,
Basic
,
Authorization
o
setRequestHeader
en las fuentes, para ese caso particular, la primera coincidencia se encuentra en
https://www.plugshare.com/js/main.js?_=1
:
Haga clic en una estación en el mapa y aparecerá un XHR más con URL como
https://www.plugshare.com/api/locations/[id]
con información detallada para esa estación, como se muestra a continuación:
La respuesta también está en formato JSON:
También puede obtener datos de estaciones de URL como
https://www.plugshare.com/api/stations/[id]
.
Puede usar el siguiente código de VBA para recuperar información como se describe anteriormente. Importe el módulo JSON.bas al proyecto VBA para el procesamiento JSON.
Option Explicit
Sub Test_www_plugshare_com()
Const Transposed = False '' Output option
Const Detailed = True '' Scrape option
Dim sResponse As String
Dim aQryHds()
Dim oQuery As Object
Dim sQuery As String
Dim vRegionJSON
Dim sState As String
Dim aResult()
Dim i As Long
Dim vLocationJSON
Dim aRows()
Dim aHeader()
'' Retrieve auth token
XmlHttpRequest "GET", "https://www.plugshare.com/js/main.js?_=1", "", "", "", sResponse
With RegExMatches(sResponse, "var s/=""(Basic [^""]*)"";") '' var s="Basic *";
If .Count > 0 Then
aQryHds = Array( _
Array("Authorization", .Item(0).SubMatches(0)), _
Array("Accept", "application/json") _
)
Else
MsgBox "Can''t retrieve auth token"
Exit Sub
End If
End With
'' Set query parameters
Set oQuery = CreateObject("Scripting.Dictionary")
With oQuery
.Add "minimal", "1"
.Add "count", "500"
.Add "latitude", "19.697593650121235"
.Add "longitude", "-155.06529816792295"
.Add "spanLng", "0.274658203125"
.Add "spanLat", "0.11878815323507652"
.Add "access", "1,3"
.Add "outlets", "[{""connector"":1},{""connector"":2},{""connector"":3},{""connector"":4},{""connector"":5},{""connector"":6,""power"":0},{""connector"":6,""power"":1},{""connector"":7},{""connector"":8},{""connector"":9},{""connector"":10},{""connector"":11},{""connector"":12},{""connector"":13},{""connector"":14},{""connector"":15}]"
.Add "fast", "add"
End With
sQuery = EncodeQueryParams(oQuery)
'' Retrieve a list of stations for the viewport
XmlHttpRequest "GET", "https://www.plugshare.com/api/locations/region?" & sQuery, aQryHds, "", "", sResponse
'' Parse JSON response
JSON.Parse sResponse, vRegionJSON, sState
If sState <> "Array" Then
MsgBox "Invalid JSON response"
Exit Sub
End If
'' Populate result array
ReDim aResult(UBound(vRegionJSON))
'' Extract selected properties from parsed JSON
For i = 0 To UBound(aResult)
Set aResult(i) = ExtractKeys(vRegionJSON(i), Array("id", "name", "latitude", "longitude"))
DoEvents
Next
If Detailed Then
'' Populate result array with detailed info for each location
For i = 0 To UBound(aResult)
'' Retrieve detailed info for each location
XmlHttpRequest "GET", "https://www.plugshare.com/api/locations/" & aResult(i)("id"), aQryHds, "", "", sResponse
'' Parse JSON response
JSON.Parse sResponse, vLocationJSON, sState
If sState = "Object" Then
'' Extract selected properties from parsed JSON
Set aResult(i) = ExtractKeys(vLocationJSON, Array("reverse_geocoded_address", "hours", "phone", "description"), aResult(i))
End If
DoEvents
Next
End If
'' Convert resulting array to arrays for output
JSON.ToArray aResult, aRows, aHeader
'' Output
With ThisWorkbook.Sheets(1)
.Cells.Delete
If Transposed Then
Output2DArray .Cells(1, 1), WorksheetFunction.Transpose(aHeader)
Output2DArray .Cells(1, 2), WorksheetFunction.Transpose(aRows)
Else
OutputArray .Cells(1, 1), aHeader
Output2DArray .Cells(2, 1), aRows
End If
.Columns.AutoFit
End With
MsgBox "Completed"
End Sub
Sub XmlHttpRequest(sMethod As String, sUrl As String, arrSetHeaders, sFormData, sRespHeaders As String, sContent As String)
Dim arrHeader
''With CreateObject("Msxml2.ServerXMLHTTP")
'' .SetOption 2, 13056 '' SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS
With CreateObject("MSXML2.XMLHTTP")
.Open sMethod, sUrl, False
If IsArray(arrSetHeaders) Then
For Each arrHeader In arrSetHeaders
.SetRequestHeader arrHeader(0), arrHeader(1)
Next
End If
.send sFormData
sRespHeaders = .GetAllResponseHeaders
sContent = .responseText
End With
End Sub
Function RegExMatches(sText, sPattern, Optional bGlobal = True, Optional bMultiLine = True, Optional bIgnoreCase = True) As Object
With CreateObject("VBScript.RegExp")
.Global = bGlobal
.MultiLine = bMultiLine
.IgnoreCase = bIgnoreCase
.Pattern = sPattern
Set RegExMatches = .Execute(sText)
End With
End Function
Function EncodeQueryParams(oParams As Object) As String
Dim aParams
Dim i As Long
aParams = oParams.Keys()
For i = 0 To UBound(aParams)
aParams(i) = EncodeUriComponent((aParams(i))) & "=" & EncodeUriComponent((oParams(aParams(i))))
Next
EncodeQueryParams = Join(aParams, "&")
End Function
Function EncodeUriComponent(strText As String) As String
Static objHtmlfile As Object
If objHtmlfile Is Nothing Then
Set objHtmlfile = CreateObject("htmlfile")
objHtmlfile.parentWindow.execScript "function encode(s) {return encodeURIComponent(s)}", "jscript"
End If
EncodeUriComponent = objHtmlfile.parentWindow.encode(strText)
End Function
Function ExtractKeys(oSource, aKeys, Optional oTarget = Nothing) As Object
Dim vKey
If oTarget Is Nothing Then Set oTarget = CreateObject("Scripting.Dictionary")
For Each vKey In aKeys
If oSource.Exists(vKey) Then
If IsObject(oSource(vKey)) Then
Set oTarget(vKey) = oSource(vKey)
Else
oTarget(vKey) = oSource(vKey)
End If
End If
Next
Set ExtractKeys = oTarget
End Function
Sub OutputArray(oDstRng As Range, aCells As Variant)
With oDstRng
.Parent.Select
With .Resize(1, UBound(aCells) - LBound(aCells) + 1)
.NumberFormat = "@"
.Value = aCells
End With
End With
End Sub
Sub Output2DArray(oDstRng As Range, aCells As Variant)
With oDstRng
.Parent.Select
With .Resize( _
UBound(aCells, 1) - LBound(aCells, 1) + 1, _
UBound(aCells, 2) - LBound(aCells, 2) + 1)
.NumberFormat = "@"
.Value = aCells
End With
End With
End Sub
Cambie a
Const Detailed = False
si tiene muchos elementos para salida para evitar que la aplicación se bloquee, ya que los XHR están en modo síncrono.
La salida para mí con coordenadas de ventana gráfica especificadas es la siguiente:
Por cierto, el mismo enfoque utilizado en this , this , this , this , this y this responde.