Skip to content

restaurant

gurume.restaurant

PriceRange

Bases: StrEnum

Budget range.

Restaurant dataclass

Restaurant(
    name: str,
    url: str,
    rating: float | None = None,
    review_count: int | None = None,
    save_count: int | None = None,
    area: str | None = None,
    station: str | None = None,
    distance: str | None = None,
    genres: list[str] = list(),
    description: str | None = None,
    lunch_price: str | None = None,
    dinner_price: str | None = None,
    address: str | None = None,
    phone: str | None = None,
    business_hours: str | None = None,
    closed_days: str | None = None,
    reservation_url: str | None = None,
    has_vpoint: bool = False,
    has_reservation: bool = False,
    image_urls: list[str] = list(),
)

Restaurant information.

RestaurantSearchRequest dataclass

RestaurantSearchRequest(
    area: str | None = None,
    keyword: str | None = None,
    genre_code: str | None = None,
    reservation_date: str | None = None,
    reservation_time: str | None = None,
    party_size: int | None = None,
    sort_type: SortType = SortType.STANDARD,
    page: int = 1,
    price_range: PriceRange | None = None,
    online_booking_only: bool = False,
    seat_only: bool = False,
    new_open: bool = False,
    has_private_room: bool = False,
    has_parking: bool = False,
    smoking_allowed: bool = False,
    card_accepted: bool = False,
)

Restaurant search request.

do async

do() -> list[Restaurant]

Run the search asynchronously. Deprecated; use search().

Source code in src/gurume/restaurant.py
580
581
582
async def do(self) -> list[Restaurant]:
    """Run the search asynchronously. Deprecated; use search()."""
    return await self.search()

do_sync

do_sync() -> list[Restaurant]

Run the search synchronously. Deprecated; use search_sync().

Source code in src/gurume/restaurant.py
576
577
578
def do_sync(self) -> list[Restaurant]:
    """Run the search synchronously. Deprecated; use search_sync()."""
    return self.search_sync()

search async

search(
    use_cache: bool = True, use_retry: bool = True
) -> list[Restaurant]

Run the search asynchronously.

Parameters:

Name Type Description Default
use_cache bool

Whether to use the cache. Defaults to True.

True
use_retry bool

Whether to use retry handling. Defaults to True.

True

Returns:

Type Description
list[Restaurant]

List of restaurants.

Source code in src/gurume/restaurant.py
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
async def search(self, use_cache: bool = True, use_retry: bool = True) -> list[Restaurant]:
    """Run the search asynchronously.

    Args:
        use_cache: Whether to use the cache. Defaults to True.
        use_retry: Whether to use retry handling. Defaults to True.

    Returns:
        List of restaurants.
    """
    params = self._build_params()
    headers = {"User-Agent": USER_AGENT}

    area_slug = get_area_slug(self.area) if self.area else None
    url, params = build_search_url_and_params(params, area_slug, self.genre_code)

    # Check cache.
    if use_cache:
        cached_html = cached_get(url, params)
        if cached_html is not None:
            return self._parse_restaurants(cached_html)

    # Execute the request with or without retry handling.
    if use_retry:
        resp = await fetch_with_retry_async(url=url, params=params, headers=headers, request_timeout=30.0)
    else:
        async with requests.AsyncSession(
            timeout=30.0,
            allow_redirects=True,
            impersonate=DEFAULT_IMPERSONATE,
        ) as client:
            resp = await client.get(
                url=url,
                params=params,
                headers=headers,
            )
            resp.raise_for_status()

    # Store in cache.
    if use_cache:
        cache_set(url, params, resp.text, ttl=1800.0)  # 30 minutes TTL

    return self._parse_restaurants(resp.text)

search_sync

search_sync(
    use_cache: bool = True, use_retry: bool = True
) -> list[Restaurant]

Run the search synchronously.

Parameters:

Name Type Description Default
use_cache bool

Whether to use the cache. Defaults to True.

True
use_retry bool

Whether to use retry handling. Defaults to True.

True

Returns:

Type Description
list[Restaurant]

List of restaurants.

Source code in src/gurume/restaurant.py
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
def search_sync(self, use_cache: bool = True, use_retry: bool = True) -> list[Restaurant]:
    """Run the search synchronously.

    Args:
        use_cache: Whether to use the cache. Defaults to True.
        use_retry: Whether to use retry handling. Defaults to True.

    Returns:
        List of restaurants.
    """
    params = self._build_params()
    headers = {"User-Agent": USER_AGENT}

    area_slug = get_area_slug(self.area) if self.area else None
    url, params = build_search_url_and_params(params, area_slug, self.genre_code)

    # Check cache.
    if use_cache:
        cached_html = cached_get(url, params)
        if cached_html is not None:
            return self._parse_restaurants(cached_html)

    # Execute the request with or without retry handling.
    if use_retry:
        resp = fetch_with_retry(url=url, params=params, headers=headers, timeout=30.0)
    else:
        resp = requests.get(
            url=url,
            params=params,
            headers=headers,
            timeout=30.0,
            allow_redirects=True,
            impersonate=DEFAULT_IMPERSONATE,
        )
        resp.raise_for_status()

    # Store in cache.
    if use_cache:
        cache_set(url, params, resp.text, ttl=1800.0)  # 30 minutes TTL

    return self._parse_restaurants(resp.text)

