【问题标题】:Paging on Google Places API returns status INVALID_REQUESTGoogle Places API 上的分页返回状态 INVALID_REQUEST
【发布时间】:2014-02-11 11:56:03
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

    标签: google-maps google-places-api rate-limiting


    【解决方案1】:

    已记录在案,请参阅documentation

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

    【讨论】:

    • 有没有其他人注意到,如果您收到一个 INVALID_REQUEST,那么无论您等待多长时间,如果您重试并发布相同的 pagetoken(根据文档),您会不断收到 INVALID_REQUESTS?我发现获得多页结果的唯一方法是足够幸运,当我第一次请求辅助页面时,我已经等了足够长的时间才没有收到 INVALID_REQUEST。
    • @Danny,我注意到同样的事情:第一个响应“INVALID_REQUEST”之后的每个请求也都给出了“INVALID_REQUEST”。在花了大约 6 个小时试图弄清楚之后,我为 API 的每个请求附加了一个新参数“request_count”,以避免任何类型的缓存响应。有效。我想这与缓存响应有关,但我不是 100% 确定。
    • @dmmd 好点子,这很可能是一个缓存问题——我假设您在任何文档中都没有找到“request_count”,它的唯一用途是缓存破坏器?
    • @Danny 完全正确。我在回答here(在这个问题内回答)详细说明了我的方法。
    • 这可能是为了防止滥用行为:issuetracker.google.com/issues/35826470
    【解决方案2】:

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

    正如 geocodezip 在他的回答中指出的那样,在返回下一页令牌与该页面实际可用之间存在轻微延迟。所以除了使用某种sleep之外,我没有找到任何其他方法来解决它。

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

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

    我使用的参数是request_count,对于每个发出的请求,我都将它加1。

    为了便于说明,这是我使用的 Python POC 代码(请勿复制粘贴,因为这只是 POC sn-p 并且不起作用):

    # 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's 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-05
      • 1970-01-01
      • 1970-01-01
      • 2013-05-25
      • 2013-07-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多