Geolocate multiple IP addresses
It seems to be possible to get this data directly in Mathematica using entities. Here's an example:
Let's get a list of IP addresses to work with:
ips =
First@*First@*HostLookup /@ {"wolfram.com", "stackexchange.com",
"google.com", "baidu.com", "www.elysee.fr", "global.jaxa.jp",
"qwant.com", "eso.org"}
(* {"206.123.112.238", "151.101.1.69", "216.58.198.206", \
"220.181.57.217", "207.123.33.126", "202.32.9.55", "194.187.168.99", \
"134.171.75.1"} *)
Transform them into entities:
entityList = Entity["IPAddress", #] & /@ ips;
The coordinates are available as a property:
EntityProperties@First[entityList]
(* {EntityProperty["IPAddress", "Address"],
EntityProperty["IPAddress", "Country"],
EntityProperty["IPAddress", "FullIPv6Address"],
EntityProperty["IPAddress", "HostCoordinates"],
EntityProperty["IPAddress", "HostLocation"],
EntityProperty["IPAddress", "HostOrganization"],
EntityProperty["IPAddress", "HostSegment"],
EntityProperty["IPAddress", "IPv4Address"],
EntityProperty["IPAddress", "IPv6Address"],
EntityProperty["IPAddress", "Name"]} *)
Let's get them all at once:
coords =
EntityValue[entityList, "HostCoordinates"]; // AbsoluteTiming
(* {0.976854, Null} *)
And plot them:
GeoGraphics[GeoMarker@GeoPosition[coords], GeoRange -> "World"]
Optionally use DeleteMissing[coords]
to remove failed lookups.
Timing using the benchmark dataset:
coord =
EntityValue[Entity["IPAddress", #] & /@ iplist, "HostCoordinates"]; // AbsoluteTiming
(* {3.68313, Null} *)
Do not apply this method to long lists, where Batch mode would be more efficient and less abusive to the API provider. See this other answer.
3 API Integrated solution (One API call per IP)
ClearAll[IP2Location];
Options[IP2Location] = {Method -> Automatic};
SetAttributes[IP2Location, Listable]
IP2Location[ip_String | IPAddress[ip_String], OptionsPattern[]] :=
Block[{url, response, methodlist, method, latlon},
methodlist = {"nekudo", "freegeoip", "ip-api"};
method = If[
OptionValue[Method] === Automatic || FreeQ[methodlist, OptionValue[Method]],
RandomChoice[methodlist],
OptionValue[Method]
];
url = URLBuild@Switch[method,
"nekudo", <|"Scheme" -> "http", "Domain" -> "geoip.nekudo.com", "Path" -> {"api", ip}|>,
"freegeoip", <|"Scheme" -> "http", "Domain" -> "freegeoip.net", "Path" -> {"json", ip}|>,
"ip-api", <|"Scheme" -> "http", "Domain" -> "ip-api.com", "Path" -> {"json", ip}|>
];
response = Import[url, "RawJSON"];
latlon = ToExpression@Values[
Switch[method,
"nekudo", Query["location", {"latitude", "longitude"}],
"freegeoip", Query[{"latitude", "longitude"}],
"ip-api", Query[{"lat", "lon"}]
][response]
];
(*Echo[response,method];*)
GeoLocation[latlon]
]
Performance
First@AbsoluteTiming@IP2Location[iplist]
(*37.6867*)
Failed in 7 sites.
Batch mode API call, for best performance
From the documentation at http://ip-api.com/docs/api:batch
"Batch JSON", a batch processing with the ability to query multiple IP addresses in one HTTP request, significantly faster than submitting individual queries.
A batch request requires a POST request to http://ip-api.com/batch with a Body string in JSON array format, containing up to 100 objects. Therefore, here the ipLongList
arguments is Partition
into as many ipShortList
as necessary.
Free for non-commercial use only!
BatchIP2Location[ipLongList : List[_String ..]] :=
AssociationThread[ipLongList,
Flatten@Table[
Query[Values, GeoPosition][
ImportString[
URLRead[
HTTPRequest[
"http://ip-api.com/batch",
<|
Method -> "POST",
"Query" -> {"fields" -> "lat,lon"},
"Body" ->
ExportString[
Map[{"query" -> #} &, ipShortList],
"JSON"]
|>]
, "Body"], {"RawJSON"}]
],
{ipShortList, Partition[ipLongList, UpTo[99]]}
]]
Performance
Length@iplist
(* 358 *)
First@AbsoluteTiming@BatchIP2Location@iplist
(* 0.525389 *)