IPWeatherObject.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # @Author : Rocky
  2. # @File : IPWeatherObject.py
  3. # @Time : 2023/8/16 8:56
  4. import datetime
  5. import geoip2.webservice
  6. import requests
  7. from Model.models import IPAddr, OpenWeatherMapCallCount
  8. from Object.AWS.S3Email import S3Email
  9. from Object.RedisObject import RedisObject
  10. from Service.CommonService import CommonService
  11. from Ansjer.config import LOGGER, CONFIG_INFO
  12. class IPQuery:
  13. """
  14. 阿里云IP地址查询
  15. https://market.aliyun.com/products/57002003/cmapi021970.html?spm=5176.2020520132.recommend-card.dbottom-suggestion.33e17218GYjWDt#sku=yuncode15970000020
  16. """
  17. def __init__(self, ip):
  18. self.appcode = 'd7d63b34b1d54214be446608a57ff0a2'
  19. self.host = 'https://c2ba.api.huachen.cn'
  20. self.path = '/ip'
  21. self.district = '' # 区
  22. self.city = '' # 市
  23. self.region = '' # 省/州
  24. self.country_id = ''
  25. param = 'ip=' + ip
  26. url = self.host + self.path + '?' + param
  27. # 获取ip的区级和城市信息
  28. headers = {'Authorization': 'APPCODE ' + self.appcode}
  29. res = requests.get(url=url, headers=headers)
  30. if res.status_code == 200:
  31. # 反序列化响应数据
  32. res_data = eval(res.content.decode('utf-8'))
  33. if res_data['ret'] == 200:
  34. district = res_data['data']['district']
  35. city = res_data['data']['city']
  36. region = res_data['data']['region']
  37. country_id = res_data['data']['country_id']
  38. # 国内城市数据不为空字符,拼上'市'字
  39. if country_id == 'CN' and city != '':
  40. city += '市'
  41. # ip地址信息存表或更新
  42. ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=False)
  43. if ip_addr_qs.exists():
  44. ip_addr_qs.update(district=district, city=city, region=region, country_code=country_id)
  45. else:
  46. IPAddr.objects.create(ip=ip, district=district, city=city, region=region, country_code=country_id)
  47. # 确定天气城市
  48. if district != '':
  49. self.district = district
  50. elif city != '':
  51. self.district = city
  52. self.country_id = country_id
  53. class WeatherInfo:
  54. """
  55. 阿里云墨迹天气服务
  56. https://market.aliyun.com/products/57096001/cmapi013828.html?spm=5176.2020520132.101.19.2b8f7218NuiGPd#sku=yuncode782800000
  57. """
  58. def __init__(self, city_id):
  59. self.appcode = 'd7d63b34b1d54214be446608a57ff0a2'
  60. self.headers = {'Authorization': 'APPCODE ' + self.appcode,
  61. 'Content-Type': 'application/x-www-form-urlencoded'}
  62. self.city_id = city_id
  63. def get_city_weather(self):
  64. url = "https://aliv18.data.moji.com/whapi/json/alicityweather/condition" # 获取实时天气
  65. data = {'cityId': self.city_id}
  66. response = requests.post(url, headers=self.headers, data=data, verify=False)
  67. if response.status_code == 200:
  68. result = response.json()
  69. if result['code'] == 0:
  70. # 返回温湿度
  71. return result['data']['condition']['temp'], result['data']['condition']['humidity']
  72. return None, None
  73. def get_city_24_weather(self):
  74. url = 'https://aliv18.data.moji.com/whapi/json/alicityweather/forecast24hours' # 获取城市24小时天气
  75. data = {'cityId': self.city_id}
  76. response = requests.post(url, headers=self.headers, data=data, verify=False)
  77. if response.status_code == 200:
  78. result = response.json()
  79. if result['code'] == 0:
  80. # 返回天气列表
  81. return result['data']['hourly']
  82. return None
  83. class GeoIP2:
  84. """
  85. MaxMind GeoIP2查询国外ip
  86. 同时保存ip信息
  87. https://www.maxmind.com/
  88. """
  89. def __init__(self, ip):
  90. self.account_id = 938644
  91. self.license_key = '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'
  92. self.license_key_sandbox = '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'
  93. try:
  94. with geoip2.webservice.Client(self.account_id, self.license_key) as client:
  95. # You can also use `client.city` or `client.insights`
  96. # `client.insights` is not available to GeoLite2 users
  97. response = client.city(ip)
  98. # 经纬度精确到小数点两位
  99. lat = round(response.location.latitude, 2)
  100. lon = round(response.location.longitude, 2)
  101. # 获取中文或英文城市名,省/州
  102. city = ''
  103. city_names = response.city.names
  104. city_cn = city_names.get('zh-CN')
  105. if city_cn:
  106. city = city_cn
  107. elif city_names.get('en'):
  108. city = city_names['en']
  109. region = ''
  110. subdivisions_names = response.subdivisions[0].names
  111. region_cn = subdivisions_names.get('zh-CN')
  112. if region_cn:
  113. region = region_cn
  114. elif subdivisions_names.get('en'):
  115. region = subdivisions_names['en']
  116. country_code = response.country.iso_code
  117. # 保存ip信息
  118. ip_addr_data = {
  119. 'ip': ip,
  120. 'lat': lat,
  121. 'lon': lon,
  122. 'city': city,
  123. 'region': region,
  124. 'country_code': country_code,
  125. 'is_geoip2': True
  126. }
  127. IPAddr.objects.create(**ip_addr_data)
  128. except Exception as e:
  129. LOGGER.info('GeoIP2解析ip异常:error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  130. class OpenWeatherMap:
  131. """
  132. OpenWeatherMap查询国外天气服务
  133. https://openweathermap.org/
  134. """
  135. def __init__(self, lat, lon):
  136. self.appid = '7a6cd7dfeb034ededa451ed575788857'
  137. self.lat = lat # 纬度
  138. self.lon = lon # 经度
  139. def get_weather(self):
  140. """
  141. 从缓存查询天气数据
  142. 或者查询当前天气,并缓存数据
  143. @return: temp, humidity
  144. """
  145. # 查询缓存数据
  146. today = datetime.datetime.today()
  147. now_time = datetime.datetime(today.year, today.month, today.day, today.hour)
  148. str_time = now_time.strftime('%Y-%m-%d %H:%M:%S')
  149. time_stamp = CommonService.str_to_timestamp(str_time)
  150. key = 'weather:lat:{}_lon:{}_time_stamp:{}'.format(self.lat, self.lon, time_stamp)
  151. redis_obj = RedisObject()
  152. weather = redis_obj.get_data(key)
  153. if weather:
  154. temp, humidity = weather.split('/')
  155. else:
  156. temp, humidity = self.get_current_weather(str_time[:7])
  157. if temp is not None and humidity is not None:
  158. key = 'weather:lat:{}_lon:{}_time_stamp:{}'.format(self.lat, self.lon, time_stamp)
  159. redis_obj.set_ex_data(key, '{}/{}'.format(temp, humidity), 3600)
  160. return temp, humidity
  161. def get_current_weather(self, month):
  162. """
  163. 根据经纬度获取当前天气
  164. @param month: 年月份
  165. @return: temp, humidity
  166. """
  167. url = 'https://api.openweathermap.org/data/2.5/weather'
  168. params = {
  169. 'lat': self.lat,
  170. 'lon': self.lon,
  171. 'appid': self.appid,
  172. 'units': 'metric' # 公制单位,温度单位:摄氏度
  173. }
  174. res = requests.get(url=url, params=params, timeout=10)
  175. # 记录调用次数
  176. open_weather_map_call_count_qs = OpenWeatherMapCallCount.objects.filter(month=month).values('count')
  177. if not open_weather_map_call_count_qs.exists():
  178. OpenWeatherMapCallCount.objects.create(month=month)
  179. else:
  180. count = open_weather_map_call_count_qs[0]['count'] + 1
  181. open_weather_map_call_count_qs.update(count=count)
  182. # 调用次数超过750,000,邮件提醒
  183. warning_count = 750000
  184. if count > warning_count:
  185. redis_obj = RedisObject()
  186. key = 'open_weather_map_call_count_warning_time_limit'
  187. time_limit = redis_obj.get_data(key)
  188. if not time_limit:
  189. # 限制一天提醒一次
  190. redis_obj.set_data(key, 1, 60 * 60 * 24)
  191. subject = '邮件提醒'
  192. data = '{}服open weather调用次数大于{}'.format(CONFIG_INFO, warning_count)
  193. S3Email().send_email(subject, data, 'servers@ansjer.com')
  194. if res.status_code != 200:
  195. return None, None
  196. res_data = eval(res.text)
  197. if res_data['cod'] != 200:
  198. return None, None
  199. temp = int(res_data['main']['temp'])
  200. humidity = int(res_data['main']['humidity'])
  201. return temp, humidity