|
|
|
|
@ -1281,27 +1281,150 @@ class WeatherAPI:
|
|
|
|
|
def get_isp_info(self):
|
|
|
|
|
"""获取ISP信息"""
|
|
|
|
|
try:
|
|
|
|
|
url = "http://ip-api.com/json/"
|
|
|
|
|
response = requests.get(url, timeout=5)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
# 尝试多个ISP信息服务以提高成功率
|
|
|
|
|
|
|
|
|
|
# 方法1: 使用ip-api.com接口
|
|
|
|
|
try:
|
|
|
|
|
url = "http://ip-api.com/json/"
|
|
|
|
|
headers = {'User-Agent': 'MagicWord/1.0'}
|
|
|
|
|
response = requests.get(url, timeout=5, headers=headers)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
if data.get('status') == 'success':
|
|
|
|
|
isp = data.get('isp', '')
|
|
|
|
|
org = data.get('org', '')
|
|
|
|
|
as_info = data.get('as', '')
|
|
|
|
|
country = data.get('country', '')
|
|
|
|
|
return f"{isp} {org} {as_info} {country}".strip()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"ip-api ISP信息获取失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# 方法2: 使用ipinfo.io接口
|
|
|
|
|
try:
|
|
|
|
|
url = "https://ipinfo.io/json"
|
|
|
|
|
headers = {'User-Agent': 'MagicWord/1.0'}
|
|
|
|
|
response = requests.get(url, timeout=5, headers=headers)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
if 'org' in data:
|
|
|
|
|
org = data.get('org', '')
|
|
|
|
|
country = data.get('country', '')
|
|
|
|
|
return f"{org} {country}".strip()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"ipinfo ISP信息获取失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# 方法3: 使用httpbin.org获取基础信息
|
|
|
|
|
try:
|
|
|
|
|
url = "https://httpbin.org/ip"
|
|
|
|
|
headers = {'User-Agent': 'MagicWord/1.0'}
|
|
|
|
|
response = requests.get(url, timeout=5, headers=headers)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
# 这个接口主要用于获取IP,不是ISP信息,但可以作为备选
|
|
|
|
|
data = response.json()
|
|
|
|
|
origin = data.get('origin', '')
|
|
|
|
|
if origin:
|
|
|
|
|
return f"IP: {origin}"
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"httpbin ISP信息获取失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
if data.get('status') == 'success':
|
|
|
|
|
isp = data.get('isp', '')
|
|
|
|
|
org = data.get('org', '')
|
|
|
|
|
as_info = data.get('as', '')
|
|
|
|
|
return f"{isp} {org} {as_info}".strip()
|
|
|
|
|
return None
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"获取ISP信息失败: {e}")
|
|
|
|
|
print(f"获取ISP信息总体失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def get_location_by_ip(self):
|
|
|
|
|
"""通过IP地址获取用户位置"""
|
|
|
|
|
try:
|
|
|
|
|
# 首先获取公网IP地址
|
|
|
|
|
ip_address = None
|
|
|
|
|
try:
|
|
|
|
|
# 使用多个IP获取服务确保能获取到公网IP
|
|
|
|
|
ip_services = [
|
|
|
|
|
"https://api.ipify.org",
|
|
|
|
|
"https://icanhazip.com",
|
|
|
|
|
"https://ident.me",
|
|
|
|
|
"https://ipecho.net/plain",
|
|
|
|
|
"https://myexternalip.com/raw"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for service in ip_services:
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get(service, timeout=3)
|
|
|
|
|
if response.status_code == 200:
|
|
|
|
|
ip_address = response.text.strip()
|
|
|
|
|
# 验证是否为有效的IPv4地址
|
|
|
|
|
import re
|
|
|
|
|
if re.match(r'^(\d{1,3}\.){3}\d{1,3}$', ip_address):
|
|
|
|
|
print(f"获取到公网IP: {ip_address}")
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
ip_address = None
|
|
|
|
|
except:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if not ip_address:
|
|
|
|
|
print("无法获取公网IP地址")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"获取IP地址失败: {e}")
|
|
|
|
|
|
|
|
|
|
# 尝试多个免费的IP地理位置API
|
|
|
|
|
|
|
|
|
|
# API 1: 搜狐IP接口(HTTP,无SSL问题)
|
|
|
|
|
# API 1: 使用ip-api.com接口(更稳定的免费服务,支持HTTPS)
|
|
|
|
|
try:
|
|
|
|
|
if ip_address:
|
|
|
|
|
url = f"https://ip-api.com/json/{ip_address}"
|
|
|
|
|
else:
|
|
|
|
|
url = "https://ip-api.com/json/"
|
|
|
|
|
headers = {'User-Agent': 'MagicWord/1.0'}
|
|
|
|
|
response = requests.get(url, timeout=5, headers=headers)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
if data.get('status') == 'success':
|
|
|
|
|
city = data.get('city', '')
|
|
|
|
|
region = data.get('regionName', '')
|
|
|
|
|
country = data.get('country', '')
|
|
|
|
|
if city and city not in ['null', 'None', '']:
|
|
|
|
|
print(f"ip-api定位成功: {city}, {region}, {country}")
|
|
|
|
|
# 如果城市信息不完整,尝试用地区信息补充
|
|
|
|
|
if len(city) < 2 and region:
|
|
|
|
|
city = region
|
|
|
|
|
return city
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"ip-api接口失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# API 2: 使用ipinfo.io接口(需要处理免费版限制)
|
|
|
|
|
try:
|
|
|
|
|
if ip_address:
|
|
|
|
|
url = f"https://ipinfo.io/{ip_address}/json"
|
|
|
|
|
else:
|
|
|
|
|
url = "https://ipinfo.io/json"
|
|
|
|
|
headers = {'User-Agent': 'MagicWord/1.0'}
|
|
|
|
|
response = requests.get(url, timeout=5, headers=headers)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
if 'city' in data:
|
|
|
|
|
city = data.get('city', '')
|
|
|
|
|
region = data.get('region', '')
|
|
|
|
|
country = data.get('country', '')
|
|
|
|
|
if city and city not in ['null', 'None', '']:
|
|
|
|
|
print(f"ipinfo定位成功: {city}, {region}, {country}")
|
|
|
|
|
# 如果城市信息不完整,尝试用地区信息补充
|
|
|
|
|
if len(city) < 2 and region:
|
|
|
|
|
city = region
|
|
|
|
|
return city
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"ipinfo接口失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# API 3: 搜狐IP接口(HTTP,无SSL问题)
|
|
|
|
|
try:
|
|
|
|
|
url = "http://pv.sohu.com/cityjson?ie=utf-8"
|
|
|
|
|
response = requests.get(url, timeout=5)
|
|
|
|
|
@ -1326,7 +1449,7 @@ class WeatherAPI:
|
|
|
|
|
print(f"搜狐IP接口失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# API 2: 使用pconline接口(HTTP)
|
|
|
|
|
# API 4: 使用pconline接口(HTTP)
|
|
|
|
|
try:
|
|
|
|
|
url = "http://whois.pconline.com.cn/ipJson.jsp"
|
|
|
|
|
response = requests.get(url, timeout=5)
|
|
|
|
|
@ -1349,26 +1472,14 @@ class WeatherAPI:
|
|
|
|
|
print(f"pconline接口失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# API 3: 使用ip-api.com接口(更稳定的免费服务)
|
|
|
|
|
# API 5: 使用淘宝IP接口
|
|
|
|
|
try:
|
|
|
|
|
url = "http://ip-api.com/json/"
|
|
|
|
|
response = requests.get(url, timeout=5)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
if data.get('status') == 'success':
|
|
|
|
|
city = data.get('city', '')
|
|
|
|
|
if city:
|
|
|
|
|
print(f"ip-api定位成功: {city}")
|
|
|
|
|
return city
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"ip-api接口失败: {e}")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# API 4: 使用淘宝IP接口
|
|
|
|
|
try:
|
|
|
|
|
url = "http://ip.taobao.com/outGetIpInfo"
|
|
|
|
|
params = {'ip': '', 'accessKey': 'alibaba-inc'}
|
|
|
|
|
if ip_address:
|
|
|
|
|
url = "http://ip.taobao.com/outGetIpInfo"
|
|
|
|
|
params = {'ip': ip_address, 'accessKey': 'alibaba-inc'}
|
|
|
|
|
else:
|
|
|
|
|
url = "http://ip.taobao.com/outGetIpInfo"
|
|
|
|
|
params = {'ip': '', 'accessKey': 'alibaba-inc'}
|
|
|
|
|
response = requests.get(url, params=params, timeout=5)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
@ -1387,25 +1498,42 @@ class WeatherAPI:
|
|
|
|
|
return None
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"IP定位总体失败: {e}")
|
|
|
|
|
return None
|
|
|
|
|
# 返回默认城市而不是None,确保天气功能仍然可用
|
|
|
|
|
return "北京"
|
|
|
|
|
|
|
|
|
|
def get_current_location(self):
|
|
|
|
|
"""获取当前位置信息"""
|
|
|
|
|
try:
|
|
|
|
|
# 首先尝试通过IP获取位置
|
|
|
|
|
city = self.get_location_by_ip()
|
|
|
|
|
if city:
|
|
|
|
|
print(f"通过IP定位成功: {city}")
|
|
|
|
|
location_result = self.get_location_by_ip()
|
|
|
|
|
|
|
|
|
|
# 检查是否是默认城市(表示IP定位失败)
|
|
|
|
|
if location_result == "北京":
|
|
|
|
|
print("IP定位失败,使用默认城市")
|
|
|
|
|
print("自动定位失败,建议手动选择城市")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
if location_result:
|
|
|
|
|
print(f"通过IP定位成功: {location_result}")
|
|
|
|
|
|
|
|
|
|
# 检查是否是教育网或特殊网络环境
|
|
|
|
|
isp_info = self.get_isp_info()
|
|
|
|
|
if isp_info and ('教育网' in isp_info or 'CERNET' in isp_info or 'University' in isp_info):
|
|
|
|
|
if isp_info and ('教育网' in isp_info or 'CERNET' in isp_info or 'University' in isp_info or '大学' in isp_info):
|
|
|
|
|
print(f"检测到教育网环境: {isp_info}")
|
|
|
|
|
print("教育网IP定位可能不准确,建议手动选择城市")
|
|
|
|
|
# 教育网环境下,如果定位到北京,可能是IP分配问题
|
|
|
|
|
if city.lower() in ['beijing', '北京', 'haidian', '海淀']:
|
|
|
|
|
if isinstance(location_result, str) and location_result.lower() in ['beijing', '北京', 'haidian', '海淀']:
|
|
|
|
|
print("提示:教育网环境下北京定位可能是网络出口导致的")
|
|
|
|
|
return {'city': city, 'note': '教育网环境,定位可能不准确', 'isp': isp_info}
|
|
|
|
|
return {'city': location_result, 'note': '教育网环境,定位可能不准确', 'isp': isp_info}
|
|
|
|
|
|
|
|
|
|
# 处理返回结果格式
|
|
|
|
|
city = None
|
|
|
|
|
if isinstance(location_result, dict) and 'city' in location_result:
|
|
|
|
|
city = location_result['city']
|
|
|
|
|
elif isinstance(location_result, str):
|
|
|
|
|
city = location_result
|
|
|
|
|
else:
|
|
|
|
|
city = str(location_result)
|
|
|
|
|
|
|
|
|
|
# 智能处理 - 如果是区级单位,映射到市级城市
|
|
|
|
|
district_to_city_map = {
|
|
|
|
|
@ -1553,6 +1681,7 @@ class WeatherAPI:
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"获取当前位置失败: {e}")
|
|
|
|
|
# 即使出现异常也返回None而不是抛出异常,确保程序继续运行
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def get_city_weather_by_name(self, city_name):
|
|
|
|
|
|