11

我正在使用 Google Place API 进行地点搜索:

https://developers.google.com/places/documentation/search

在api的第一次查询之后,我通过设置pagetoken来获取下一页。如果我在请求之间等待 2 秒,它会起作用,但我注意到如果我在前一个查询之后立即进行下一个查询,它会返回状态 INVALID_REQUEST。

这是某种速率限制吗?我在文档中的任何地方都没有看到这一点。

https://developers.google.com/places/usage

由于每个请求有 20 个位置,因此获取 100 个结果的列表将花费 10 多秒,这对于使用应用程序的人来说是很长的等待时间。

4

2 回答 2

29

它已记录在案,请参阅文档

默认情况下,每个附近搜索或文本搜索每个查询最多返回 20 个建立结果;但是,每次搜索最多可以返回 60 个结果,分成三个页面。如果您的搜索将返回超过 20 个,则搜索响应将包含一个附加值 — next_page_token。将 next_page_token 的值传递给新搜索的 pagetoken 参数以查看下一组结果。如果 next_page_token 为 null 或未返回,则没有进一步的结果。从发出 next_page_token 到它生效之间有短暂的延迟。在下一页可用之前请求下一页将返回 INVALID_REQUEST 响应。使用相同的 next_page_token 重试请求将返回下一页结果。

于 2014-01-21T18:18:00.567 回答
10

虽然我不是 100% 确定这是原因,但我会在此处留下这个答案,因为我花了大约 6 个小时才弄清楚这一点,并且可能会对某人有所帮助。

正如 geocodezip 在他的回答中指出的那样,在返回下一页令牌和该页面实际可用之间存在轻微延迟。所以除了使用某种sleep.

但我确实发现,无论我等待 1、2 还是 10 秒,第一个响应之后的每个请求INVALID_REQUEST都给出了响应。INVALID_REQUEST

我怀疑这与谷歌部分的缓存响应有关。我找到的解决方案是将随机增量新参数附加到 URL 以使其成为“不同”的 URL,从而请求无缓存响应。

我使用的参数是request_count,对于每个请求,我将其增加 1。

出于说明目的,这是我使用的 Python POC 代码(不要复制粘贴,因为这只是一个 POC 片段并且不起作用):

# If except raises, it's most likely due to an invalid api key
# The while True is used to keep trying a new key each time
query_result_next_page = None
google_places = GooglePlaces(api_key)
invalid_requests_found = 0
request_count = 0
while True:

    request_count = request_count + 1

    try:
        query_result = google_places.nearby_search(
                lat_lng={'lat': event['latitude'], 'lng': event['longitude']}, 
                radius=event['radius'],
                pagetoken=query_result_next_page,
                request_count=request_count)

        # If there are additional result pages, lets get it on the next while step
        if query_result.has_next_page_token:
            query_result_next_page = query_result.next_page_token
        else:
            break

    except Exception as e:
        # If the key is over the query limit, try a new one
        if str(e).find('OVER_QUERY_LIMIT') != -1:
            logInfo("Key "+api_key+" unavailable.")
            self.set_unavailable_apikey(api_key)
            api_key = self.get_api_key()
        # Sometimes the Places API doesn't create the next page
        # despite having a next_page_key and throws an INVALID_REQUEST.
        # We should just sleep for a bit and try again.
        elif str(e).find('INVALID_REQUEST') != -1:
            # Maximum of 4 INVALID_REQUEST responses
            invalid_requests_found = invalid_requests_found + 1
            if invalid_requests_found > 4:
                raise e

            time.sleep(1)
            continue
        # If it is another error, different from zero results, raises an exception
        elif str(e).find('ZERO_RESULTS') == -1:
            raise e
        else:
            break

编辑:忘了提到该GooglePlaces对象来自slimkrazy 的 Google API lib。不幸的是,我不得不调整实际库的代码以接受这个新request_count参数。

我不得不为此更换nearby_search方法:

def nearby_search(self, language=lang.ENGLISH, keyword=None, location=None,
           lat_lng=None, name=None, radius=3200, rankby=ranking.PROMINENCE,
           sensor=False, type=None, types=[], pagetoken=None, request_count=0):
    """Perform a nearby search using the Google Places API.

    One of either location, lat_lng or pagetoken are required, the rest of 
    the keyword arguments are optional.

    keyword arguments:
    keyword  -- A term to be matched against all available fields, including
                but not limited to name, type, and address (default None)
    location -- A human readable location, e.g 'London, England'
                (default None)
    language -- The language code, indicating in which language the
                results should be returned, if possible. (default lang.ENGLISH)
    lat_lng  -- A dict containing the following keys: lat, lng
                (default None)
    name     -- A term to be matched against the names of the Places.
                Results will be restricted to those containing the passed
                name value. (default None)
    radius   -- The radius (in meters) around the location/lat_lng to
                restrict the search to. The maximum is 50000 meters.
                (default 3200)
    rankby   -- Specifies the order in which results are listed :
                ranking.PROMINENCE (default) or ranking.DISTANCE
                (imply no radius argument).
    sensor   -- Indicates whether or not the Place request came from a
                device using a location sensor (default False).
    type     -- Optional type param used to indicate place category.
    types    -- An optional list of types, restricting the results to
                Places (default []). If there is only one item the request
                will be send as type param.
    pagetoken-- Optional parameter to force the search result to return the next
                20 results from a previously run search. Setting this parameter 
                will execute a search with the same parameters used previously. 
                (default None)
    """
    if location is None and lat_lng is None and pagetoken is None:
        raise ValueError('One of location, lat_lng or pagetoken must be passed in.')
    if rankby == 'distance':
        # As per API docs rankby == distance:
        #  One or more of keyword, name, or types is required.
        if keyword is None and types == [] and name is None:
            raise ValueError('When rankby = googleplaces.ranking.DISTANCE, ' +
                             'name, keyword or types kwargs ' +
                             'must be specified.')
    self._sensor = sensor
    radius = (radius if radius <= GooglePlaces.MAXIMUM_SEARCH_RADIUS
              else GooglePlaces.MAXIMUM_SEARCH_RADIUS)
    lat_lng_str = self._generate_lat_lng_string(lat_lng, location)
    self._request_params = {'location': lat_lng_str}
    if rankby == 'prominence':
        self._request_params['radius'] = radius
    else:
        self._request_params['rankby'] = rankby
    if type:
        self._request_params['type'] = type
    elif types:
        if len(types) == 1:
            self._request_params['type'] = types[0]
        elif len(types) > 1:
            self._request_params['types'] = '|'.join(types)
    if keyword is not None:
        self._request_params['keyword'] = keyword
    if name is not None:
        self._request_params['name'] = name
    if pagetoken is not None:
        self._request_params['pagetoken'] = pagetoken
    if language is not None:
        self._request_params['language'] = language
    self._request_params['request_count'] = request_count
    self._add_required_param_keys()
    url, places_response = _fetch_remote_json(
            GooglePlaces.NEARBY_SEARCH_API_URL, self._request_params)
    _validate_response(url, places_response)
    return GooglePlacesSearchResult(self, places_response)
于 2017-07-19T22:52:16.850 回答