新浪有开放ip查询的接口(http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=123.124.2.85),通过不断查询就能抓取到ip信息。不过要遍历所有的ip地址不现实,数据存储和查询都是问题,一般我们面对的是中国的用户,只需要遍历抓取下中国范围的ip信息就行,而且ip地址最后一位的256个ip一般分配到同一地区,所以最后一位只需遍历.0的ip就可以了。 目前ip4地址已经分配完,中国的ip段可以在以下网址找到: https://www.countryipblocks.net/e_country_data/CN_range.txt 首先需要处理,把上面的ip段转换为具体的ip地址:

private static List HandleIpRange() { var list = new List(); var lines = File.ReadAllLines(“CN_range.txt”); var pattern = @"(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s-\s(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})"; foreach (var line in lines) { var match = Regex.Match(line, pattern, RegexOptions.IgnoreCase); if (match.Success) { var begin = match.Groups[1].Value; var end = match.Groups[2].Value;

                    var beginInt = Ip3ToInt(begin);
                    var endInt = Ip3ToInt(end);

                    while (beginInt <= endInt)
                    {
                        list.Add(IntToIp3(beginInt));
                        beginInt += 1;
                    }
                }
            }

            return list;
        }

        private static int Ip3ToInt(string ip4)
        {
            var arr = ip4.Split('.');
            var p0 = int.Parse(arr[0]);
            var p1 = int.Parse(arr[1]);
            var p2 = int.Parse(arr[2]);

            return (p0 << 16) | (p1 << 8) | p2;
        }

        private static string IntToIp3(int s)
        {
            var p0 = s >> 16 & 0xFF;
            var p1 = s >> 8 & 0xFF;
            var p2 = s & 0xFF;
            return string.Format("{0}.{1}.{2}.0", p0, p1, p2);
        }

剩下只需用多线程请求新浪接口,把结果保存到数据库就可以了。

数据修正: 从新浪接口查询回来的数据,有一部分国家和省市都是空的,还有一部分国家是美国等,这部分数据需要修正下。数据修正可以查询ip138网站,它的数据来源是APNIC,比较新,请求地址如下(http://www.ip138.com/ips8.asp?action=2&ip=223.72.166.0)。把请求回来的内容用正则解析并update到数据库就可以了。

进一步减少数据大小: 新浪接口返回的内容都会带有start和end的ip段,表示ip段中的ip都是属于同一地区。可以把start和end的ip转换为int值,再把int表示的ip段和地区信息保存到另一张表中。查询用户ip所属时,只需把用户ip也转换为int值,再找出int值所在的ip段对应的地区就可以了。