Как сохранить куки для xmlrpc.client в Python 3?

Python по умолчанию xmlrpc.client.Transport (может использоваться с xmlrpc.client.ServerProxy) не сохраняет файлы cookie, которые иногда необходимы для входа на основе файлов cookie.

Например, следующий прокси-сервер при использовании с API-интерфейсом TapaTalk (для которого login метод использует куки для аутентификации), выдаст ошибку разрешения при попытке изменить посты.

proxy = xmlrpc.client.ServerProxy(URL, xmlrpc.client.Transport())

В сети есть несколько решений для Python 2, но они не совместимы с Python 3.

Как я могу использовать Transport что сохраняет куки?

2 ответа

Решение

Это простой Transport подкласс, который сохранит все куки:

class CookiesTransport(xmlrpc.client.Transport):
"""A Transport subclass that retains cookies over its lifetime."""

    def __init__(self):
        super().__init__()
        self._cookies = []

    def send_headers(self, connection, headers):
        if self._cookies:
            connection.putheader("Cookie", "; ".join(self._cookies))
        super().send_headers(connection, headers)

    def parse_response(self, response):
        for header in response.msg.get_all("Set-Cookie"):
            cookie = header.split(";", 1)[0]
            self._cookies.append(cookie)
        return super().parse_response(response)

Использование:

proxy = xmlrpc.client.ServerProxy(URL, CookiesTransport())

поскольку xmlrpc.client в Python 3 для этого лучше подходят ловушки, это намного проще, чем в эквивалентной версии Python 2.

Существующий ответ от GermainZ работает только для HTTP. После большого количества времени, борющегося с этим, есть адаптация HTTPS. Обратите внимание context вариант, который имеет решающее значение.

class CookiesTransport(xmlrpc.client.SafeTransport):
    """A SafeTransport (HTTPS) subclass that retains cookies over its lifetime."""

    # Note context option - it's required for success
    def __init__(self, context=None):
        super().__init__(context=context)
        self._cookies = []

    def send_headers(self, connection, headers):
        if self._cookies:
            connection.putheader("Cookie", "; ".join(self._cookies))
        super().send_headers(connection, headers)

    def parse_response(self, response):
        # This check is required if in some responses we receive no cookies at all
        if response.msg.get_all("Set-Cookie"):
            for header in response.msg.get_all("Set-Cookie"):
                cookie = header.split(";", 1)[0]
                self._cookies.append(cookie)
        return super().parse_response(response)

Причина в том, что ServerProxy не уважает context Опция относится к транспорту, если указан транспорт, поэтому нам нужно использовать его непосредственно в конструкторе транспорта.

Использование:

import xmlrpc.client
import ssl


transport = CookiesTransport(context=ssl._create_unverified_context())
# Note the closing slash in address as well, very important
server = xmlrpc.client.ServerProxy("https://<api_link>/", transport=transport)

# do stuff with server
server.myApiFunc({'param1': 'x', 'param2': 'y'})
Другие вопросы по тегам