概要
今回はPython(FastAPI)を使ってAPIリクエストを送って、レスポンスを返すというところを行なっていきたいと思います
いろんなリクエストを送れるようにしてレスポンスを返却するということを雑多にやりたいと思います
本題
どこからリクエストされたかをリクエストから取得し、レスポンスする
こちらはレスポンスでリクエスト元のIPを返すAPIになります
IPアドレスから大体の位置をレスポンスしたかったのですが、地理情報のDBデータが必要だそうなので今回は断念します
コード
from fastapi import APIRouter, Request
router = APIRouter()
@router.get("/location/")
def get_location(request: Request):
client_host = request.client.host
return {"your_ip": client_host}
レスポンス
{"your_ip":"xxx.xxx.xxx.xxx"}
単純にリクエストするとリクエスト元のIPアドレスを返却するのみとなっております
改めてFastAPIの基本知識から学んでいますが、routersというものを使うとmain.pyに集約することなく、エンドポイント単位でファイル分割することができて非常に読みやすいです
ディレクトリ構成については最適なもの、推奨されているものが身に付いていないのでこれからも学んでいければと思います
乗り換え検索
yahoo乗換案内でスクレイピングを行なって現在地から目的地までの乗り換え情報をレスポンスで送るようにします
コード
from fastapi import APIRouter
import requests
from bs4 import BeautifulSoup
router = APIRouter()
def search_transit(url: str):
route_response = requests.get(url)
route_soup = BeautifulSoup(route_response.text, "html.parser")
# 経路のサマリーを取得
# route_summary = route_soup.find("div", class_ = "routeSummary")
# # 所要時間を取得
# required_time = route_summary.find("li", class_ = "time").get_text()
# # 乗り換え回数を取得
# transfer_count = route_summary.find("li", class_ = "transfer").get_text()
# # 料金を取得
# fare = route_summary.find("li", class_ = "fare").get_text()
# 駅情報を含むすべての <div> を "station" クラスで検索
results = []
# すべての駅情報と区間情報を含む routeDetail ブロックを取得
route_detail_div = route_soup.find("div", class_="routeDetail")
# routeDetail の子要素を順に処理
children = list(route_detail_div.children)
current_station = {}
for child in children:
if child.name is None:
continue
# -------------------
# 1. 駅情報 (station)
# -------------------
if "station" in child.get("class", []):
station_dt = child.find("dt")
station_name = station_dt.get_text(strip=True) if station_dt else "不明な駅"
times = [li.get_text(strip=True).replace("", "") for li in child.select("ul.time li")]
if not current_station:
current_station = {
"station_name": station_name,
"departure_time": times[0].replace("着", "").replace("発", "").strip() if times else None,
"type": "出発"
}
else:
results.append(current_station)
current_station = {
"station_name": station_name,
"arrival_time": times[0].replace("着", "").replace("発", "").strip() if times and "着" in times[0] else None,
"departure_time": times[1].replace("着", "").replace("発", "").strip() if len(times) > 1 else None,
"type": "乗換" if len(times) > 1 else "到着"
}
if current_station["type"] == "到着":
current_station["arrival_time"] = times[0]
del current_station["departure_time"]
results.append(current_station)
current_station = {}
# -------------------
# 2. 区間情報 (fareSection)
# -------------------
elif "fareSection" in child.get("class", []):
if current_station:
transport_li = child.select_one(".access .transport")
if transport_li:
line_div = transport_li.find("div")
destination_span = line_div.find("span", class_="destination")
destination = destination_span.get_text(strip=True) if destination_span else "N/A"
line_text_parts = [t.strip() for t in line_div.find_all(string=True, recursive=False) if t.strip()]
line_name = line_text_parts[0] if line_text_parts else "N/A"
platform_span = line_div.find("span", class_="platform")
platform = platform_span.get_text(strip=True) if platform_span else "N/A"
current_station["route_line"] = line_name
current_station["route_destination"] = destination
current_station["route_platform"] = platform
return results
@router.get("/transit/")
def get_location(departure: str = "東京", destination: str = "新橋"):
departure_station = departure
destination_station = destination
# 経路の取得先URL
route_url = f"https://transit.yahoo.co.jp/search/print?from={departure_station}&flatlon=&to={destination_station}"
return search_transit(route_url)
レスポンス
[{"station_name":"東京","departure_time":"17:07","type":"出発","route_line":"JR東海道本線","route_destination":"平塚行","route_platform":"[発]10番線→[着]1番線"},{"station_name":"新橋","arrival_time":"17:10","type":"到着"}]
長いコードになりましたが、単に出発から到着までの駅名・駅到着時刻・何番線かなどを返してくれるようにしました
クエリパラメータにdepartureとdestinationに駅名をしていることで目的地までの乗り換え案内をしてくれます
特定の駅名などを手打ちしなければいけないのが改善どころですので、今後の課題です
本当はGoogle Maps APIに使えそうなものがあったのですが、都合上一旦仮で作成しました
いつかリベンジでAPI使って実装してみます
おわりに
いろいろなと言いつつ2つしかエンドポイント作れていませんが、久しぶりにPythonコードが書けて個人的には非常に満足しました
取り組めそうなこともいくつか見つけたので、継続して繋げていければと思います