| from flask import Flask, request, render_template_string |
| import requests |
|
|
| app = Flask(__name__) |
|
|
| INDEX_HTML = """ |
| <!DOCTYPE html> |
| <html lang="ja"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>入力フォーム</title> |
| </head> |
| <body> |
| <h2>⇂名前を入力</h2> |
| <form action="/next" method="POST"> |
| <input type="text" name="name" placeholder="本名以外の名前" required> |
| <p style="color: red;">本名は絶対に入力しないでください</p> |
| |
| <label><input type="radio" name="send_mode" value="all" checked> 県名・市区町村まで特定して!</label><br> |
| <label><input type="radio" name="send_mode" value="pref_only"> 県名のみにしときます。</label><br> |
| <label><input type="radio" name="send_mode" value="none"> 技術だけ見たいです。みんなに市区町村を知られたくないです。</label><br><br> |
| |
| <button type="submit">続ける</button> |
| </form> |
| </body> |
| </html> |
| """ |
|
|
| MAP_HTML = """ |
| <!DOCTYPE html> |
| <html lang="ja"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>現在地マップ</title> |
| <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" /> |
| <style> |
| #map { height: 100vh; width: 100vw; } |
| </style> |
| </head> |
| <body> |
| |
| <div id="map"></div> |
| |
| <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script> |
| <script> |
| var lat = {{ lat }}; |
| var lon = {{ lon }}; |
| |
| var map = L.map('map').setView([lat, lon], 12); |
| |
| L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { |
| maxZoom: 18 |
| }).addTo(map); |
| |
| L.marker([lat, lon]).addTo(map) |
| .bindPopup("推定位置:{{ prefecture }} {{ city }}") |
| .openPopup(); |
| </script> |
| |
| </body> |
| </html> |
| """ |
|
|
| |
| |
| |
|
|
| CORS_PROXY = "https://cors.isomorphic-git.org/" |
|
|
| def safe_request_json(url, timeout=2, use_proxy_on_fail=True): |
| """ |
| 安全に GET JSON を取得する。 |
| 失敗時は CORS プロキシで再試行する。 |
| """ |
| try: |
| res = requests.get(url, timeout=timeout) |
| if res.status_code == 200: |
| return res.json() |
| else: |
| raise Exception(f"HTTP {res.status_code}") |
| except Exception: |
| if use_proxy_on_fail: |
| try: |
| proxy_url = CORS_PROXY + url |
| res = requests.get(proxy_url, timeout=timeout) |
| if res.status_code == 200: |
| return res.json() |
| except Exception: |
| pass |
|
|
| return None |
|
|
|
|
| |
| |
| |
|
|
| def get_geo_ipapi(ip): |
| url = f"http://ip-api.com/json/{ip}?lang=ja" |
| res = safe_request_json(url) |
| if res and res.get("status") == "success": |
| return { |
| "prefecture": res.get("regionName", ""), |
| "city": res.get("city", ""), |
| "lat": res.get("lat"), |
| "lon": res.get("lon") |
| } |
| return fallback() |
|
|
|
|
| def get_geo_ipwho(ip): |
| url = f"https://api.ipwho.org/ip/{ip}?format=json" |
| res = safe_request_json(url) |
| if res and res.get("success") is True: |
| data = res.get("data", {}) |
| return { |
| "prefecture": data.get("region", ""), |
| "city": data.get("city", ""), |
| "lat": data.get("latitude"), |
| "lon": data.get("longitude"), |
| "capital": data.get("capital", "") |
| } |
| return fallback(capital="") |
|
|
|
|
| |
| |
| |
|
|
| def get_geo_reallyfreegeoip(ip): |
| url = f"https://reallyfreegeoip.org/json/{ip}" |
| res = safe_request_json(url) |
| if res: |
| return { |
| "prefecture": res.get("region_name", ""), |
| "city": res.get("city", ""), |
| "lat": res.get("latitude"), |
| "lon": res.get("longitude") |
| } |
| return fallback() |
|
|
|
|
|
|
| def get_geo_ipapico(ip): |
| url = f"https://ipapi.co/{ip}/json/" |
| res = safe_request_json(url) |
| if res: |
| return { |
| "prefecture": res.get("region_name", res.get("region", "")), |
| "city": res.get("city", ""), |
| "lat": res.get("latitude"), |
| "lon": res.get("longitude") |
| } |
| return fallback() |
|
|
|
|
| def get_geo_iplocalize(ip): |
| url = f"https://iplocalize.com/api/v1/lookup/{ip}" |
| res = safe_request_json(url) |
| if res: |
| return { |
| "prefecture": res.get("region", ""), |
| "city": res.get("city", ""), |
| "lat": res.get("latitude"), |
| "lon": res.get("longitude") |
| } |
| return fallback() |
|
|
|
|
| |
| |
| |
|
|
| def fallback(capital=None): |
| base = { |
| "prefecture": "", |
| "city": "", |
| "lat": 35.681236, |
| "lon": 139.767125 |
| } |
| if capital is not None: |
| base["capital"] = capital |
| return base |
|
|
|
|
| |
| |
| |
|
|
| @app.route("/", methods=["GET"]) |
| def index(): |
| return render_template_string(INDEX_HTML) |
|
|
|
|
| @app.route("/next", methods=["POST"]) |
| def next_page(): |
| name = request.form.get("name", "").strip() |
| mode = request.form.get("send_mode") |
|
|
| xff = request.headers.get("X-Forwarded-For") |
| if xff: |
| ip = xff.split(",")[0].strip() |
| else: |
| ip = request.remote_addr |
|
|
| print(ip) |
| |
| api1 = get_geo_ipapi(ip) |
| api2 = get_geo_ipwho(ip) |
| api3 = get_geo_reallyfreegeoip(ip) |
| api5 = get_geo_ipapico(ip) |
| api6 = get_geo_iplocalize(ip) |
|
|
| |
| def mask(mode, prefecture, city): |
| if mode == "all": |
| return prefecture, city |
| elif mode == "pref_only": |
| return prefecture, "" |
| else: |
| return "", "" |
|
|
| apis = [api1, api2, api3, api5, api6] |
| names = ["ip-api", "IPWho", "ReallyFreeGeoIP", "ipapi.co", "iplocalize"] |
|
|
| for api, name_api in zip(apis, names): |
| pref, city = mask(mode, api.get("prefecture", ""), api.get("city", "")) |
| print(f"[{name_api}] 名前:{name} {pref} {city}") |
|
|
| |
| return render_template_string( |
| MAP_HTML, |
| lat=api1["lat"], |
| lon=api1["lon"], |
| prefecture=api1["prefecture"], |
| city=api1["city"] |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| app.run(host="0.0.0.0", port=7860) |
|
|