Metadata-Version: 2.4
Name: norikae
Version: 1.0.0
Summary: Yahoo!路線情報 乗換案内の検索（HTTP）と検索結果のパース
Author: tikisan
License: MIT License
        
        Copyright (c) 2026 tikisan
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/tikipiya/norikae
Keywords: transit,yahoo,japan,train,norikae,乗換
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: beautifulsoup4>=4.12.0
Dynamic: license-file

# norikae

[Yahoo!路線情報](https://transit.yahoo.co.jp/) の乗換案内を **HTTP GET** で取得し、経路一覧・各区間の列車・途中停車などをパースする **非公式** の Python パッケージです。ブラウザ（Playwright 等）は不要です。

- **取得**: 検索結果ページ `/search/result` をクエリ付きで GET し、HTML を取得
- **パース**: 一覧（`ul#rsltlst`）と、各ルートの詳細（`div.routeDetail` 内の駅・列車・停車駅）

利用条件・著作権・禁止事項は [Yahoo!路線情報](https://transit.yahoo.co.jp/) および [利用規約](https://www.lycorp.co.jp/ja/company/terms/) 等に従ってください。本パッケージは LY 社・ Val Laboratory による公式ツールではありません。

---

## 目次

- [機能一覧](#機能一覧)
- [インストール](#インストール)
- [コマンドライン（CLI）](#コマンドラインcli)
- [出力例](#出力例)
- [Python API](#python-api)
- [取得データの構造](#取得データの構造)
- [検索 URL の仕組み（概要）](#検索-url-の仕組み概要)
- [注意・制限](#注意制限)
- [トラブルシュート](#トラブルシュート)
- [開発・配布](#開発配布)
- [バージョン・作者・ライセンス](#バージョン作者ライセンス)

---

## 機能一覧

| 機能 | 説明 |
|------|------|
| 経路検索（HTTP） | 出発・到着・日時・経由などをクエリに載せて結果 HTML を取得 |
| 一覧パース | 各候補の発着時刻・所要・運賃・乗換回数など |
| 詳細パース | 区間ごとの列車名・番線・乗車位置・**途中停車駅と時刻** |
| 出発日時 | 「今から N 分後」「HH:MM 指定（過去なら翌日）」 |
| CLI | `norikae` コマンド / `python -m norikae` |

---

## インストール

PyPI から:

```bash
pip install norikae
```

依存パッケージは **beautifulsoup4** のみ（標準ライブラリの `urllib` で HTTP）。

リポジトリをクローンして開発する場合:

```bash
git clone https://github.com/tikipiya/norikae.git
cd norikae
pip install -e .
```

インストール後、CLI が使えるか確認:

```bash
norikae --help
python -m norikae --help
```

---

## コマンドライン（CLI）

### 基本形

```bash
norikae --from <出発> --to <到着> [オプション]
```

`--from` / `-f` と `--to` / `-t` は **必須** です。

### オプション一覧

| オプション | 短縮 | 既定 | 説明 |
|------------|------|------|------|
| `--from` | `-f` | （必須） | 出発地（駅名・バス停名など。サイトの検索と同じ表記が安全） |
| `--to` | `-t` | （必須） | 到着地 |
| `--via` | — | なし | 経由地。**複数回**指定すると `via01`, `via02` … の順になる |
| `--minutes` | `-m` | `0` | 出発を **現在時刻から N 分後** にする |
| `--at` | — | なし | 出発時刻 **HH:MM**（例: `9:30`, `14:05`）。**`--minutes` より優先**。すでに過ぎた時刻なら **翌日** として解釈 |
| `--max-summary` | — | `5` | 一覧に表示する **候補ルート数**（上から N 件） |
| `--max-detail` | — | `3` | **詳細テキスト**を出すルート数（`format_verbose` 相当） |
| `--no-departure-line` | — | オフ | 採用した出発日時の行を **表示しない** |
| `--timeout` | — | `60` | HTTP タイムアウト（秒） |

### CLI の使用例

```bash
# 今から 30 分後に出発で検索
norikae --from 愛子 --to 南仙台 --minutes 30

# 当日（または翌日）9:30 発
norikae -f 東京 -t 新大阪 --at 9:30

# 経由: 名古屋（--via を複数回で経由2も可）
norikae -f 東京 -t 大阪 --via 名古屋

# 一覧だけ多め、詳細は 1 ルートだけ
norikae -f 愛子 -t 南仙台 --max-summary 10 --max-detail 1

# ヘルプ
python -m norikae --help
```

---

## 出力例

`norikae -f 愛子 -t 南仙台 -m 0` などで得られる表示の一例です（環境・時刻により検索結果は変わります）。

```text
title: 愛子から南仙台への乗換案内 - Yahoo!路線情報
1 05:30 → 06:09 39分 440 円 乗換： 1 回
2 05:55 → 06:36 41分 440 円 乗換： 1 回
3 06:32 → 07:11 39分 440 円 乗換： 1 回

【ルート 1】 route01
  概要: 05:30 発→ 06:09 着 39分 （乗車 33分 ）
  乗換： 1 回 / IC優先： 440 円 / 22.9km
  乗り換え通過駅: 仙台
  --- 各区間（列車）---
  [1] 愛子 → 仙台
      時刻: 05:30 ～ 05:54 着 06:00 発
      列車: ＪＲ仙山線 当駅始発 仙台行
      乗り場: [発] 情報なし → [着] 7 番線
      乗車位置：[4両] 前 中 / [6両] 前 中
      途中停車 (7 駅):
        - 05:34  陸前落合
        - 05:37  葛岡
        - 05:40  国見(宮城県)
        - 05:42  東北福祉大前
        - 05:45  北山(宮城県)
        - 05:47  北仙台
        - 05:50  東照宮
  [2] 仙台 → 南仙台
      時刻: 05:54 着 06:00 発 ～ 06:09
      列車: ＪＲ東北本線 当駅始発 郡山行
      乗り場: [発] 5 番線 → [着] 情報なし
      途中停車 (2 駅):
        - 06:04  長町
        - 06:06  太子堂

【ルート 2】 route02
  （以下、CLI の --max-detail により省略される場合あり）
```

先頭の `title:` 行はページの `<title>` に相当し、その下の `1 2 3 …` 行は **候補一覧**（`RouteSummary`）。`【ルート N】` 以降は **区間ごとの列車・停車駅**（`RouteDetail` + `TrainLeg`）です。

---

## Python API

### 高水準: `search_transit_route`

出発地・到着地に加え、次のいずれかで日時を決めて **一発で** `SearchResultData` を返します。

- `departure=` … `datetime` を直接指定（これがあると他の日時指定は無視）
- または `minutes_from_now` / `at_clock` / `now` … 内部で `resolve_departure_datetime` を使用

その他の検索条件は `TransitSearchParams` と同じキーワードを **`search_transit_route(..., vias=(...), time_search_type=...)`** のように渡せます。

```python
from norikae import search_transit_route, SearchTimeType

data = search_transit_route(
    "愛子",
    "南仙台",
    minutes_from_now=45,
)

data2 = search_transit_route(
    "東京",
    "新大阪",
    at_clock="14:30",
)

data3 = search_transit_route(
    "東京",
    "大阪",
    vias=("名古屋",),
)

for s in data.summaries:
    print(s.index, s.time_line, s.fare_yen, s.transfers)

for d in data.details:
    print(d.format_verbose())
    for leg in d.legs:
        print(leg.line_and_direction, leg.intermediate_stops)
```

### 中水準: `TransitSearchParams` + `search_transit`

日付・時刻を数値で完全に制御したい場合。

```python
from norikae import TransitSearchParams, search_transit

params = TransitSearchParams(
    from_station="東京",
    to_station="新大阪",
    year=2026,
    month=4,
    day=2,
    hour=9,
    minute=0,
    vias=(),  # または ("名古屋",)
)
data = search_transit(params, timeout=60.0)
```

`datetime` から組み立てる場合:

```python
from norikae import TransitSearchParams, resolve_departure_datetime, search_transit

dep = resolve_departure_datetime(minutes_from_now=20)
params = TransitSearchParams.from_datetime(dep, "東京", "新大阪")
data = search_transit(params)
```

### 低水準: 生 HTML

URL の組み立て・GET・パースを自分で繋ぐ場合。

```python
from norikae import (
    build_search_result_url,
    fetch_transit_search_html,
    parse_search_result_html,
)

url = build_search_result_url(
    "東京",
    "新大阪",
    year=2026,
    month=4,
    day=2,
    hour=9,
    minute=0,
)
html = fetch_transit_search_html(url)
data = parse_search_result_html(html)
```

### 主な公開シンボル（抜粋）

| 名前 | 種別 | 役割 |
|------|------|------|
| `search_transit_route` | 関数 | 高水準検索 |
| `search_transit` | 関数 | `TransitSearchParams` → パース済み結果 |
| `fetch_search_result` | 関数 | パラメータから GET して HTML 文字列 |
| `fetch_transit_search_html` | 関数 | 任意 URL の HTML |
| `parse_search_result_html` | 関数 | HTML → `SearchResultData` |
| `build_search_result_url` | 関数 | GET 用 URL（`vias`, `extra_query` 可） |
| `resolve_departure_datetime` | 関数 | 相対分・`at_clock` から `datetime` |
| `TransitSearchParams` | データクラス | 検索クエリ一式 |
| `SearchResultData` | データクラス | パース結果のルート |
| `RouteSummary` / `RouteDetail` / `TrainLeg` / `IntermediateStop` | データクラス | 一覧・詳細・区間・停車駅 |
| `SearchTimeType` / `DisplaySort` / `WalkSpeed` / `TicketKind` / `SeatKind` | Enum | クエリの列挙値 |
| `DepartureTimeError` | 例外 | `at_clock` 形式不正など |

---

## 取得データの構造

### `SearchResultData`

| フィールド | 内容 |
|------------|------|
| `page_title` | 検索結果ページのタイトル文字列 |
| `summaries` | `list[RouteSummary]` 候補の一覧（画面上部のリスト） |
| `details` | `list[RouteDetail]` 各候補の詳細ブロック |

### `RouteSummary`（一覧 1 行相当）

| フィールド | 内容 |
|------------|------|
| `route_id` | 例: `route01` |
| `index` | ルート番号（整数） |
| `time_line` | 発着・所要などの要約テキスト |
| `fare_yen` | 運賃表示のテキスト |
| `transfers` | 乗換回数などのテキスト |

### `RouteDetail`（詳細 1 ブロック）

| フィールド | 内容 |
|------------|------|
| `route_anchor` | `route01` など |
| `title` | 見出し（例: ルート 1） |
| `time_summary` / `transfers` / `fare` / `distance_km` | 概要行の各要素 |
| `legs` | `list[TrainLeg]` 列車区間のリスト |
| `format_verbose()` | 人間向けの複数行テキスト（CLI の詳細表示と同系） |

### `TrainLeg`（1 乗車区間）

| フィールド | 内容 |
|------------|------|
| `departure_station` / `arrival_station` | 区間の端の駅名 |
| `departure_times` / `arrival_times` | 駅ブロックから取った時刻文字列 |
| `line_and_direction` | 路線名・方面など |
| `platform` | 番線情報 |
| `riding_position` | 乗車位置（車両両数など） |
| `intermediate_stops` | `list[IntermediateStop]` 途中停車 |
| `segment_fare_note` | 特急区間などの料金メモがあれば |

### `IntermediateStop`

| フィールド | 内容 |
|------------|------|
| `time` | 時刻 |
| `station_name` | 駅名 |

---

## 検索 URL の仕組み（概要）

本パッケージは、ブラウザがフォーム送信で開くのと同様に、概ね次の形の URL に **GET** します。

```text
https://transit.yahoo.co.jp/search/result?from=...&to=...&y=...&m=...&d=...&hh=...&m1=...&m2=...&...
```

- 分は HTML フォームの `mm` ではなく、クエリでは **`m1`（十の位）と `m2`（一の位）** に分かれる形式です。`minute_to_query_parts` がその変換を行います。
- 経由は `via01`, `via02`, … として付与します。
- 列挙型でない追加パラメータは `TransitSearchParams.extra_query` に `dict[str, str]` で渡せます（サイト側が受け付けるキーに限る）。

---

## 注意・制限

1. **駅名の表記**  
   ブラウザのサジェストで確定した駅名と、手入力の揺れで結果が変わることがあります。意図した停車駅にならない場合は、サイト上の表記に合わせてください。

2. **利用ポリシー**  
   短時間に大量リクエストを送らない、商用利用は各種規約を確認する、などご利用は自己責任でお願いします。

3. **HTML 変更**  
   Yahoo!路線情報のマークアップが変わると、パースが一部失敗する可能性があります。

4. **取得できないもの**  
   ログイン後のみの機能、JavaScript 実行後にしか出ない情報などは、本パッケージの対象外です。

---

## トラブルシュート

| 現象 | 確認すること |
|------|----------------|
| `URLError` / タイムアウト | ネットワーク、`--timeout` の延長、ファイアウォール |
| 一覧は空、パース結果が空 | HTML 構造変更の可能性。`fetch_search_result` の生 HTML を保存して issue に |
| 駅が別名に解釈される | 出発・到着の文字列をサイト検索と揃える |
| Windows で文字化け | コンソールのコードページ。UTF-8 出力（`chcp 65001`）やファイルへリダイレクトを検討 |

---

## 開発・配布

```bash
pip install build twine
python -m build
twine check dist/*
twine upload dist/*
```

ローカルでサンプルを動かす:

```bash
pip install -e .
python examples/demo_route_search.py
```

---

## バージョン・作者・ライセンス

- **バージョン**: 1.0.0（`norikae.__version__` と同じ）
- **作者**: tikisan
- **リポジトリ**: [github.com/tikipiya/norikae](https://github.com/tikipiya/norikae)
- **ライセンス**: MIT（`LICENSE` ファイル）
