Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

В прошлой статье мы рассказали, как с помощью Excel создали уникальные быстрые ссылки для 7000 объектов национального туроператора «Алеан», с которым Adgasm.io сотрудничает уже более 4 лет. Наше решение повысило CTR объявлений на 30%, а выручка генераторных кампаний, где мы, в том числе, использовали уникальные быстрые ссылки, выросла в несколько раз.

Объявление, которое получается в результате внедрения уникальных быстрых ссылок
Объявление, которое получается в результате внедрения уникальных быстрых ссылок

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

Я не эксперт в Python, и, как любой новичок, сначала формулирую задачу, а затем ищу способы реализации. Поэтому готов к критике и буду рад обсудить в комментариях оптимальные решения.

Шаг 1. Сначала нужно установить среду разработки Jupyter Notebook, где мы будем писать код. Процесс установки для популярных операционных систем легко найти в сети.

Открываем файл Python 3.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Импортируем библиотеки — файлы с шаблонами кода, которые упрощают работу, а также импортируем данные из фида в датафрейм pandas (проще сказать — в таблицу). Если говорить условно, строчки в фиде мы записываем в ячейки таблицы. Этот процесс подробно описан на канале «Товарищ Excel — Power Query, Power Pivot, Python».

Шаг 2. В отличие от гайда «Товарищ Excel» наш фид начинается с url, который относится не к объекту (отелю), а к url сайта, к которому относится фид. Это нужно исправить.

