每周源代码37:轻松实现地理定位(反向查找IP地址)


[原文发表地址]  The Weekly Source Code 37 – Geolocation/Geotargeting (Reverse IP Address Lookup) in ASP.NET MVC made easy

[原文发表时间]   2008-11-27  07:30 AM

首先我想说,我不断追求并奉信通过阅读源代码使自己成为一个更好的开发者,亲爱的读者,我给大家呈现第37篇“每周源代码”,之后还会有无尽的续篇。

我正和Rob ConeryDave Ward以及其他人一起做一个边缘项目。鉴于用户们源源不断地来页面访问,他们的位置源于其IP地址,我希望可以马上着手一项调研。我想用“地理定位”或反向IP地址查询获取IP,如127.0.0.1并转换成“纽约,NY。”

你能买到很多种服务和数据库,用来帮你实现web服务器调用指令,获取位置信息。有的会涵盖经纬,有的则显示城市名称。这些服务中有的相当昂贵,要花费500美元甚至更多。

我找到了两种解决方案,一个是服务方,那就是社区计划,而另一个则是客户方,就是从Google获取。

利用hostip.info实现基于社区的地理定位

如果你在浏览器中点击进入http://www.hostip.info,它会推测你的位置,并且显示它所估计的你所处位置及周边50到100公里范围的地图。这一项基于社区的项目使用免费可得的数据库作技术支持。在他们的数据库里,已经有超过860万的信息条。

更有趣的是,他们还为已定位的IP地址查询设定了清晰的API

比如:

http://api.hostip.info/get_html.php?ip=12.215.42.19&position=true

  Country: UNITED STATES (US)  City: Sugar Grove, IL  Latitude: 41.7696  Longitude: -88.4588

如果你想添加信息,只要点击:

http://api.hostip.info/?ip=12.215.42.19

你会收到一个包含很多有用信息的XML文档。

所以,我为这个服务编写了一项便捷的.NET包装。注意注释中的XML文件样本。我还为调试设置了一个默认位置。这个代码看上去虽然不是最清楚的,但XML的LINQ让它变得简单易懂。

 1: public class LocationInfo

 2: {

 3: public float Latitude { get; set; }

 4: public float Longitude { get; set; }

 5: public string CountryName { get; set; }

 6: public string CountryCode { get; set; }

 7: public string Name { get; set; }

 8: }

 9:  

 10: public class GeoLocationService

 11: {

 12: public static LocationInfo GetLocationInfo()

 13: {

 14: //TODO: How/where do we refactor this and tidy up the use of Context? This isn't testable.

 15: string ipaddress = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

 16: LocationInfo v = new LocationInfo();

 17:  

 18: if (ipaddress != "127.0.0.1")

 19: v = GeoLocationService.GetLocationInfo(ipaddress);

 20: else //debug locally

 21: v = new LocationInfo()

 22: {

 23: Name = "Sugar Grove, IL",

 24: CountryCode = "US",

 25: CountryName = "UNITED STATES",

 26: Latitude = 41.7696F,

 27: Longitude = -88.4588F

 28: };

 29: return v;

 30: }

 31:  

 32: private static Dictionary<string, LocationInfo> cachedIps = new Dictionary<string, LocationInfo>();

 33:  

 34: public static LocationInfo GetLocationInfo(string ipParam)

 35: {

 36: LocationInfo result = null;

 37: IPAddress i = System.Net.IPAddress.Parse(ipParam);

 38: string ip = i.ToString();

 39: if (!cachedIps.ContainsKey(ip))

 40: {

 41: string r;

 42: using (var w = new WebClient())

 43: {

 44: r = w.DownloadString(String.Format("http://api.hostip.info/?ip={0}&position=true", ip));

 45: }

 46:  

 47: /*

 48:  string r =

 49:  @"<?xml version=""1.0"" encoding=""ISO-8859-1"" ?>

 50:  <HostipLookupResultSet version=""1.0.0"" xmlns=""http://www.hostip.info/api"" xmlns:gml=""http://www.opengis.net/gml"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:schemaLocation=""http://www.hostip.info/api/hostip-1.0.0.xsd"">

 51:  <gml:description>This is the Hostip Lookup Service</gml:description>

 52:  <gml:name>hostip</gml:name>

 53:  <gml:boundedBy>

 54:  <gml:Null>inapplicable</gml:Null>

 55:  </gml:boundedBy>

 56:  <gml:featureMember>

 57:  <Hostip>

 58:  <gml:name>Sugar Grove, IL</gml:name>

 59:  <countryName>UNITED STATES</countryName>

 60:  <countryAbbrev>US</countryAbbrev>

 61:  <!-- Co-ordinates are available as lng,lat -->

 62:  <ipLocation>

 63:  <gml:PointProperty>

 64:  <gml:Point srsName=""http://www.opengis.net/gml/srs/epsg.xml#4326"">

 65:  <gml:coordinates>-88.4588,41.7696</gml:coordinates>

 66:  </gml:Point>

 67:  </gml:PointProperty>

 68:  </ipLocation>

 69:  </Hostip>

 70:  </gml:featureMember>

 71:  </HostipLookupResultSet>";

 72:  */

 73:  

 74: var xmlResponse = XDocument.Parse(r);

 75: var gml = (XNamespace)"http://www.opengis.net/gml";

 76: var ns = (XNamespace)"http://www.hostip.info/api";

 77:  

 78: try

 79: {

 80: result = (from x in xmlResponse.Descendants(ns + "Hostip")

 81: select new LocationInfo

 82: {

 83: CountryCode = x.Element(ns + "countryAbbrev").Value,

 84: CountryName = x.Element(ns + "countryName").Value,

 85: Latitude = float.Parse(x.Descendants(gml + "coordinates").Single().Value.Split(',')[0]),

 86: Longitude = float.Parse(x.Descendants(gml + "coordinates").Single().Value.Split(',')[1]),

 87: Name = x.Element(gml + "name").Value

 88: }).SingleOrDefault();

 89: }

 90: catch (NullReferenceException)

 91: {

 92: //Looks like we didn't get what we expected.

 93: }

 94: if (result != null)

 95: {

 96: cachedIps.Add(ip, result);

 97: }

 98: }

 99: else

 100: {

 101: result = cachedIps[ip];

 102: }

 103: return result;

 104: }

 105: }

