0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FastAPIで色々なエンドポイント作ってみる

0
Last updated at Posted at 2025-11-29

概要

今回は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コードが書けて個人的には非常に満足しました

取り組めそうなこともいくつか見つけたので、継続して繋げていければと思います

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?