Считываем данные из фида и записываем их в переменные (после знака «#» идут комментарии, которые не считаются частью кода).

# считываем данные из фида по атрибутам и записываем в переменную url_object = feed.find_all('url') id_object = feed.find_all('id') region_1 = feed.find_all('region_1') region_2 = feed.find_all('region_2') distance_to_sea = feed.find_all('distance_to_sea')

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

# Удаляем первый url из шапки фида, который относится к сайту, но не к объекту url_object.pop(0)

Шаг 3. Остальные шаги получились идентичными гайду от «Товарищ Excel». Датафрейм при парсинге фида получается точь-в-точь таким же, как мы импортировали при помощи Power Query в предыдущей в первой части статьи.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Далее нам нужно повторить шаги из первой части статьи, только при помощи Python. Преобразуем значения из фида в тексты, которые мы будем использовать в расширениях.

Шаг 4. При помощи метода replace мы заменяем значения true на тот текст, который хотим видеть в расширениях. Но в нашем случае есть исключения:

— рейтинг, где мы конкатенируем (объединяем) текст «Рейтинг» и числовое значение рейтинга, а также заменяем значения без рейтинга на false (аналогично и для расстояния до моря, например);

— бассейны (крытый/открытый), которые мы будем дополнительно преобразовывать.

# заменяем значения true на тот текст, который хотим отображать в заголовке быстрой ссылки в дальнейшем bs.services_open_pool = bs.services_open_pool.replace("true", "Открытый") bs.services_closed_pool = bs.services_closed_pool.replace("true", "крытый") bs.for_kids = bs.for_kids.replace("true", "Отдых с детьми") bs.services_spa = bs.services_spa.replace("true", "SPA") bs.services_treatment = bs.services_treatment.replace("true", "Лечение") bs.services_beach = bs.services_beach.replace("true", "Пляж рядом") bs.hotel_rating = "Рейтинг: " + bs.hotel_rating bs.hotel_rating = bs.hotel_rating.replace("Рейтинг: null", "false") bs.services_wifi = bs.services_wifi.replace("true", "Wi-Fi")

Также можно совместить несколько сходных преимуществ: например, в рамках термального комплекса.

# Соединяем некоторые схожие преимущества, например, наличие Сауны, Джакузи, Бани, Хаммама в одну колонку с разделителем "/" bs["thermal"] = bs.sauna + "/" + bs.jacuzzi + "/" + bs.bath + "/" + bs.hammam # На выходе получится Сауна/Джакузи/Баня/Хаммам или Сауна/Джакузи/false/Хаммам - если Бани нет. # Поэтому следующей строкой заменяем значения "false/" или ""/false" на пустое место bs.thermal = bs.thermal.replace(["false/", "/false"], ["", ""], regex=True)

Чтобы проверить, какие уникальные значения получились в результате обработки, применяем к столбцу метод «unique()»

Получаем массив из уникальных значений
Получаем массив из уникальных значений

Далее преобразовываем колонки, связанные с расстоянием: до моря, до подъёмника. Например, форматируем километры в метры, удаляем слишком отдаленные значения по типу «до моря: 1 000 000 км». Не будем углубляться в подробности, потому что это специфика объявлений «Алеан» — чтобы раскрыть все нюансы, пришлось бы написать еще одну часть статьи.

Шаг 5. Создаем в датафрейме колонки ОБС (описания быстрых ссылок) для соответствующих ЗБС (заголовки быстрых ссылок)

# Создаём колонки под ОБС и помещаем в них любое значения (в моём случае "1") для каждой из ячеек bs["for_kids_obs"] = 1 bs["services_spa_obs"] = 1 bs["services_treatment_obs"] = 1 bs["services_beach_obs"] = 1 bs["hotel_rating_obs"] = 1 bs["bar_restaurant_obs"] = 1 bs["services_wifi_obs"] = 1 bs["animal_friendly_obs"] = 1 bs["services_parking_obs"] = 1

Заполняем актуальными значениями колонки ОБС, созданные на предыдущем шаге.

# генерируем описания быстрых ссылок для каждого заголовка быстрой ссылки при помощи функции obs_generic(bs.for_kids, bs.for_kids_obs, 'Подходит для отдыха с детьми. Подробности на сайте') obs_generic(bs.services_spa, bs.services_spa_obs, 'Посетите SPA-комплекс') obs_generic(bs.services_treatment, bs.services_treatment_obs, 'Есть лечебная программа. Выбирайте процедуру') obs_generic(bs.services_beach, bs.services_beach_obs, 'Оборудованный пляж для комфортного отдыха')

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

# Генерирует описания быстрых ссылок для заголовков. Если заголовок равен null или false, то описание будет false def obs_generic(sbs, obs, text): for i in range(0, len(sbs)): if "false" in sbs[i] or "null" in sbs[i]: obs[i] = "false" else: obs[i] = text

Наша функция принимает данные о колонке ЗБС (sbs), колонке ОБС (obs) и тексте (text, для каждого ОБС — свой), который мы хотим поместить в колонку ОБС. Функция преобразовывает колонки ОБС, принимая соответствующие значения.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Работа с функциями выглядит сложной, если вы только начинаете работу с Python. Мы описали процесс упрощенно, но если вы хотите разобраться глубже, рекомендуем поискать статьи или видео на темы «Как работать с функциями Python», «Как работать с циклом for в Python».

В нашем случае функция сработала корректно. Чтобы в этом убедиться, мы проверили колонки с ОБС на наличие уникальных значений. Получили уникальные значения false там, где ЗБС также имеет значение false, или текст быстрой ссылки, если ячейка ЗБС не содержит false.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Шаг 6. Далее нам нужно каждую колонку ЗБС добавить в общую колонку со всеми ЗБС. Этот шаг аналогичен шагу 4 в разделе «Обрабатываем значения фида для формата быстрых ссылок» первой части статьи.

Результат конкатенации для объекта под индексом 464 получился таким.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Конкатенируем адреса быстрых ссылок уже с другой, более простой механикой, чем в случае с Excel. Нам достаточно добавить 8 url объекта через разделитель ||

# Формируем общую колонку со всеми (8) адресами быстрых ссылок для каждого из объектов # При этом разделителей должно быть только 7, поэтому конструкцию "bs.url_object + """ домножаем на 7, а не 8. # И следом добавляем еще один адрес bs["url_bs_all"] = (bs.url_object + "") * 7 + bs.url_object

Шаг 7. Чтобы работать с объединенными ЗБС, ОБС и адресами БС, поместим их в новый датафрейм bs_all.

# в новый датафрейм отбираем нужные нам значениям bs_all = bs[["id_object", "url_object", "url_bs_all", "obs_all", "sbs_all"]]

В новом датафрейме получаем только необходимые для дальнейшей работы поля.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Удаляем все значения false и null.

# удаляем false и null в датафрейме bs_all = bs_all.replace("false", "", regex = True).replace("null", "", regex = True)

Получаем такие значения.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Шаг 8. Удаляем лишние разделители || из ЗБС и ОБС. Этот шаг аналогичен шагу 5 в разделе «Обрабатываем значения фида для формата быстрых ссылок» первой части статьи.

# удаляем повторения \\\\ при помощи replace и regex for i in range(0, 20): bs_all["obs_all"].replace("\|\|\|\|", "", regex=True, inplace=True) bs_all["sbs_all"].replace("\|\|\|\|", "", regex=True, inplace=True)
# удаляем символы || слева и справа при помощи lstrip и rstrip for i in range(0, len(bs_all.obs_all)): a = "|" bs_all.obs_all[i] = bs_all.obs_all[i].lstrip(a).rstrip(a) bs_all.sbs_all[i] = bs_all.sbs_all[i].lstrip(a).rstrip(a)

Получаем более чистые и читаемые значения.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Шаг 9. Считаем количество ЗБС. Этот шаг аналогичен шагу 1 в разделе «Готовим быстрые ссылки для выгрузки в Директ Коммандер» первой части статьи.

Повторяем то же самое для ОБС.

# то же самое для описаний быстрых ссылок bs_all['count_obs_all'] = 0 bs_all['count_obs_all_2'] = bs_all.obs_all.str.count('\|') for i in range(0, len(bs_all.obs_all)): a = 0 if bs_all.count_obs_all_2[i] > 0 or len(bs_all.obs_all[i]) > 0: a = bs_all.count_obs_all_2[i] // 2 + 1 bs_all.count_obs_all[i] = a

К числовой колонке мы можем применить метод describe, который покажет сводку по основным показателям. Если применим describe к колонке с количеством ЗБС, получим следующие данные:

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Где,

  • count — количество объектов в фиде
  • mean — среднее число ЗБС для одного объекта
  • min/max — минимальное/максимальное число ЗБС для одного объекта
  • 25, 50, 75% — значения по процентилям. 50% — это медиана, то есть для половины объектов количество ЗБС больше 4, для половины — меньше.

Если мы все сделали правильно, то при применении describe к колонке с количеством ОБС получим идентичные значения. Так и получилось.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Шаг 10. Удаляем некоторое количество ЗБС и ОБС, если их больше 8. И наоборот, добавляем, если их меньше 8. Этот шаг аналогичен шагу 2 в разделе «Готовим быстрые ссылки для выгрузки в Директ Коммандер» первой части статьи.

Сформируем списки из значений с разделителем || и значением ЗБС или ОБС. Чем ближе ЗБС или ОБС к началу списка, тем чаще оно будет использоваться. Важно, чтобы порядок значений в ЗБС соответствовал порядку значений в ОБС.

sbs_app = ["||Рассрочка 0%", "||Отмена брони - 0₽", "||Отзывы", "||Оформление за 3 мин", "||Актуальные цены", "||Места в наличии", "||Контроль качества", "||Алеан: один из лидеров туризма"] obs_app = ["||При бронировании предоставляем беспроцентную рассрочку", "||Об условиях читайте на сайте", "||Честные отзывы от клиентов Алеан", "||Удобная навигация и формы на сайте", "||Всегда поддерживаем актуальность", "||Бронируйте на удобную дату", "||1 место по результатам мониторинга лояльности туроператоров", "||1 место по итогам голосования турагентов (лето 2022)"]

Далее напишем код для добавления необходимого количества ЗБС и ОБС.

# В переменную x для каждого объекта мы помещаем следюущий расчёт: из 8 вычитаем количество ЗБС. # или равно Если результат получится со знаком меньше 0 или равно 0 - это будет значить, # что количество ЗБС для этого объекта больше 8. И дополнительные ЗБС добавлять не надо. # Если же получится больше 0, значит, количество ЗБС меньше 8 и дополнительные ЗБС нужно добавить. for i in range(0, len(bs_all.sbs_all)): x = 8 - bs_all.count_sbs_all[i] c = 0 # Для каждой ячейки в ЗБС и ОБС мы делаем проверку: если x > 0 , то мы добавим дополнительные ЗБС. # При этом количество ЗБС, которые нужно добавить, будет определяться как раз переменной "x" if x > 0: bs_all.sbs_all[i] = bs_all.sbs_all[i] + "".join(sbs_app[c:x]) bs_all.obs_all[i] = bs_all.obs_all[i] + "".join(obs_app[c:x])

Из-за того, что мы присоединяли первые значения в ЗБС и ОБС с разделителем || в начале, могли образоваться лишние разделители слева. Это происходит в случае, если количество уникальных ЗБС или ОБС равно 0. Поэтому нужно удалить первые значения || слева при помощи str.lstrip()

# Удаляем лишние разделители "|" слева a = "|" bs_all["sbs_all"] = bs_all["sbs_all"].str.lstrip(a) bs_all["obs_all"] = bs_all["obs_all"].str.lstrip(a)

Недостающие ЗБС и ОБС мы добавили. Осталось удалить те, где ЗБС и ОБС больше 8.

Этот процесс можно описать в такой последовательности.

1) Создаём новый датафрейм. ОБС разбиваем по разделителю ||. Задаем название каждой из полученных колонок.