我在这里写了些简单的缓存。可能还会加入一些清理项,以控制缓存的大小在几千条IP容量。可能会用FIFO队列,在空间满了之后删除旧的信息。或者用ASP.NET缓存,让它管理内存和一些长期不用的弹出项。

除了在某些点上,这项做得还是不错的。如果我的网站成功了,我会给这些家伙酬金,或者定期去他们免费的数据库下载地理位置信息,并在本地操作整个流程

当把Google’s Ajax Libraries API网站看作是为我自己的网站提供jQuery libraries的资源而不是为我所主导的网站,这样我就注意到…

Google’s AJAX Libraries API中建立了免费的地理定位

Google免费提供一项叫做google.load的服务,可以通过Google Javascript Loader API加载你喜欢的Javascript API,而不是通过本地主机控制。这意味着你和你的站点获得如下好处:

  • 因为是在Google的CDN(内容分发网络)上,文件加载的速度就会很快。
  • 通过新的浏览器,文件实现异步加载。因为它们不会存储到你的站点(大多数浏览器每个DNS/站点对外开放6个HTTP连接,而旧版只开放2个。)
  • 这也是为什么早在90年代,我们已经有www.800.com和images.800.com以保持平衡。YSlow也推荐说,既然DNS查找是有代价的,那就要平衡这种知识技能。像Chrome这样的新浏览器有他们自己的本地DNS缓存来缓解问题。有什么意义呢?知晓这一切,但也要知道这一切将很快被淘汰。
  • 他们提供版本化的API,所以你可以说“给我XXX的第二版”,这样你得到的就是最新的2.24版。

这和地理定位有什么关系呢?大家都知道登录Google.com的时候,他们知道你在哪里。如果你在西班牙,它会自动分配西班牙语网站。因为他们已经知道这些信息,他们就能免费地通过google.loader.ClientLocation来改变你的脚本。他们在八月就公布了这个消息,而我直到今天才知道。

我是这么做的:

 1: <script src="http://www.google.com/jsapi?key=YOURAPIKEY" type="text/javascript"></script>

 2: <script>

 3: google.load("jquery", "1.2.6");

 4: google.load("jqueryui", "1.5.2");

 5: var yourLocation = google.loader.ClientLocation.address.city + ", "

 6: + google.loader.ClientLocation.address.region;

 7: </script>

需要注意的是你得注册获取一个免费的API 密匙以进行操作,这样如果脚本有什么问题,他们可以联系你。

之后,我会使用你们各种的Location JavaScript,放到我的应用里的Search Box中,并基于你们的位置进行查询。如果你们的位置错误,我会想是不是你们在Search Box里键入了不同的城市,我会再试一次,所以我只会打扰你们一次。

如果信息还是不够,你可以用适当的地理定位API,这个使用起来更驾轻就熟,不过需要Google Gears支持

希望你们喜欢!

Comments (0)

Skip to main content