289732 / app.py
soiz1's picture
Update app.py
9470606 verified
Raw
History Blame Contribute Delete
6.69 kB
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/エラー対策
##########################################
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
##########################################
# 既存 API(エラーハンドリング強化 + CORS fallback)
##########################################
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="")
##########################################
# 新規追加 API(エラーハンドリング統一)
##########################################
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()
##########################################
# Fallback 共通
##########################################
def fallback(capital=None):
base = {
"prefecture": "",
"city": "",
"lat": 35.681236,
"lon": 139.767125
}
if capital is not None:
base["capital"] = capital
return base
##########################################
# Flask Routes
##########################################
@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() # 先頭のクライアント IP を使う
else:
ip = request.remote_addr
print(ip)
# 各 API を使用
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}")
# 地図表示は ip-api を優先
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)