Новый датафрейм будет выглядеть так.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

2) Конкатенируем колонки с ОБС из нового датафрейма с разделителем || в одну колонку в старом датафрейме obs all при помощи методов .agg и .join

# Соединяем 8 колонок, а не 9, т.к. максимальное количество быстрых ссылок - 8. Разделитель - "\\" bs_all["obs_all"] = new_df_obs[['obs1','obs2','obs3','obs4','obs5','obs6','obs7','obs8']].agg(''.join , axis=1)

Ячейки колонки для каждого из объектов сформированы правильно — проверили на конкректном объекте под индексом 464.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Тот же порядок действий повторяем для ЗБС.

# то же самое для ЗБС new_df_sbs = bs_all["sbs_all"].astype(str).str.split('\|\|', n=8, expand=True) new_df_sbs.columns=['sbs1','sbs2','sbs3','sbs4','sbs5','sbs6','sbs7','sbs8','sbs9'] bs_all["sbs_all"] = new_df_sbs[['sbs1','sbs2','sbs3','sbs4','sbs5','sbs6','sbs7','sbs8']].agg('||'.join , axis=1)

На этом преобразования закончены. Осталось убедиться, что количество ЗБС и ОБС везде равняется 8. Эту работу для ЗБС мы уже проделывали, поэтому повторяем ее для новых колонок.

