Руководство о том, как анализировать целевые страницы, сканировать лексические элементы и внедрять их в свои инициативы в области маркетинга и электронной коммерции.
Предисловие
В своей предыдущей статье я рассказал о возможностях автоматизации копирования объявлений Google Ads Responsive Search Ads с использованием Python 3 и Pandas. На этот раз я представлю руководство о том, как извлечь специфическую для веб-страницы лексическую информацию с помощью обработки естественного языка (NLP) для дополнительных соображений по копированию. Программа НЛП, представленная в этой статье, позволит вам:
- Разобрать веб-страницу на наличие текста и лексических элементов
- Определить тематику веб-страницы по ее лексическим элементам
- Представьте наиболее подходящие лексические элементы для создания копий
Мы завершим наши уроки, интегрировав извлеченные лексические элементы в наш ранее созданный генератор адаптивных поисковых объявлений.
Примечание: лексическая информация, которую мы будем извлекать, также может быть использована для A/B-тестирования в среде электронной коммерции — процесс, лежащий в основе этого, будет описан в следующей публикации. А пока добавьте меня в LinkedIn, если хотите следить за всеми моими новыми проектами.
Зависимости проекта
Для эффективной работы этой программе требуется несколько библиотек. Мы начнем с краткого описания каждой библиотеки вместе с соответствующими инструкциями по установке.
$ pip install requests import requests
Модуль requests
позволяет отправлять HTTP-запросы с помощью Python. HTTP-запрос возвращает Объект ответа со всеми данными ответа (содержимое, кодировка, статус и т. д.).
$ pip install justext import justext
jusText — это инструмент для удаления стандартного содержимого, такого как навигационные ссылки, верхние и нижние колонтитулы, со страниц HTML. Он предназначен для сохранения в основном текста, содержащего полные предложения, и поэтому хорошо подходит для создания лингвистических ресурсов, таких как веб-корпусы.
pip install -U pip setuptools wheel pip install -U spacy python -m spacy download en_core_web_trf import spacy from spacy.matcher import Matcher
spaCy — это бесплатная библиотека с открытым исходным кодом для расширенной обработки естественного языка (NLP) в Python. spaCy разработан специально для производственного использования и помогает создавать приложения, которые обрабатывают и понимают большие объемы текста. Его можно использовать для создания систем извлечения информации или понимания естественного языка, а также для предварительной обработки текста для глубокого обучения. Модуль Matcher
позволяет находить слова и фразы, используя правила, описывающие атрибуты их токенов.
Примечание. Мы будем использовать конвейер en_core_web_trf
для получения более точных результатов (в отличие от более распространенного и ориентированного на производительность варианта en_core_web_sm
).
from collections import Counter
Counter
— это подкласс dict
для подсчета хешируемых объектов. Это коллекция, в которой элементы хранятся как ключи словаря, а их счетчики хранятся как значения словаря. Подсчеты могут быть любыми целыми значениями, включая нулевые или отрицательные подсчеты. Класс Counter
подобен сумкам или мультимножествам в других языках.
Извлечение текста
Эта программа предназначена для извлечения лексических элементов из любого заданного URL-адреса, где доступ к запрошенному ресурсу не запрещен. Если вы пытаетесь запросить ресурсы веб-страницы, которые заблокированы в целях безопасности, вы, скорее всего, столкнетесь с ошибкой 403. Если вы хотите развернуть эту программу на уровне предприятия, вам необходимо проконсультироваться со своим ИТ-отделом, чтобы определить, возможно ли исключение безопасности для ваших вызовов API.
В нашем примере мы будем анализировать целевую страницу продукта из канадского ассортимента бытовой техники Whirlpool.
Это довольно низкоуровневая страница в иерархии веб-страниц Whirlpool, так почему мы анализируем именно ее? На практике гораздо сложнее правильно определить тематику низкоуровневого лендинга, чем высокоуровневого — обычно это связано с отсутствием текстового контента на низкоуровневых страницах. Если мы сможем получить точные результаты на детальном уровне, программа легко масштабируется до более общих целевых страниц.
Мы начинаем с отправки HTTP-запроса на нашу целевую целевую страницу с помощью модуля requests
.
#extract text url = "https://www.whirlpool.ca/en_ca/laundry/washers/he-top-load-washer/p.5.5-cu.-ft.-i.e.c.-smart-top-load-washer.wtw6120hw.html?originVariantsOrder=WH,C6" response = requests.get(url)
Библиотека jusText
необходима для извлечения текстовых элементов веб-страницы. jusText
удалит лишний «беспорядок» (шаблоны, шаблоны) вокруг основного текстового содержимого веб-страницы и вернет текстовые элементы.
paragraphs = justext.justext(response.content, justext.get_stoplist("English"))
Каждый абзац, проанализированный jusText, будет добавлен в список — в данном случае это пустой список с именем for_processing
. После этого список объединяется в одну строку для анализа spaCy.
for_processing = [] for paragraph in paragraphs: if not paragraph.is_boilerplate: for_processing.append(paragraph.text) for_processing = ''.join(for_processing)
Наконец, просмотрите извлеченное содержимое в терминале, распечатав список.
#print(for_processing)
Извлечь все теги части речи (POS)
Лексические элементы также могут называться тегами части речи (POS). Чтобы проанализировать извлеченный текст для тегов POS, мы начинаем с загрузки конвейера en_core_web_trf
с помощью spaCy.
nlp = spacy.load('en_core_web_trf')
Мы также инициализируем doc
, контейнер для доступа к лингвистическим аннотациям. В нашем сценарии doc
будет присвоено значение for_processing
.
doc = nlp(for_processing)
Каждый POS в пределах for_processing
необходимо будет разбить на отдельные слова, знаки препинания и т. д., известные как «токены». Этот процесс называется токенизацией, и spaCy выполнит это, применяя правила, специфичные для языка, с которым мы работаем. Мы загрузили конвейер en_core_web_trf
для нашего проекта, что означает, что правила, применимые к токенизации, будут специфичны для английского языка.
Вы можете просмотреть значения каждого POS, распечатав их. На нашей целевой странице слишком много POS-тегов для разумного отображения — я покажу лишь небольшой пример результатов токенизации.
for token in doc: print(token.text, token.pos_) OUTPUT Wash PROPN technology NOUN with ADP Active PROPN Bloom PROPN ™ PUNCT wash NOUN action NOUN senses VERB the DET
Каждому токену присваивается POS-тег в spaCy из следующего списка:
Просмотр всех тегов POS сам по себе не совсем полезен, но мы выполнили первую задачу нашей программы.
- Разобрать веб-страницу на наличие текста и лексических элементов (Готово!)
Давайте посмотрим, как мы можем применить наши POS-теги на практическом примере.
Определите тему и сущность веб-страницы
Если бы нам нужно было создавать маркетинговые материалы, специфичные для веб-страницы, мы должны были бы:
- Иметь возможность анализировать нашу веб-страницу на предмет ее основной темы и сущности.
- Извлеките описательные токены (прилагательные), непосредственно связанные с нашей основной темой.
- Используйте наиболее подходящие прилагательные для использования в наших маркетинговых активациях.
Давайте сначала выясним, о чем наша веб-страница. Мы сделаем это, взглянув на три отдельных показателя частоты — частоту общих токенов, частоту имен существительных и частоту имен собственных.
Мы начинаем с создания списка всех токенизированных элементов нашей веб-страницы, которые можно считать «словами», т.е. токены, не являющиеся стоп-словами или знаками препинания.
nlp = spacy.load("en_core_web_trf") matcher = Matcher(nlp.vocab) doc = nlp(for_processing) # all tokens that aren't stop words or punctuations words = [token.text for token in doc if not token.is_stop and not token.is_punct]
Давайте отобразим наши топ-5 наиболее часто наблюдаемых токенов «слов».
# display the five most common tokens word_freq = Counter(words) common_words = word_freq.most_common(5) print(common_words) OUTPUT [('\n', 43), ('washer', 15), ('5', 12), ('load', 10), ('water', 9)]
Похоже, на нашей странице много новых строк или разрывов строк. Также есть значение «шайб» (это хороший знак), а также несколько других цифр и существительных.
Это хорошее начало — давайте теперь попробуем определить наиболее употребительные существительные. Сосредоточив внимание строго на существительных, мы сможем воспроизвести иерархию, подобную той, что наблюдалась в common_words
, с дополнительным преимуществом, заключающимся в том, что мы можем быть уверены, что маркер представляет собой класс людей, мест или вещей.
# noun tokens that aren't stop words or punctuations nouns = [token.text for token in doc if (not token.is_stop and not token.is_punct and token.pos_ == "NOUN")]
Теперь мы отображаем 5 наиболее часто встречающихся существительных. Мы также присвоим нашему наиболее употребительному существительному значение most_common_noun
для дальнейшего использования.
# display the five most common noun tokens noun_freq = Counter(nouns) common_nouns = noun_freq.most_common(5) most_common_noun = str(common_nouns[0][0]) print(common_nouns) print("The most common noun is:", most_common_noun, "or", most_common_noun.capitalize()) OUTPUT [('washer', 15), ('load', 10), ('water', 9), ('account', 9), ('information', 7)] The most common noun is: washer or Washer
Теперь мы определили человека, место или объект, на которые чаще всего ссылаются на нашей веб-странице. Учитывая доступную нам информацию, мы сделаем предположение, что это тема веб-страницы.
Примечательно, что этот результат сильно зависит от контента, связанного с веб-страницей — если веб-страница не имеет достаточного текстового контента, привлекающего внимание к ее основному предмету, наиболее распространенное существительное может оказаться совершенно не связанным с его истинным предметом.
Что, если мы проверим наш извлеченный текст на имена собственные? Это существительные, которые обозначают конкретное существо или вещь, обычно используя имя. Давайте попробуем использовать этот подход, чтобы попытаться установить марку нашей идентифицированной стиральной машины.
# noun tokens that arent stop words or punctuations names = [token.text for token in doc if (not token.is_stop and not token.is_punct and token.pos_ == "PROPN")] # five most common proper noun tokens name_freq = Counter(names) common_names = name_freq.most_common(5) print(common_names) OUTPUT [('Privacy', 5), ('Notice', 4), ('Load', 4), ('Wash', 4), ('Terms', 3)]
К сожалению, эти результаты выглядят не совсем правильно. Из-за отсутствия текстового содержания, в котором упоминается наша торговая марка, наша программа не может определить, что эта стиральная машина на самом деле является стиральной машиной Whirlpool. Есть еще один способ решить нашу проблему — с помощью функции распознавания именованных сущностей spaCy. Согласно документации spaCy:
spaCy может распознавать различные типы именованных сущностей в документе, запрашивая у модели прогноз. Обученные конвейеры по умолчанию могут идентифицировать множество именованных и числовых объектов, включая компании, местоположения, организации и продукты.
Категории распознавания именованных сущностей (NER) могут подпадать под следующие ярлыки:
Поскольку мы хотим идентифицировать только организацию/бренд, мы укажем это с помощью if (ent.label == ‘ORG')
в нашем прогоне NER ниже.
# attempt named entity recognition entities = [ent.text for ent in doc.ents if (ent.label_ == 'ORG')]
Обратите внимание, что домены также будут отнесены к категории организаций — мы активно продолжим удалять суффиксы доменов из нашего списка распознавания сущностей.
entities = list(map(lambda x: x.replace('.com','').replace('.ca',''), entities))
Теперь давайте отобразим наши результаты в том же формате, что и раньше — напечатайте пять наиболее часто наблюдаемых сущностей.
# display the five most common entities ent_freq = Counter(entities) common_entities = ent_freq.most_common(5) print(common_entities) OUTPUT [('Whirlpool', 3), ('AccountWhirlpool', 1), ('Whirlpool Canada', 1), ('emailWhirlpool Canada', 1), ('Home Depot', 1)]
Похоже, NER сработал — теперь мы знаем бренд, который чаще всего встречается на соответствующей веб-странице. Учитывая имеющуюся у нас информацию, мы можем предположить, что это марка нашей ранее идентифицированной стиральной машины. Давайте назначим этот недавно идентифицированный бренд most_common_entity
для дальнейшего использования.
most_common_entity = str(common_entities[0][0])
Подводя итог, на данный момент мы определили:
- Тема нашей веб-страницы, определенная spaCy как «стиральная машина».
- Бренд/организация нашей стиральной машины, идентифицированная spaCy как «Whirlpool».
Соответствующие описания и анализ настроений
Мы уже можем описать нашу стиральную машину как стиральную машину Whirlpool, но что, если бы мы нашли дополнительные описательные слова в форме прилагательных, а не сущностей?
Мы могли бы получить весь список прилагательных, найденных на нашей веб-странице, но это потребовало бы много ручной работы по созданию пар существительное-прилагательное — некоторые из них даже не представляли бы для нас интереса.
К счастью, модуль spaCy matcher
позволяет нам находить экземпляры прилагательных на нашей веб-странице, которые находятся рядом с нашей ранее определенной темой веб-страницы. Мы выражаем эту смежную связь с помощью шаблона — «найди мне все экземпляры тегов POS, которые являются прилагательными, за которыми сразу следует наиболее часто идентифицируемое существительное, «стиральная машина» / «стиральная машина».
nlp = spacy.load("en_core_web_trf") matcher = Matcher(nlp.vocab) doc = nlp(for_processing) pattern = [ [{"POS": "ADJ"}, {"TEXT": most_common_noun}], [{"POS": "ADJ"}, {"TEXT": most_common_noun.capitalize()}] ] matcher.add("ADJ_NOUN_PATTERN", pattern) matches = matcher(doc)
В нашем более раннем алгоритме обнаружения существительных мы видели, что на нашей веб-странице было найдено 15 экземпляров слова «шайба» — давайте отобразим результаты нашего сопоставления и посмотрим, сколько экземпляров с нашим наложенным шаблоном возвращается.
print("Total matches found: ", len(matches)) OUTPUT Total matches found: 3
В текстовом контенте есть три случая, когда наш шаблон совпадает. Давайте просмотрим пары существительное-прилагательное и назначим их переменной relevant_adjectives
.
relevant_adjectives = [] for match_id, start, end in matches: relevant_adjectives.append(doc[start].text) for match_id, start, end in matches: print("Match Found: ", doc[start:end].text) print("The relevant adjectives are:", relevant_adjectives) OUTPUT Match Found: smart washer Match Found: Best washer Match Found: smelly washer The relevant adjectives are: ['smart', 'Best', 'smelly']
spaCy смог распознать умный, лучший и вонючий прилагательные, связанные с нашим most_common_noun
. Это начало… однако «вонючий» не совсем похоже на прилагательное, которым вы хотели бы описать эту стиральную машину, особенно в маркетинговых материалах.
Мы можем провести анализ настроений связанных прилагательных, чтобы определить, имеют ли они «положительную» или «отрицательную» коннотацию. Для этого мы будем импортировать библиотеку TextBlob.
TextBlob — библиотека Python (2 и 3) для обработки текстовых данных. Он предоставляет согласованный API для погружения в общие задачи обработки естественного языка (NLP), такие как тегирование частей речи, извлечение именной фразы, анализ настроений и многое другое.
$ pip install -U textblob $ python -m textblob.download_corpora
Процедура проста — для каждого прилагательного в нашем ранее определенном relevant_adjectives
мы будем использовать TextBlob для анализа тональности прилагательного, а затем отобразим его полярность и субъективность.
Примечание. Полярность представлена в виде диапазона с плавающей запятой, где показатель полярности 1,0 указывает на абсолютное положительное значение, а полярность -1,0 указывает на абсолютное отрицательное значение.
Примечание. Субъективность представлена в виде диапазона с плавающей запятой, где показатель субъективности 1,0 указывает на то, что значение открыто для бесконечной интерпретации, а полярность -1,0 указывает на беспристрастное и объективное значение.
После того, как наши оценки проанализированы, мы создаем экземпляр переменной с именем positive_adjectives
для хранения прилагательных, которые передают условный аргумент, при этом принимаются только прилагательные с положительными значениями полярности.
#conduct sentiment analysis (1.0 polarity = positive, -1.0 polarity = negative) from textblob import TextBlob positive_adjectives = [] for adjective in relevant_adjectives: print(adjective, TextBlob(adjective).sentiment) if TextBlob(adjective).sentiment.polarity > 0: positive_adjectives.append(adjective) print("The positive adjectives are:", positive_adjectives) OUTPUT smart Sentiment(polarity=0.2, subjectivity=0.6) Best Sentiment(polarity=1.0, subjectivity=0.3) smelly Sentiment(polarity=0.0, subjectivity=0.0) The positive adjectives are: ['smart', 'Best']
Собираем все вместе — автоматизируем копирование
В своей предыдущей статье я представил пошаговое руководство по созданию инструмента автоматизации копирования рекламы на Python с прямым выводом в формате .CSV, отформатированном для совместимости с платформами Google Ads и Microsoft Ads.
Мы будем использовать лексические элементы, которые мы извлекли с нашей веб-страницы, для создания рекламного текста без необходимости вручную определять продукт, марку продукта и соответствующие прилагательные продукта. Я не буду воспроизводить весь код, который я написал в своей предыдущей статье — если вы хотите создать свою собственную программу, вы можете найти полный код в моем репозитории gitHub.
Я изменил исходный код программы Python Responsive Search Ads, чтобы он соответствовал нашему текущему примеру электронной коммерции Whirlpool. Напомним, что мы принимаем пользовательские данные для adjectives
, topic_phrases
, branded_phrases
и final_url
. Теперь все они могут быть заменены с помощью наших токенизированных текстовых извлечений.
Сначала мы расширяем наш список прилагательных, добавляя значения, хранящиеся в positive_adjectives
. Мы также изменим наш список положительных прилагательных, чтобы он соответствовал требованиям к регистру заголовков нашей копии, а также исправим форматирование некоторых пробелов, чтобы оно соответствовало нашему будущему фрейму данных.
adjectives = ['Quality', 'High Quality', 'Excellent', 'Great Quality'] adjectives.extend(positive_adjectives) adjectives = [adjective.title() for adjective in adjectives] adjectives = [adjective + ' ' for adjective in adjectives]
Переменная topic_phrases
больше не требует ввода пользователем, так как она была идентифицирована нашими POS-тегами как most.common_noun
. Еще раз, мы отформатируем эту переменную, чтобы она соответствовала форматированию регистра заголовка нашей копии.
В большинстве маркетинговых активаций мы относимся к ассортименту товаров во множественном числе. Здесь мы предоставляем условный аргумент, который ставит во множественное число соответствующий most.common_noun
, если он не заканчивается символом "s"
.
topic_phrases = most_common_noun.capitalize() suffix = "s" if topic_phrases.endswith(suffix) == False: topic_phrases = str(topic_phrases + suffix)
Переменная branded_phrases
больше не принимает пользовательский ввод, поскольку мы смогли определить марку стиральной машины с помощью нашего модуля NER. Мы добавляем пробел в конце строки этой переменной, чтобы обеспечить согласованность форматирования и удобочитаемость.
branded_phrases = most_common_entity + ' '
Наконец, переменная final_url
не требует ввода пользователем, так как это URL-адрес, с которого мы начали синтаксический анализ нашей веб-страницы — значение хранится в нашей первой созданной переменной url
.
final_url = url
Мы все сделали — давайте запустим и посмотрим результаты! (Примечание. Я транспонировал выходные данные .CSV, чтобы их было легче читать)
Давайте попробуем это на другой целевой странице для 4-дверного холодильника шириной 36 дюймов и посмотрим, что получится.
Нам удалось еще раз определить правильные лексические элементы.
OUTPUT #webpage subject identification The most common noun is: fridge or Fridge #webpage entity identification The most common entity is: Whirlpool #adjectives matched to our most common noun The relevant adjectives are: ['new', 'current', 'Great', 'old', 'Great'] #relevant adjective sentiment analysis new Sentiment(polarity=0.1, subjectivity=0.4) current Sentiment(polarity=0.0, subjectivity=0.4) Great Sentiment(polarity=0.8, subjectivity=0.75) old Sentiment(polarity=0.1, subjectivity=0.2) The positive adjectives are: ['new', 'Great', 'old'] #'old' in this case is associated to be very slightly positive. For more strict parsing of polarity, increase the threshold of acceptance (if TextBlob(adjective).sentiment.polarity > 0:)
Мы подошли к концу нашего путешествия по НЛП — спасибо за чтение! Я надеюсь, что вы нашли это руководство полезным для автоматизации некоторых ваших маркетинговых тактик. Как всегда, полный исходный код доступен в моем репозитории gitHub — https://github.com/rfinatan/Natural-Language-Processing-for-Marketing-and-E-Commerce-Using-spaCy.
Вы также можете найти больше похожих проектов на странице моего портфолио. Если вы хотите подключиться, пожалуйста, не стесняйтесь добавить меня в LinkedIn.