Skip to content

search

gurume.search

SearchMeta dataclass

SearchMeta(
    total_count: int,
    current_page: int,
    results_per_page: int,
    total_pages: int,
    has_next_page: bool,
    has_prev_page: bool,
    search_time: datetime = _now(),
)

搜尋元資料

SearchRequest dataclass

SearchRequest(
    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,
    max_pages: int = 1,
    include_meta: bool = True,
    timeout: float = 30.0,
)

通用搜尋請求 - 擴展 RestaurantSearchRequest

do async

do() -> SearchResponse

異步執行搜尋(已棄用,請使用 search())

Source code in src/gurume/search.py
357
358
359
async def do(self) -> SearchResponse:
    """異步執行搜尋(已棄用,請使用 search())"""
    return await self.search()

do_sync

do_sync() -> SearchResponse

同步執行搜尋(已棄用,請使用 search_sync())

Source code in src/gurume/search.py
353
354
355
def do_sync(self) -> SearchResponse:
    """同步執行搜尋(已棄用,請使用 search_sync())"""
    return self.search_sync()

search async

search() -> SearchResponse

異步執行搜尋

Source code in src/gurume/search.py
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
async def search(self) -> SearchResponse:
    """異步執行搜尋"""
    try:
        all_restaurants: list[Restaurant] = []
        meta = None

        async with httpx.AsyncClient(timeout=self.timeout, follow_redirects=True) as client:
            start_page = self.page
            end_page = self.page + self.max_pages

            for page in range(start_page, end_page):
                request = self._create_restaurant_request(page)
                html, restaurants = await self._search_page_async(client, request)
                all_restaurants.extend(restaurants)
                meta = self._update_meta(meta, html, page)
                if meta and meta.total_count == 0:
                    break

                # 如果這一頁沒有結果,停止搜尋
                if not restaurants:
                    break

        status = SearchStatus.SUCCESS if all_restaurants else SearchStatus.NO_RESULTS
    except SEARCH_EXCEPTIONS as e:
        return SearchResponse(
            status=SearchStatus.ERROR,
            error_message=str(e),
        )
    else:
        return SearchResponse(
            status=status,
            restaurants=all_restaurants,
            meta=meta,
        )

search_sync

search_sync() -> SearchResponse

同步執行搜尋

Source code in src/gurume/search.py
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
def search_sync(self) -> SearchResponse:
    """同步執行搜尋"""
    try:
        all_restaurants: list[Restaurant] = []
        meta = None

        start_page = self.page
        end_page = self.page + self.max_pages

        for page in range(start_page, end_page):
            request = self._create_restaurant_request(page)
            html, restaurants = self._search_page_sync(request)
            all_restaurants.extend(restaurants)
            meta = self._update_meta(meta, html, page)
            if meta and meta.total_count == 0:
                break

            # 如果這一頁沒有結果,停止搜尋
            if not restaurants:
                break

        status = SearchStatus.SUCCESS if all_restaurants else SearchStatus.NO_RESULTS
    except SEARCH_EXCEPTIONS as e:
        return SearchResponse(
            status=SearchStatus.ERROR,
            error_message=str(e),
        )
    else:
        return SearchResponse(
            status=status,
            restaurants=all_restaurants,
            meta=meta,
        )

SearchResponse dataclass

SearchResponse(
    status: SearchStatus,
    restaurants: list[Restaurant] = list(),
    meta: SearchMeta | None = None,
    error_message: str | None = None,
)

搜尋回應

filter

filter(
    condition: Callable[[Restaurant], bool] | None = None,
    min_rating: float | None = None,
    min_review_count: int | None = None,
) -> SearchResponse

過濾餐廳

Parameters:

Name Type Description Default
condition Callable[[Restaurant], bool] | None

自定義過濾條件函數

None
min_rating float | None

最低評分

None
min_review_count int | None

最低評論數

None

Returns:

Type Description
SearchResponse

包含過濾後餐廳的新 SearchResponse

Source code in src/gurume/search.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def filter(
    self,
    condition: Callable[[Restaurant], bool] | None = None,
    min_rating: float | None = None,
    min_review_count: int | None = None,
) -> SearchResponse:
    """過濾餐廳

    Args:
        condition: 自定義過濾條件函數
        min_rating: 最低評分
        min_review_count: 最低評論數

    Returns:
        包含過濾後餐廳的新 SearchResponse
    """
    filtered = self.restaurants

    if min_rating is not None:
        filtered = [r for r in filtered if r.rating and r.rating >= min_rating]

    if min_review_count is not None:
        filtered = [r for r in filtered if r.review_count and r.review_count >= min_review_count]

    if condition is not None:
        filtered = [r for r in filtered if condition(r)]

    return SearchResponse(
        status=self.status,
        restaurants=filtered,
        meta=self.meta,
        error_message=self.error_message,
    )

sort_by

sort_by(key: str, reverse: bool = False) -> SearchResponse

依指定欄位排序

Parameters:

Name Type Description Default
key str

排序欄位(rating, review_count, save_count, name)

required
reverse bool

是否反向排序(預設 False)

False

Returns:

Type Description
SearchResponse

包含排序後餐廳的新 SearchResponse

Source code in src/gurume/search.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def sort_by(self, key: str, reverse: bool = False) -> SearchResponse:
    """依指定欄位排序

    Args:
        key: 排序欄位(rating, review_count, save_count, name)
        reverse: 是否反向排序(預設 False)

    Returns:
        包含排序後餐廳的新 SearchResponse
    """
    sorted_restaurants = sorted(
        self.restaurants,
        key=lambda r: getattr(r, key, 0) or 0,
        reverse=reverse,
    )

    return SearchResponse(
        status=self.status,
        restaurants=sorted_restaurants,
        meta=self.meta,
        error_message=self.error_message,
    )

to_dict

to_dict() -> dict

轉換為字典

Returns:

Type Description
dict

包含所有資料的字典

Source code in src/gurume/search.py
155
156
157
158
159
160
161
162
163
164
165
166
def to_dict(self) -> dict:
    """轉換為字典

    Returns:
        包含所有資料的字典
    """
    return {
        "status": self.status.value,
        "restaurants": [asdict(r) for r in self.restaurants],
        "meta": asdict(self.meta) if self.meta else None,
        "error_message": self.error_message,
    }

to_json

to_json(indent: int = 2) -> str

匯出為 JSON 字串

Parameters:

Name Type Description Default
indent int

JSON 縮排空格數

2

Returns:

Type Description
str

JSON 字串

Source code in src/gurume/search.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def to_json(self, indent: int = 2) -> str:
    """匯出為 JSON 字串

    Args:
        indent: JSON 縮排空格數

    Returns:
        JSON 字串
    """
    data = {
        "status": self.status.value,
        "restaurants": [asdict(r) for r in self.restaurants],
        "meta": asdict(self.meta) if self.meta else None,
        "error_message": self.error_message,
    }
    return json.dumps(data, ensure_ascii=False, indent=indent, default=str)

top

top(n: int) -> SearchResponse

取前 N 筆餐廳

Parameters:

Name Type Description Default
n int

要取的數量

required

Returns:

Type Description
SearchResponse

包含前 N 筆餐廳的新 SearchResponse

Source code in src/gurume/search.py
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def top(self, n: int) -> SearchResponse:
    """取前 N 筆餐廳

    Args:
        n: 要取的數量

    Returns:
        包含前 N 筆餐廳的新 SearchResponse
    """
    return SearchResponse(
        status=self.status,
        restaurants=self.restaurants[:n],
        meta=self.meta,
        error_message=self.error_message,
    )

SearchStatus

Bases: StrEnum

搜尋狀態