---
metadata:
  - name: generator
    content: Diplodoc Platform v5.50.2
alternate:
  - https://yandex.ru/dev/direct/doc/dg-v4/en/examples/python-suds.md
  - https://yandex.ru/dev/direct/doc/dg-v4/ru/examples/python-suds.md
---
> **Documentation Index:** Fetch the complete configuration index at https://yandex.ru/dev/direct/doc/dg-v4/ru/llms.txt

# SOAP (библиотека Suds)

Создание и редактирование кампаний, объявлений и фраз на Python 2.7 с применением библиотеки Suds 0.4 GA.

Библиотека [Suds](https://pypi.python.org/pypi/suds) анализирует WSDL-описание и обеспечивает взаимодействие по протоколу SOAP. Библиотека создает входные структуры данных, которые приложению остается заполнить и передать на сервер.

{% note warning %}

Подсистема сокетов Python должна поддерживать SSL (дистрибутив Python содержит модуль `ssl.py`).

{% endnote %}

Пример приложения: [app-python-oauth.py](http://download.cdn.yandex.net/tech/ru/direct/doc/download/app-python-oauth.py)[app-python-oauth.py](http://download.cdn.yandex.net/tech/com/direct/doc/download/app-python-oauth.py). Ниже приведено пошаговое описание приложения.

## 1. Подключение Suds и настройка логирования {#import}

```python
# -&ast;- coding: utf_8 -&ast;-
from suds.client import Client
from suds.cache import DocumentCache
from suds.sax.element import Element
from suds import WebFault
```

Класс `Client` (модуль `suds.client`) анализирует WSDL-описание, создает структуры данных, отправляет SOAP-пакеты и разбирает ответы. Это основной класс для взаимодействия с API. Класс `DocumentCache` (модуль `suds.cache`) обеспечивает кеширование WSDL-описания.

Класс `Element` (модуль `suds.sax.element`) служит для добавления произвольных заголовков в SOAP-пакеты. С помощью SOAP-заголовков на сервер передаются метаданные и данные для OAuth-авторизации.

Класс `WebFault` является исключением (exception), генерируемым при ошибках на стороне сервера API. Перехват этого исключения позволяет отличать ошибки на сервере от ошибок на стороне приложения.

Ниже показаны параметры логирования.

```python
import logging
logging.basicConfig(level=logging.INFO)
if __debug__:
    logging.getLogger('suds.client').setLevel(logging.DEBUG)
else:
    logging.getLogger('suds.client').setLevel(logging.CRITICAL)
```

При запуске интерпретатора Python с ключом -O (оптимизированный режим) в выходной поток помещаются сообщения только о критических ошибках. При запуске без этого ключа выводятся отладочные сообщения об отправке и получении SOAP-пакетов, включая текст пакетов.

## 2. Плагин для коррекции ответов {#ns-correction}

Показанная ниже техника исправляет известную ошибку API, в следствие которой типы данных в ответе принадлежат пространству имен `http://namespaces.soaplite.com/perl` вместо пространства имен `API`. Из-за этого Suds создает некорректные структуры результирующих данных, которые не могут повторно использоваться в запросах к API. В следующей версии API ошибка будет исправлена и потребность в данной технике исчезнет.

```python
from suds.plugin import &ast;
class NamespaceCorrectionPlugin(MessagePlugin):
    def received(self, context):
        context.reply = context.reply.replace('"http://namespaces.soaplite.com/perl"','"API"')
```

Пользовательская функция `received` корректирует пространство имен в ответах API, прежде чем Suds проанализирует ответы. Ответ передается в параметре `context` как строка unicode. Ответ представляет собой SOAP-сообщение в том виде, как оно пришло по HTTP.

## 3. Экземпляр класса suds.Client {#create-client}

Экземпляр класса `suds.Client` служит для взаимодействия с API. При его создании указывают URL, по которому находится WSDL-описание.

```python
api = Client('https://api.direct.yandex.ru/v4/wsdl/', plugins = [NamespaceCorrectionPlugin()])
api.set_options(cache=DocumentCache())
```

Показанный код также подключает плагин, созданный на предыдущем шаге, а последней строкой включает кеширование WSDL-описания.

## 4. Метаданные в заголовках SOAP-пакетов {#meta}

SOAP-пакеты содержат элемент `<Header>`, в котором на сервер передаются метаданные. Метаданные относятся к запросу в целом, а не к вызываемому методу. Ниже показано, как передать параметр `locale`, указывающий язык ответных сообщений.

```python
locale = Element('locale').setText('en')
api.set_options(soapheaders=(locale))
```

SOAP-заголовки автоматически включаются во все SOAP-пакеты.

## 5. OAuth-авторизация {#oauth}

Для OAuth-авторизации необходимо знать авторизационный токен (`token`). Его указывают в заголовках SOAP-пакетов как метаданные.

```python
token = Element('token').setText('e4d3b3d2a74e4fa387a18dda5cd1c8d9')
locale = Element('locale').setText('en')
api.set_options(soapheaders=(token, locale))
```

## 6. Функция для вызова методов API {#call-method}

Следующая функция вызывает указанный метод API с входными параметрами.

```python
def directRequest(methodName, params):
    '''
    Вызов метода API Яндекс Директа:
       api - экземпляр класса suds.Client
       methodName - имя метода
       params - входные параметры
    В случае ошибки программа завершается,
    иначе возвращается результат вызова метода
    '''
    try:
        result = api.service['APIPort'][methodName](params)
        return result
    except WebFault, err:
        print unicode(err)
    except:
        err = sys.exc_info()[1]
        print 'Other error: ' + str(err)
    exit(-1)
```

При успешном выполнении метода, функция возвращает полученные с сервера данные. Они имеют вид иерархии объектов, которую создал Suds на основе анализа SOAP-ответа. Далее будут рассмотрены техники работы с результирующими данными.

В случае ошибки функция анализирует объект-исключение. Если он принадлежит классу `WebFault`, выводится сообщение об ошибке, полученное от сервера. В других случаях ошибка произошла на стороне приложения, и сообщение об ошибке выводится с префиксом “Other error:” При любой ошибке приложение завершается с кодом возврата -1.

## 7. Создать кампанию {#create-camp}

Следующий код демонстрирует использование словарей для формирования входных параметров. Словарь `params` содержит минимально необходимые параметры для создания кампании.

```python
params = {
   'CampaignID': 0,
   'Login': 'agrom',
   'Name': u'Кампания создана через API',
   'FIO': 'Alex Gromov',
   'Strategy':{
      'StrategyName': 'WeeklyBudget',
      'WeeklySumLimit': 400,
      'MaxPrice': 8,
   },
   'EmailNotification':{
      'MoneyWarningValue':20,
      'SendAccNews':'Yes',
      'WarnPlaceInterval':60,
      'SendWarn':'Yes',
      'Email':'agrom@yandex.ru'
   },
}
```

Ниже показан вызов метода CreateOrUpdateCampaign с помощью ранее созданной функции `directRequest`. В результирующий параметр `campaignId` помещается идентификатор созданной кампании.

```python
campaignId = directRequest('CreateOrUpdateCampaign', params)
print u'Создана кампания ID=' + str(campaignId)
```

## 8. Редактировать кампанию {#edit-camp}

Для редактирование кампании целесообразно получать ее параметры, вносить необходимые изменения и сохранять параметры в API. Метод [GetCampaignsParams](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/GetCampaignsParams.md) принимает массив идентификаторов кампаний и возвращает их параметры.

```python
params = {'CampaignIDS': [campaignId]}
campaignsParams = directRequest('GetCampaignsParams', params)
```

Ниже показаны способы доступа к результирующим данным.

```python
params = campaignsParams[0]
params['EmailNotification']['Email'] = 'new@email.ru'
params.MinusKeywords = [u'автохолодильник', u'рефрижератор'] 
params.TimeTarget.DaysHours[0].Hours = [6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
```

Массив `campaignsParams` содержит объекты [CampaignInfo](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/GetCampaignsParams.md#CampaignInfo). Поскольку запрошены параметры одной кампании, далее работаем с первым элементом массива, присвоив его переменной `params`.

Вторая строка кода показывает, что доступ к параметрам возможен в нотации словарей. Для этого названия параметров указывают в кавычках внутри квадратных скобок. В третьей и четвертой строке демонстрируется доступ к параметрам в объектной нотации, при которой названия параметров разделены точкой.

После внесения необходимых изменений сохраняем параметры с помощью метода CreateOrUpdateCampaign.

```python
result = directRequest('CreateOrUpdateCampaign', params)
print u'Изменены параметры кампании ID=' + str(result)
```

## 9. Создать объявление и фразу {#create-banner}

Для создания объявления и фразы воспользуемся классом `Factory` модуля `suds`. С его помощью создадим структуру входных данных, а затем заполним ее значениями. Этот способ является альтернативой созданию словарей.

```python
banner = api.factory.create('BannerInfo')
banner.CampaignID = campaignId
banner.BannerID = 0
banner.Title = u'Холодильники'
banner.Text = u'Продажа и ремонт бытовых холодильников'
banner.Href = 'http://www.api.ru/banner{param1}?page={param2}'
banner.Geo = '1,10174'
```

В первой строке создается структура входных данных [BannerInfo](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateOrUpdateBanners.md#BannerInfo), которая содержит все предусмотренные параметры. В следующих строках задаются обязательные параметры, без которых фраза не будет создана.

Входная структура, созданная с помощью `Factory`, не должна содержать неинициализированные массивы. Поэтому параметры, содержащие массивы, нужно либо явно инициализировать, например присвоить пустой массив, либо удалить, как показано ниже.

```python
banner.Sitelinks = []
del(banner.ContactInfo) 
del(banner.MinusKeywords)
```

Для структуры [BannerInfo](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateOrUpdateBanners.md#BannerInfo) предусмотрен обязательный массив `Phrases`, содержащий по меньшей мере одну фразу. Создадим ее и поместим в массив `Phrases`.

```python
phrase = api.factory.create('BannerPhraseInfo')
phrase.PhraseID = 0
phrase.Phrase = u'холодильник'
phrase.Price = 1.99
banner.Phrases = [phrase]
```

В первой строке создается структура [BannerPhraseInfo](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateOrUpdateBanners.md#BannerPhraseInfo). В следующих строках задаются обязательные параметры фразы.

API предоставляет возможность задать для фразы переменные, которые автоматически подставляются в ссылку на сайт, если объявление найдено по данной фразе. Ниже показано как задать такие переменные.

```python
userParams = api.factory.create('PhraseUserParams')
userParams.Param1 = ''
userParams.Param2 = 'freezer'
banner.Phrases[0].UserParams = userParams
```

Теперь, если объявление найдено по фразе [холодильник], ссылка на сайт `http://www.api.ru/banner{param1}?page={param2}` примет вид `http://www.api.ru/banner?page=freezer`

Сохраняем параметры объявления с помощью метода [CreateOrUpdateBanners](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateOrUpdateBanners.md).

```python
params = [banner]
bannerID = directRequest('CreateOrUpdateBanners', params)[0]
print u'Создано объявление ID=' + str(bannerID)
```

В случае успеха результирующий параметр `bannerId` содержит идентификатор созданного объявления.

## 10. Добавить фразу к объявлению {#edit-banner}

Для добавления фраз целесообразно получать текущие параметры объявлений, включая параметры фраз, вносить необходимые изменения и сохранять параметры в API.

Ниже показано, как получить параметры объявления.

```python
params = {'BannerIDS': [bannerID]}
bannerParams = directRequest('GetBanners', params)[0]
```

Метод [GetBanners](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/GetBanners.md) возвращает массив объектов [BannerInfo](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateOrUpdateBanners.md#BannerInfo), каждый из которых содержит параметры объявления. В нашем примере запрошены данные одного объявления, которые помещены в переменную `bannerParams`.

Создаем объект BannerPhraseInfo и задаем параметры фразы.

```python
phrase = api.factory.create('BannerPhraseInfo')
phrase.PhraseID = 0
phrase.Phrase = u'морозильник'
phrase.Price = 1.44
```

Создаем пользовательские переменные для подстановки в ссылку на сайт.

```python
phrase.UserParams = api.factory.create('PhraseUserParams')
phrase.UserParams.Param1 = '_2'
phrase.UserParams.Param2 = 'deepfreeze'
```

Теперь, если объявление найдено по фразе [морозильник], ссылка на сайт `http://www.api.ru/banner{param1}?page={param2}` примет вид `http://www.api.ru/banner_2?page=deepfreeze`

Добавляем фразу в массив `Phrases` и сохраняем параметры объявления с помощью метода [CreateOrUpdateBanners](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateOrUpdateBanners.md).

```python
bannerParams.Phrases.append(phrase)
params = [bannerParams]
bannerIDS = directRequest('CreateOrUpdateBanners', params)
print u'Изменено объявление ID=' + str(bannerIDS[0])
```

## 11. Вызов финансовых методов {#create-invoice}

При вызове финансовых методов необходимо дополнительно указывать финансовый токен и номер финансовой операции.

Для примера вызовем метод [CreateInvoice](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateInvoice.md), который создает счет на оплату. Ниже показано формирование финансового токена. Исходными данными являются: мастер-токен (получают в интерфейсе Яндекс Директа), номер финансовой операции, название метода, логин пользователя.

```python
import hashlib

masterToken  = 'AEgchkX2M3FBL8lU'
operationNum = 119
usedMethod   = 'CreateInvoice'
login        = 'agrom'

financeToken = hashlib.sha256(masterToken + str(operationNum) + usedMethod + login).hexdigest()
```

Создается финансовый токен для однократного вызова метода `CreateInvoice` от имени пользователя `agrom`. Пример токена показан ниже.

```
7215f95e84a766971d8ec4eb5a39ae96505b3a5529a91e5a03e5943565a6e6c7
```

Финансовый токен и номер финансовой операции передают в заголовке SOAP-пакета. Ниже показана установка параметров в заголовке.

```python
finance_token = Element('finance_token').setText(financeToken)
operation_num = Element('operation_num').setText(operationNum)
api.set_options(soapheaders=(finance_token, operation_num))
```

{% note alert %}

При использовании OAuth-авторизации также требуется указать авторизационный токен (`token`) в заголовке SOAP-пакета.

{% endnote %}

Создаем структуру входных данных и вызываем метод [CreateInvoice](https://yandex.ru/dev/direct/doc/dg-v4/ru/reference/CreateInvoice.md).

```python
invoice = api.factory.create('PayCampElement')
invoice.CampaignID = campaignId
invoice.Sum = 150
params = api.factory.create('CreateInvoiceInfo')
params.Payments = []
params.Payments.append(invoice)
url = directRequest('CreateInvoice', params)
```

В случае успеха метод возвращает URL, по которому можно получить печатную форму счета на оплату. Сделать это может только авторизованный пользователь Яндекса, от лица которого выполнен запрос (в примере — пользователь agrom).

