IPWeatherObject.py 8.9 KB

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