15

所以我有一个带有集合的 RESTful API people,可以这样调用:

http://example.com/people?lastname=smith

它返回如下 JSON 响应:

    {
      "page": 0,
      "next": 1,
      "total": 5000000,
      "people": [
        { 
          "firstname": "John",
          "lastname": "Smith",
          "age": 32
        },
        { 
          "firstname": "Adam",
          "lastname": "Smith",
          "age": 84
        },
        ...
    }

我想编写一个 Python 生成器,它将从响应中产生每个人,当它到达最后一个人时,如果有下一页,它将发出对下一页的请求,http://example.com/people?lastname=smith&page=1并继续无缝地迭代结果。生成的类调用将非常简单:

    client = PeopleClient("http://example.com/people")
    smiths = client.get_people_by_last_name("smith")

然后我可以在其中迭代每个“Smith” smiths;如有必要,通过全部 500 万。

关于如何实现这一点或是否有可能的任何想法?

更新

使用@ali-afshar 的答案作为指导,这个实现应该适用于假设的 REST API:

    import requests

    class PeopleClient:
        def __init__(self, url):
            self._url = url

        def _get_people(self, **kwargs):
            return requests.get(self._url, params=kwargs)

        def get_people_by_last_name(self, lastname):
            current_page = 0
            while current_page >= 0:
                result = self._get_people(lastname=lastname, page=current_page)
                for person in result.get("people", []):
                    yield person

                current_page = result.get("next", -1)
4

2 回答 2

11

除了为您编写代码之外,您还想利用 Python 的生成器,而不是将整个集合实现为列表。这样,您可以立即开始使用结果,并且仅在到达页面末尾时才执行分页请求。

for person in PeopleClient("http://ex..").get_people_by_last_name("smith"):
    # Do something with the person

其次,您对实际请求的实现应该采用一个可以递增的页面参数,并且可以由包装器生成器调用。

def get_people_page(name, page):
    # Perform the HTTP request, using page=page

生成器本身将类似于:

def get_all_people(name):
    page = 0
    has_more = 1
    while has_more:
        for person in get_people_page(name, page):
            yield person
        page += 1
        has_more = # calculate has more by whether you have a next link
                   # or whether the results set is empty
                   # or whether you get an error
于 2013-07-17T14:47:18.897 回答
1

这是我的生成器解决方案,我认为它是一种触摸清洁器,当使用指定的时,per_page它可以为您节省额外的不必要的请求。

def get_all(per_page=100):
    page = 0
    while True:
        items = self.api.get(per_page=per_page, page=page)

        for item in items:
            yield item

        if len(items) < per_page:
            break

        page += 1

all_items = list(get_all())

self.api.get()必须接受 apageper_page参数。

于 2017-11-27T15:33:03.437 回答