SortType

Bases: StrEnum

Search sort order.

build_search_url_and_params

build_search_url_and_params(
    params: dict[str, Any],
    area_slug: str | None,
    genre_code: str | None,
) -> tuple[str, dict[str, Any]]

Build the Tabelog search URL and adjust params for area/genre filters.

Area-only searches still use /{area_slug}/rstLst/. Cuisine searches now require a cuisine-specific path segment (for example .../yakiniku/ or .../MC0101/) because the legacy LstG query parameter is ignored by current Tabelog pages.

Parameters:

Name Type Description Default
params dict[str, Any]

Search params dict (mutated in place and returned).

required
area_slug str | None

Tabelog area slug like "tokyo" or None.

required
genre_code str | None

Tabelog genre code like "RC0201" or None.

required

Returns:

Type Description
tuple[str, dict[str, Any]]

Tuple of (url, params).

Source code in src/gurume/restaurant.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def build_search_url_and_params(
    params: dict[str, Any],
    area_slug: str | None,
    genre_code: str | None,
) -> tuple[str, dict[str, Any]]:
    """Build the Tabelog search URL and adjust params for area/genre filters.

    Area-only searches still use ``/{area_slug}/rstLst/``. Cuisine searches
    now require a cuisine-specific path segment (for example ``.../yakiniku/`` or
    ``.../MC0101/``) because the legacy ``LstG`` query parameter is ignored by
    current Tabelog pages.

    Args:
        params: Search params dict (mutated in place and returned).
        area_slug: Tabelog area slug like ``"tokyo"`` or ``None``.
        genre_code: Tabelog genre code like ``"RC0201"`` or ``None``.

    Returns:
        Tuple of (url, params).
    """
    url = "https://tabelog.com/rst/rstsearch"
    cuisine_slug = get_cuisine_slug_by_code(genre_code) if genre_code else None
    keyword = params.get("sk")
    if keyword:
        params.setdefault("sw", keyword)
        if genre_code:
            params["LstG"] = genre_code
        return url, params

    if area_slug and cuisine_slug:
        params.pop("sa", None)
        params.pop("LstG", None)
        return f"https://tabelog.com/{area_slug}/rstLst/{cuisine_slug}/", params

    if cuisine_slug and params.get("sa") in (None, "全国"):
        params.pop("sa", None)
        params.pop("LstG", None)
        return f"https://tabelog.com/rstLst/{cuisine_slug}/", params

    if area_slug:
        url = f"https://tabelog.com/{area_slug}/rstLst/"
        params.pop("sa", None)
    if genre_code:
        params["LstG"] = genre_code
    return url, params

query_restaurants cached

query_restaurants(
    area: str | None = None,
    keyword: str | None = None,
    reservation_date: str | None = None,
    reservation_time: str | None = None,
    party_size: int | None = None,
    sort_type: SortType = SortType.STANDARD,
    page: int = 1,
    price_range: PriceRange | None = None,
    online_booking_only: bool = False,
    seat_only: bool = False,
    new_open: bool = False,
    has_private_room: bool = False,
    has_parking: bool = False,
    smoking_allowed: bool = False,
    card_accepted: bool = False,
) -> list[Restaurant]

Quick restaurant lookup.

Source code in src/gurume/restaurant.py
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
@cache
def query_restaurants(
    area: str | None = None,
    keyword: str | None = None,
    reservation_date: str | None = None,
    reservation_time: str | None = None,
    party_size: int | None = None,
    sort_type: SortType = SortType.STANDARD,
    page: int = 1,
    price_range: PriceRange | None = None,
    online_booking_only: bool = False,
    seat_only: bool = False,
    new_open: bool = False,
    has_private_room: bool = False,
    has_parking: bool = False,
    smoking_allowed: bool = False,
    card_accepted: bool = False,
) -> list[Restaurant]:
    """Quick restaurant lookup."""
    return RestaurantSearchRequest(
        area=area,
        keyword=keyword,
        reservation_date=reservation_date,
        reservation_time=reservation_time,
        party_size=party_size,
        sort_type=sort_type,
        page=page,
        price_range=price_range,
        online_booking_only=online_booking_only,
        seat_only=seat_only,
        new_open=new_open,
        has_private_room=has_private_room,
        has_parking=has_parking,
        smoking_allowed=smoking_allowed,
        card_accepted=card_accepted,
    ).search_sync()