工作上的任务:依据客户的IP地址,获取其IP对应的经纬度,从而得到其地理位置。最好精确到市、区、镇、街道、门牌号。代码如下。
原始的表格文件如下所示:、
logindate loginip customerid
2018-7-18 23:46 223.72.61.201 97873941
2018-7-18 23:46 222.36.47.36 97599064
2018-7-18 23:46 159.226.179.90 108089419
2018-7-18 23:47 124.126.157.119 97973378
2018-7-18 23:47 122.70.145.67 108453307
2018-7-18 23:47 39.155.214.154 105427809
2018-7-18 23:47 114.242.248.135 108861637
2018-7-18 23:47 120.221.20.195 107031927
2018-7-18 23:47 117.136.38.176 105815735
2018-7-18 23:47 223.72.89.235 107899331
2018-7-18 23:47 118.168.148.250 95506679
2018-7-18 23:47 223.72.61.201 97873941
2018-7-18 23:47 114.250.102.31 108732919
2018-7-18 23:47 111.199.221.83 98066595
2018-7-18 23:48 223.72.60.17 98440300
2018-7-18 23:48 223.72.81.86 108860194
2018-7-18 23:48 119.6.87.59 105401342
2018-7-18 23:48 223.72.65.44 97918361
2018-7-18 23:48 223.72.47.104 108620068
2018-7-18 23:48 223.72.89.235 107899331
2018-7-18 23:48 223.72.61.201 97873941
2018-7-18 23:49 115.171.133.45 107136147
2018-7-18 23:51 114.248.187.60 105501306
代码如下:
# -*- coding: utf-8 -*-
import requests
from lxml import etree
from pandas import read_csv
import pandas as pd
# 从本地csv文件读取到python中,转换为pandas对象
df = read_csv(open('123456-copy-test.csv','r'))
data = pd.DataFrame(df)
#print(data)
# data_drop为去重之后的列表,去掉customerid中有重复数字的行,且保留重复的第一行
data_drop = data.drop_duplicates(['customerid'])
#print(data_drop)
#print(data_drop.customerid.is_unique)
#print(len(data_drop.index))
# 对上述dataframe删除原来索引,建立从0开始的新索引
data_drop_re = data_drop.reset_index(drop=True)
#print(data_drop_re)
#接下来将表格中的ip导入脚本中,查询地址
header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64\
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
# Get_Position函数,输入IP地址,输出其对应的经度和纬度
def GetInfo_Position(ip):
web_IP = 'http://www.882667.com/ip_' + ip + '.html' # 调用查询经纬度的网址
response = requests.get(web_IP, headers = header)
Position_xml = etree.HTML(response.text)
info_Position = Position_xml.xpath('//span[@class="lansezi"]')
info_Dimension = info_Position[1].xpath('string(.)').strip() # 获取经度
info_Longitude = info_Position[-1].xpath('string(.)').strip() # 获取纬度
#print(info_Dimension, info_Longitude)
return (info_Dimension, info_Longitude) #在Console窗体输出经度、纬度
#Get_Geography函数,输入经纬度,输出其地理位置
def Get_Geography(ip):
GetInfo_Position(ip)
dimension = GetInfo_Position(ip)[0]
longitude = GetInfo_Position(ip)[1]
# 通过百度API得到地理位置
items = {'location':str(dimension) + ',' + str(longitude), 'ak':'ov2Gp9rYiXpsQEl0EMYqg2HZ5MSLe3Uz', 'output':'json'}
res = requests.get('http://api.map.baidu.com/geocoder/v2/', params=items)
result = res.json()
result = result['result']['formatted_address'] + ' ' \
+ result['result']['business']+ ' ' \
+ result['result']['sematic_description']
#print(GetInfo_Position(ip))
print('该IP的经纬度大致为:' + GetInfo_Position(ip)[0] + ' , ' + GetInfo_Position(ip)[1])
print(result)
print('\t')
# 最后的地理位置,可以与http://www.gpsspg.com/maps.htm网址进行对比验证
for i in range(0,len(data_drop_re.index)):
print('第' + i +'次测量')
IP = str(data_drop_re.loginip[i])
# print(data_drop_re.loginip[i])
print('待测IP为:' + IP)
Get_Geography(IP)
print('OK')
经测试,由于国内大多数使用的是动态IP,因此这种定位地理位置的方法很不准确。
更新:找到了一个国外的网站,通过IP定位到经纬度,比较准确,查询次数在1000次以上就会被封禁。另外,京东万象上也提供了Ip精准定位的API借口,但是需要付费。详细代码如下:
# -*- coding: utf-8 -*-
# 作者:yangsong
# 功能:根据IP地址,获得其地理地址
# 时间:2018-7-30 15:53:59
# 思路: 1.读取csv文件,删除重复的行数据;
# 2.爬取https://db-ip.com/,利用此网站将Ip批量转化经纬度,并将经纬度提取出来
# 3.利用百度地图API,通过经纬度批量得到地理位置
# 4.将上述经纬度、地理位置导出到另外的csv文件中
# Bug:由于调用网站在域外,程序运行速度较慢,且每日爬一千次以上就会封IP
import requests
import pandas as pd
from lxml import etree
from pandas import read_csv
# 导入本地csv文件中的数据
df = read_csv(open('123456-copy.csv','r'))
data = pd.DataFrame(df)
# 数据清洗,去除重复数据
data_drop = data.drop_duplicates(['customerid'])
#print(data_drop.customerid.is_unique) # 若输出为True,表名DataFrame类中没有重复数据
# 对上述dataframe删除原来索引,建立从0开始的新索引
data_drop_re = data_drop.reset_index(drop=True)
# 添加三个新列,并重新命名
data_drop_re['Latitude'] = None # 纬度
data_drop_re['Longitude'] = None # 经度
data_drop_re['Geography'] = None # 地理位置
# 新建三个列对象,用于存储纬度、经度、地理位置数据
Latitude = []
Longitude = []
Geography = []
header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64\
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.35'}
# 定义Get_Lat_Lon函数:输入IP地址,输出其对应的纬度和经度
def Get_Lat_Lon(ip):
web_IP = 'https://db-ip.com/' + ip
response = requests.get(web_IP, headers = header)
Position_xml = etree.HTML(response.text)
info_Coordinates = Position_xml.xpath('//tr/td')[-5].xpath('string(.)').strip()
Coordinates = info_Coordinates.replace(',','').split()
info_Latitude = Coordinates[0] # 获得纬度
info_Longitude = Coordinates[1] # 获得经度
Latitude.append(info_Latitude) # 将每个IP对应的纬度加到Latitude[]中
Longitude.append(info_Longitude) # 将每个IP对应的经度加到Longitude[]中
return (info_Latitude, info_Longitude)
# 定义Get_Geography函数:输入IP,调用Get_Lat_Lon(ip),输出其地理位置
def Get_Geography(ip):
Get_Lat_Lon(ip)
latitude = Latitude[-1]
longitude = Longitude[-1]
# 通过百度API得到地理位置
items = {'location':str(latitude) + ',' + str(longitude), 'ak':'ov2Gp9rYiXpsQEl0EMYqg2HZ5MSLe3Uz', 'output':'json'}
res = requests.get('http://api.map.baidu.com/geocoder/v2/', params=items)
result = res.json()
# result为详细的地理位置
result = result['result']['formatted_address'] + ' ' \
+ result['result']['business']+ ' ' \
+ result['result']['sematic_description']
#Geography.append(result) # 将每个IP对应的地理位置加到Geography[]中
return (result)
# 执行循环语句,将IP地址批量转化为地理位置
for i in range(0,len(data_drop_re.index)):
IP = str(data_drop_re.loginip[i])
Geography.append(Get_Geography(IP))
# 将纬度、经度、地理位置写入dataframe对象当中
data_drop_re.iloc[i, 3] = Latitude[i]
data_drop_re.iloc[i, 4] = Longitude[i]
data_drop_re.iloc[i, 5] = Geography[i]
# 写入csv,在循环体内写入。保证即使程序中断,也能有数据录入csv
data_drop_re.to_csv('Get_Geography.csv')
print('第' + str(i+1) +'次测量')
print('待测IP为:' + IP)
print(Geography[i])
print(data_drop_re)
print('\t')
print('-------------------------------------OK------------------------------------')