bs_all['count_sbs_all'] = 0 bs_all['count_sbs_all_2'] = bs_all.sbs_all.str.count('\|') for i in range(0, len(bs_all.sbs_all)): a = 0 if bs_all.count_sbs_all_2[i] > 0 or len(bs_all.sbs_all[i]) > 0: a = bs_all.count_sbs_all_2[i] // 2 + 1 bs_all.count_sbs_all[i] = a # то же самое для описаний быстрых ссылок bs_all['count_obs_all'] = 0 bs_all['count_obs_all_2'] = bs_all.obs_all.str.count('\|') for i in range(0, len(bs_all.obs_all)): a = 0 if bs_all.count_obs_all_2[i] > 0 or len(bs_all.obs_all[i]) > 0: a = bs_all.count_obs_all_2[i] // 2 + 1 bs_all.count_obs_all[i] = a

С помощью колонок count_obs_all и count_sbs_all проверяем, что количество ОБС у всех объектов равно 8, применив метод describe:

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Шаг 11. Подготавливаем данные и выгружаем датафрейм в Excel.

# создаём финальный датафрейм - в него записываем все колонки для выгрузки bs_final = bs_all[["id_object", "url_object", "sbs_all", "url_bs_all", "obs_all"]] #переименовываем колонки в формат, читаемый Direct Commander bs_final.rename(columns={"url_object" : "Ссылка", "sbs_all" : "Заголовки быстрых ссылок", "obs_all" : "Описания быстрых ссылок", "url_bs_all" : "Адреса быстрых ссылок"}, inplace=True) # выгружаем датафрейм в xlsx файл "bs_final" bs_final.to_excel("bs_final.xlsx", index=False)

Элементы быстрых ссылок уже готовы для выгрузки.

Фид + Python = идеальные быстрые ссылки в Яндекс Директ и рост CTR на 30%

Остается только применить функцию ВПР. Помните, что в результате могут получиться значения « /Д», которые нельзя загружать в Директ Коммандер. Чтобы их убрать, воспользуйтесь способом, описанным в последнем разделе первой части статьи.

Больше полезной информации в нашем телеграм-канале:

Автор: Иван Кикоть, Senior performance manager

3636
10 комментариев

Выглядит сложно, но интересно :)

4
Ответить

Даже котэ в шокеде

3
Ответить

Первый раз слышу слово шокед)

Ответить

ЗБС конечно)

3
Ответить

Да уж, попроще нет вариков в 2 клика?))

1
Ответить

За 2 клика в Яндекс Директ можно только слить бюджет зачастую :)

Способ непростой, соглашусь.
Надеюсь, что это поможет новичкам в python (таким как я) сориентироваться и замотивирует изучать язык для решения задач в контекстной рекламе.

1
Ответить

Слив бюджета - больная тема. Верно :D

Ответить