{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Datacamp Search Engine Marketing Campaigns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a heavy user of Datacamp since two years, I learned quite a lot. My main business is in online marketing, and I have benefited a lot from the data science skills I learned. I'm now able to manage way larger and more complex campaigns, and efficiently maintain them. Since I'm quite familiar now with the content and the different topics and packages in R and Python, I thought about how I might create search campaigns for Datacamp, just as an exercise. Hopefully, this serves as a real life example of how these skills are used in real projects. \n", "Here goes. \n", "\n", "The three main components of a search campaign are: \n", "1. **Keywords:** the user's intention, what they are looking for. \n", "2. **Ads:** your promise to the user for that intention. \n", "3. **Landig pages:** your delivery of the promise. \n", "\n", "Mapping those three elements properly is around 70-80% of the job. If you get this right, then maintenance and changes become much easier. \n", "\n", "So, in order to be specific and relevant to users, we need to promote the stuff that we have, and mainly focus on sending the right person to the right page, through the right message / expectation. \n", "\n", "A good campaign structure reflects and follows the structure of the website, which is essentially reflecting the business strategy. \n", "\n", "The basic unit of organization is called the ad group. This is where the mapping of keywords, ads and landing pages happens. Every ad group needs to be part of a campaign. At the campaign level we have settings that govern all ad groups in the campaign; language targeting, locations, devices, time of day, etc. \n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As shown above this is the mapping that we want to achieve. As output, we want to end up having two main data frames, one for keywords and another for ads, something like the two data frames below. \n", "Once these are done, we would be ready to upload them as csv files to Google AdWords (other platforms works exactly the same way, but with minor tweaks needed (Bing, Yahoo, Yandex, etc)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Keywords" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "|Campaign |Ad Group |Keyword |\n", "|----------|---------|---------|\n", "|campaign 1|adgroup 1|keyword 1|\n", "|campaign 1|adgroup 2|keyword 2|\n", "|campaign 2|adgroup 1|keyword 3|\n", "|campaign 2|adgroup 1|keyword 4|\n", "|campaign 2|adgroup 2|keyword 5|" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Ads" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "|Campaign |Ad Group | Display URL | Final URL | Headline 1 | Headline 2 |\n", "|-----------|----------|--------------|----------------|------------|-------------|\n", "|campaign 1 |adgroup 1 | datacamp.com | datacamp.com/a | Headline 1 | Headline 1a |\n", "|campaign 1 |adgroup 2 | datacamp.com | datacamp.com/b | Headline 1 | Headline 1a |\n", "|campaign 2 |adgroup 1 | datacamp.com | datacamp.com/c | Headline 1 | Headline 1a |\n", "|campaign 2 |adgroup 2 | datacamp.com | datacamp.com/d | Headline 2 | Headline 2a |\n", "|campaign 2 |adgroup 3 | datacamp.com | datacamp.com/e | Headline 3 | Headline 1a |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looking at the different ways in which someone can express interest in something that we provide, here is an initial list of some of those ways: \n", "\n", "1. **Instructors**: 'Hadley Wickham course', 'courses by Garrett Grolemund', etc\n", "2. **Technologies**: 'python course', 'r courses', 'sql data science course', etc\n", "3. **Courses**: names of specific courses that we have; 'unsupervised learning in python', 'deep learning in python', etc.\n", "4. **Topics**: 'machine learning courses', 'data visualization course', etc\n", "5. **Packages / libraries**: 'learn ggplot', 'matplotlib tutorial', etc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You will notice in setting up the keywords that I won't go through the traditional keyword research phase, and that's because we have the power of the programming language to generate all possible combinations and group them in their respective campaigns and ad group. \n", "\n", "Therefore, we need two main things to know in order to do this setup: \n", "1. Product knowledge: knowing what courses and topics we have, how they are organized on the website, and how they are grouped. This we can see by browsing, and we can defnitely get better insights with internal knowledge (especially if there are changes planned!).\n", "2. Relevant keywords: all the possible ways in which someone might express desire in the things that we provide; 'course', 'learn', 'education', 'tutorial', etc. This can be done by brainstorming, looking into keyword tools, and other methods. \n", "\n", "Let's start by generating the keywords." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Keyword Generation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Instructors\n", "This campaign will be targeting people who search for courses by any of the instructors at Datacamp. \n", "We can have the keywords restricted to \"`course by <instructor name>`\" or we can go for a broad set of keywords targeting the instructor name only. At the beginning, it's better to target the names with 'course' or 'courses' and see the effect. \n", "We will also need to check if any of the names that we have also happens to be the name of another famous person, or a very common name, and then we will need to restrict the keywords for that instructor. \n", "\n", "We begin by getting the names of the instructors and the URLs for each:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import requests\n", "from bs4 import BeautifulSoup\n", "\n", "\n", "instructors_page = 'https://www.datacamp.com/instructors?all=true'\n", "instructor_link_selector = '.instructor-block__description .instructor-block__link' # CSS class of the link\n", "instructor_name_selector = '.mb-sm' # CSS class of the name\n", "\n", "instructor_resp = requests.get(instructors_page)\n", "soup = BeautifulSoup(instructor_resp.text, 'lxml')\n", "\n", "instructor_urls = [url['href'] for url in soup.select(instructor_link_selector)]\n", "instructor_names = [name.text.strip() for name in soup.select(instructor_name_selector)]\n", "instructor_urls = ['https://www.datacamp.com' + url for url in instructor_urls]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We put them in a data frame for later use. The URLs will be used later for generating ads." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(65, 2)\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>name</th>\n", " <th>url</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>Filip Schouwenaars</td>\n", " <td>https://www.datacamp.com/instructors/filipsch</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Jonathan Cornelissen</td>\n", " <td>https://www.datacamp.com/instructors/jonathana...</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Hugo Bowne-Anderson</td>\n", " <td>https://www.datacamp.com/instructors/hugobowne</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Nick Carchedi</td>\n", " <td>https://www.datacamp.com/instructors/nickyc</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Greg Wilson</td>\n", " <td>https://www.datacamp.com/instructors/greg48f64...</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " name url\n", "0 Filip Schouwenaars https://www.datacamp.com/instructors/filipsch\n", "1 Jonathan Cornelissen https://www.datacamp.com/instructors/jonathana...\n", "2 Hugo Bowne-Anderson https://www.datacamp.com/instructors/hugobowne\n", "3 Nick Carchedi https://www.datacamp.com/instructors/nickyc\n", "4 Greg Wilson https://www.datacamp.com/instructors/greg48f64..." ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "instructor_df = pd.DataFrame({\n", " 'name': instructor_names,\n", " 'url': instructor_urls\n", "})\n", "print(instructor_df.shape)\n", "instructor_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generate Instructor Keywords\n", "\n", "Now that we have the names of instructors, we will be using a template whereby we combine each name with a set of keywords related to our topic, which is mainly courses by the instructor. \n", "\n", "Variables used in the code below:\n", "\n", "`col_names`: This is a list of the header names of the table that we will end up uploading to Google AdWords. \n", "`words`: The words that we will be combining with the instructor names to generate the full keywords / phrases. \n", "`match_types`: More details can be found on [AdWords help center](https://support.google.com/adwords/answer/2497836?hl=en), but here are the basics. \n", "[data science course], \"data science course\", and data science course are technically three different keywords.\n", "- exact match (in brackets), will only trigger ads if a user searches for exactly that keyword, 'data science course', for example, written exactly like this.\n", "- phrase match (with quotes) , will trigger our ads if a user searches for the exact string together with anything before, or after it. So \"best data science course\", or \"data science course online\" would trigger our ads. \n", "- broad match (no punctuation) would trigger our ads if someone searches for anything similar to or related to 'data science course'. This is up to Google's algorithms, and you have to be careful with it, as it might trigger ads when someone searches for 'data science platform' for example, which is not exactly what we are trying to promote. I like to use the modified broach match more, because it restricts targeting a little more. This is basically triggering ads if someoe searches for any derivative of the word, and not any similar word in meaning. This is denoted with a '+' sign at the begininning of the word. So, '+game' would trigger ads by 'gaming', 'gamers', but by 'play'. \n", "\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total keywords: 2340\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Keyword</th>\n", " <th>Criterion Type</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars course</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars course</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>+filip +schouwenaars +course</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars courses</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars courses</td>\n", " <td>Phrase</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Keyword Criterion Type\n", "0 SEM_Instructors Filip Schouwenaars filip schouwenaars course Exact\n", "1 SEM_Instructors Filip Schouwenaars filip schouwenaars course Phrase\n", "2 SEM_Instructors Filip Schouwenaars +filip +schouwenaars +course Broad\n", "3 SEM_Instructors Filip Schouwenaars filip schouwenaars courses Exact\n", "4 SEM_Instructors Filip Schouwenaars filip schouwenaars courses Phrase" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "col_names = ['Campaign', 'Ad Group', 'Keyword', 'Criterion Type']\n", "instructor_keywords = []\n", "\n", "words = ['course', 'courses', 'learn', 'data science', 'data camp', 'datacamp']\n", "match_types = ['Exact', 'Phrase', 'Broad']\n", "for instructor in instructor_df['name']:\n", " for word in words:\n", " for match in match_types:\n", " if match == 'Broad':\n", " keyword = '+' + ' +'.join([instructor.replace(' ', ' +').lower(), word]) # modified broach match\n", " else:\n", " keyword = instructor.lower() + ' ' + word\n", " row = ['SEM_Instructors', # campaign name\n", " instructor, # ad group name\n", " keyword, # instructor <keyword>\n", " match] # keyword match type\n", " instructor_keywords.append(row)\n", "\n", "# do the same by having the keywords come before the instructor name\n", "for instructor in instructor_df['name']:\n", " for word in words:\n", " for match in match_types:\n", " if match == 'Broad':\n", " keyword = '+' + ' +'.join([word, instructor.replace(' ', ' +').lower()])\n", " else:\n", " keyword = word + ' ' + instructor.lower() \n", " row = ['SEM_Instructors', # campaign name\n", " instructor, # ad group name\n", " keyword, # <keyword> instructor \n", " match] # keyword match type\n", " instructor_keywords.append(row)\n", " \n", "\n", "instructor_keywords_df = pd.DataFrame.from_records(instructor_keywords, \n", " columns=col_names)\n", "print('total keywords:', instructor_keywords_df.shape[0])\n", "instructor_keywords_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Basically, what are doing is looping over the instructor names, all the different keywords, and all the match types that we have, so that we have all possible combinations. We are doing it twice, once to have the template 'instructor name keyword' and 'keyword instructor name'. \n", "\n", "Now we simply repeat the same for all of our segments with minor modifications on keywords, and how we extract the data. \n", "A good guideline I learned from [R for Data Science](http://r4ds.had.co.nz/functions.html) is that if you are going to copy and paste more than twice, it's time to write a function! " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def generate_keywords(topics, keywords, match_types=['Exact', 'Phrase', 'Broad'],\n", " campaign='SEM_Campaign'):\n", " col_names = ['Campaign', 'Ad Group', 'Keyword', 'Criterion Type']\n", " campaign_keywords = []\n", " \n", " for topic in topics:\n", " for word in keywords:\n", " for match in match_types:\n", " if match == 'Broad':\n", " keyword = '+' + ' +'.join([topic.lower().replace(' ', ' +'), word.replace(' ', ' +')])\n", " else:\n", " keyword = topic.lower() + ' ' + word\n", " row = [campaign, # campaign name\n", " topic, # ad group name\n", " keyword, # instructor <keyword>\n", " match] # keyword match type\n", " campaign_keywords.append(row)\n", "\n", " # I said more than twice! :) \n", " for topic in topics:\n", " for word in keywords:\n", " for match in match_types:\n", " if match == 'Broad':\n", " keyword = '+' + ' +'.join([word.replace(' ', ' +'), topic.lower().replace(' ', ' +')])\n", " else:\n", " keyword = word + ' ' + topic.lower()\n", " row = [campaign, # campaign name\n", " topic, # ad group name\n", " keyword, # <keyword> instructor\n", " match] # keyword match type\n", " campaign_keywords.append(row)\n", "\n", " return pd.DataFrame.from_records(campaign_keywords, columns=col_names)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's give it a try:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Keyword</th>\n", " <th>Criterion Type</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Campaign</td>\n", " <td>Data Science</td>\n", " <td>data science course</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Campaign</td>\n", " <td>Data Science</td>\n", " <td>data science course</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Campaign</td>\n", " <td>Data Science</td>\n", " <td>+data +science +course</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>SEM_Campaign</td>\n", " <td>Data Science</td>\n", " <td>data science tutorial</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>SEM_Campaign</td>\n", " <td>Data Science</td>\n", " <td>data science tutorial</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>5</th>\n", " <td>SEM_Campaign</td>\n", " <td>Data Science</td>\n", " <td>+data +science +tutorial</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>6</th>\n", " <td>SEM_Campaign</td>\n", " <td>Machine Learning</td>\n", " <td>machine learning course</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>7</th>\n", " <td>SEM_Campaign</td>\n", " <td>Machine Learning</td>\n", " <td>machine learning course</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>8</th>\n", " <td>SEM_Campaign</td>\n", " <td>Machine Learning</td>\n", " <td>+machine +learning +course</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>9</th>\n", " <td>SEM_Campaign</td>\n", " <td>Machine Learning</td>\n", " <td>machine learning tutorial</td>\n", " <td>Exact</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Keyword Criterion Type\n", "0 SEM_Campaign Data Science data science course Exact\n", "1 SEM_Campaign Data Science data science course Phrase\n", "2 SEM_Campaign Data Science +data +science +course Broad\n", "3 SEM_Campaign Data Science data science tutorial Exact\n", "4 SEM_Campaign Data Science data science tutorial Phrase\n", "5 SEM_Campaign Data Science +data +science +tutorial Broad\n", "6 SEM_Campaign Machine Learning machine learning course Exact\n", "7 SEM_Campaign Machine Learning machine learning course Phrase\n", "8 SEM_Campaign Machine Learning +machine +learning +course Broad\n", "9 SEM_Campaign Machine Learning machine learning tutorial Exact" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "topics = ['Data Science', 'Machine Learning']\n", "keywords = ['course', 'tutorial']\n", "generate_keywords(topics, keywords).head(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks good. Now we let's generate the relevant topics and keywords for each of our segments\n", "\n", "## 2. Technologies" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total keywords: 750\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Keyword</th>\n", " <th>Criterion Type</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Technologies</td>\n", " <td>R</td>\n", " <td>r data science</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Technologies</td>\n", " <td>R</td>\n", " <td>r data science</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Technologies</td>\n", " <td>R</td>\n", " <td>+r +data +science</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>SEM_Technologies</td>\n", " <td>R</td>\n", " <td>r programming</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>SEM_Technologies</td>\n", " <td>R</td>\n", " <td>r programming</td>\n", " <td>Phrase</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Keyword Criterion Type\n", "0 SEM_Technologies R r data science Exact\n", "1 SEM_Technologies R r data science Phrase\n", "2 SEM_Technologies R +r +data +science Broad\n", "3 SEM_Technologies R r programming Exact\n", "4 SEM_Technologies R r programming Phrase" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "topics = ['R', 'Python', 'SQL', 'Git', 'Shell'] # listed on the /courses page\n", "keywords = ['data science', 'programming', 'analytics', 'data analysis', 'machine learning',\n", " 'deep learning', 'financial analysis', 'data viz', 'visualization', 'data visualization',\n", " 'learn', 'course', 'courses', 'education', 'data import', 'data cleaning', \n", " 'data manipulation', 'probability', 'stats', 'statistics', 'course', 'courses',\n", " 'learn', 'education', 'tutorial'] # @marketing_team: this list can / should be refined or \n", " # expanded based on the strategy and how specific the \n", " # targeting needs to be\n", "tech_keywords = generate_keywords(topics, keywords, campaign='SEM_Technologies')\n", "print('total keywords:', tech_keywords.shape[0])\n", "tech_keywords.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Courses\n", "\n", "This is probably the most specific, and therefore the most relevant of the segments to target people. If someone is searching for \"data visualization with r\" and we have that course, then the ad would be extremely relevant, because we would have the right landing page that exactly satisfies that user's need. \n", "\n", "One small problem. Some of the course names don't correspond to what a user would typically search for:\n", "'Machine Learning with the Experts: School Budgets', 'Sentiment Analysis in R: The Tidy Way'. These are NOT bad course names. They just need some attention as to selecting the proper keywords that people might use to search for them. \n", "\n", "Again, we can scrape the names and correspnding URLs as we did with the instructors' campaign. \n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "courses_page = 'https://www.datacamp.com/courses/all'\n", "course_link_selector = '.courses__explore-list .course-block'\n", "\n", "course_resp = requests.get(courses_page)\n", "soup = BeautifulSoup(course_resp.text, 'lxml')\n", "\n", "course_urls = [link.contents[1]['href'] for link in soup.select(course_link_selector)] \n", "course_urls = ['https://www.datacamp.com' + url for url in course_urls]\n", "course_names = [link.h4.text for link in soup.select(course_link_selector)]" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total keywords: 95\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>name</th>\n", " <th>url</th>\n", " <th>name_clean</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Intro to Python for Data Science</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " <td>Introduction to R</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " <td>Intermediate Python for Data Science</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Intro to SQL for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-sql-...</td>\n", " <td>Intro to SQL for Data Science</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Intermediate R</td>\n", " <td>https://www.datacamp.com/courses/intermediate-r</td>\n", " <td>Intermediate R</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " name url name_clean\n", "0 Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Intro to Python for Data Science\n", "1 Introduction to R https://www.datacamp.com/courses/free-introduc... Introduction to R\n", "2 Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-... Intermediate Python for Data Science\n", "3 Intro to SQL for Data Science https://www.datacamp.com/courses/intro-to-sql-... Intro to SQL for Data Science\n", "4 Intermediate R https://www.datacamp.com/courses/intermediate-r Intermediate R" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "course_df = pd.DataFrame({\n", " 'name': course_names,\n", " 'url': course_urls\n", "})\n", "course_df['name_clean'] = course_df.name.str.replace('\\(.*\\)', '').str.strip() # remove (part x)\n", "print('total keywords:', course_df.shape[0])\n", "course_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will do the same (use the `generate_keywords` function) for the courses, but we need to be careful, as they need to be reviewed because as mentioned above, some of the names are not really what people would look for, and we just need to account for that case-by-case. The following should be good enough for a start, and then we can see the data and make decisions. \n", "Please note that using the empty character below is not a mistake. The names of course are long and specific enough that they are fit to be keywords in and of themselves, without having to add other qualifier keywords like 'learn' or 'course'. So we will be using the course names alone, as well as with the qualifier keywords. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total keywords: 3420\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Keyword</th>\n", " <th>Criterion Type</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>intro to python for data science</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>intro to python for data science</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>+intro +to +python +for +data +science +</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>intro to python for data science learn</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>intro to python for data science learn</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>5</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>+intro +to +python +for +data +science +learn</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>6</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>intro to python for data science course</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>7</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>intro to python for data science course</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>8</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>+intro +to +python +for +data +science +course</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>9</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>intro to python for data science courses</td>\n", " <td>Exact</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Keyword Criterion Type\n", "0 SEM_Courses Intro to Python for Data Science intro to python for data science Exact\n", "1 SEM_Courses Intro to Python for Data Science intro to python for data science Phrase\n", "2 SEM_Courses Intro to Python for Data Science +intro +to +python +for +data +science + Broad\n", "3 SEM_Courses Intro to Python for Data Science intro to python for data science learn Exact\n", "4 SEM_Courses Intro to Python for Data Science intro to python for data science learn Phrase\n", "5 SEM_Courses Intro to Python for Data Science +intro +to +python +for +data +science +learn Broad\n", "6 SEM_Courses Intro to Python for Data Science intro to python for data science course Exact\n", "7 SEM_Courses Intro to Python for Data Science intro to python for data science course Phrase\n", "8 SEM_Courses Intro to Python for Data Science +intro +to +python +for +data +science +course Broad\n", "9 SEM_Courses Intro to Python for Data Science intro to python for data science courses Exact" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "keywords = ['', 'learn', 'course', 'courses', 'tutorial', 'education']\n", "course_keywords = generate_keywords(course_df['name_clean'], keywords, campaign='SEM_Courses')\n", "print('total keywords:', course_keywords.shape[0])\n", "course_keywords.head(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Topics\n", "\n", "These are basically generic topics that people might be intersted in searching for. They are covered by the 'tracks' section, which has skills and career as sub-sections. For our purposed they can be grouped under the same campaign. \n", "\n", "The process is again the same. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Skills" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "skills_page = 'https://www.datacamp.com/tracks/skill'\n", "skills_link_selector = '#all .shim'\n", "\n", "skills_resp = requests.get(skills_page)\n", "skill_soup = BeautifulSoup(skills_resp.text, 'lxml')\n", "\n", "skills_urls = [link['href'] for link in skill_soup.select(skills_link_selector)] \n", "skills_names = [skill.replace('/tracks/', '').replace('-', ' ') for skill in skills_urls]\n", "skills_urls = ['https://www.datacamp.com' + url for url in skills_urls]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Careers" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "career_page = 'https://www.datacamp.com/tracks/career'\n", "career_link_selector = '#all .shim'\n", "\n", "career_resp = requests.get(career_page)\n", "career_soup = BeautifulSoup(career_resp.text, 'lxml')\n", "\n", "career_urls = [link['href'] for link in career_soup.select(career_link_selector)] \n", "\n", "career_names = [career.replace('/tracks/', '').replace('-', ' ') for career in career_urls]\n", "career_urls = ['https://www.datacamp.com' + url for url in career_urls]" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>name</th>\n", " <th>url</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>R Programming</td>\n", " <td>https://www.datacamp.com/tracks/r-programming</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Importing Cleaning Data With R</td>\n", " <td>https://www.datacamp.com/tracks/importing-clea...</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Data Manipulation With R</td>\n", " <td>https://www.datacamp.com/tracks/data-manipulat...</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Python Programming</td>\n", " <td>https://www.datacamp.com/tracks/python-program...</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Importing Cleaning Data With Python</td>\n", " <td>https://www.datacamp.com/tracks/importing-clea...</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " name url\n", "0 R Programming https://www.datacamp.com/tracks/r-programming\n", "1 Importing Cleaning Data With R https://www.datacamp.com/tracks/importing-clea...\n", "2 Data Manipulation With R https://www.datacamp.com/tracks/data-manipulat...\n", "3 Python Programming https://www.datacamp.com/tracks/python-program...\n", "4 Importing Cleaning Data With Python https://www.datacamp.com/tracks/importing-clea..." ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tracks_df = pd.DataFrame({\n", " 'name': skills_names + career_names,\n", " 'url': skills_urls + career_urls\n", "})\n", "tracks_df['name'] = [x.title() for x in tracks_df['name']]\n", "tracks_df.head()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total keywords: 720\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Keyword</th>\n", " <th>Criterion Type</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Tracks</td>\n", " <td>R Programming</td>\n", " <td>r programming</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Tracks</td>\n", " <td>R Programming</td>\n", " <td>r programming</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Tracks</td>\n", " <td>R Programming</td>\n", " <td>+r +programming +</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>SEM_Tracks</td>\n", " <td>R Programming</td>\n", " <td>r programming learn</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>SEM_Tracks</td>\n", " <td>R Programming</td>\n", " <td>r programming learn</td>\n", " <td>Phrase</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Keyword Criterion Type\n", "0 SEM_Tracks R Programming r programming Exact\n", "1 SEM_Tracks R Programming r programming Phrase\n", "2 SEM_Tracks R Programming +r +programming + Broad\n", "3 SEM_Tracks R Programming r programming learn Exact\n", "4 SEM_Tracks R Programming r programming learn Phrase" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tracks_keywords = generate_keywords(tracks_df['name'], keywords, campaign='SEM_Tracks')\n", "print('total keywords:', tracks_keywords.shape[0])\n", "tracks_keywords.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we concatenate all the data frames that we created so far in one data frame." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total keywords: 7230\n", "total campaigns: 4\n", "total ad groups: 175\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Keyword</th>\n", " <th>Criterion Type</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars course</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars course</td>\n", " <td>Phrase</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>+filip +schouwenaars +course</td>\n", " <td>Broad</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars courses</td>\n", " <td>Exact</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>SEM_Instructors</td>\n", " <td>Filip Schouwenaars</td>\n", " <td>filip schouwenaars courses</td>\n", " <td>Phrase</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Keyword Criterion Type\n", "0 SEM_Instructors Filip Schouwenaars filip schouwenaars course Exact\n", "1 SEM_Instructors Filip Schouwenaars filip schouwenaars course Phrase\n", "2 SEM_Instructors Filip Schouwenaars +filip +schouwenaars +course Broad\n", "3 SEM_Instructors Filip Schouwenaars filip schouwenaars courses Exact\n", "4 SEM_Instructors Filip Schouwenaars filip schouwenaars courses Phrase" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "full_keywords_df = pd.concat([instructor_keywords_df, tech_keywords, course_keywords, tracks_keywords])\n", "print('total keywords:', full_keywords_df.shape[0])\n", "print('total campaigns:', len(set(full_keywords_df['Campaign'])))\n", "print('total ad groups:', len(set(full_keywords_df['Ad Group'])))\n", "full_keywords_df.to_csv('keywords.csv', index=False)\n", "full_keywords_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we are ready to go with our keywords, and the full set can be found [here](keywords.csv). Next we need to generate ads for each of our ad groups. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's generate the ads." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ad Generation / Copy Writing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Strategy\n", "\n", "We currently have the following: \n", "1. Campaigns and ad group names properly mapped to each other in the `full_keywords_df` data frame\n", "2. Ad group name and the corresponding URL from our previous scraping (courses, instructors, and tracks), and we need to generate one for technologies. \n", "\n", "Here is the plan: \n", "\n", "1. Create ad templates to use (2-3 should be good to start with)\n", "4. Create a `Campaign` column and add it to the (name, url) data frames\n", "2. Merge all the (name, url) data frames into one data frame\n", "3. Generate all ads for all ad groups; this consists of the following fields for each ad (these would be new columns in the same data frame we are working with): \n", " * Headline 1: maximum 30 charaters\n", " * Headline 2: maximum 30 charaters\n", " * Display URL: automatically inferred from the final URL\n", " * Final URL: the full path where the user will end up (has to be the same domain as the display URL \n", "\n", "5. Make sure the work is consistent with the keywords data frame\n", "6. Upload and launch campaigns! \n", "\n", "Generating ads simply means replacing the topics name (course, tech, instructor, etc) where it belongs in the template, and making 2-3 ad variations for each of the ad groups that we have. \n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ad Templates\n", "\n", "An important thing to take note of, is that although plugging in the ad group names where they belong is a straighforward process, the problem is that we need to have the length of the fields under the limits mentioned above. And since the names of our courses and topics vary a lot, we need to see what can be done about it. Let's see how much of a problem that is. " ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "image/png": "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\nMi/sp8ydyu8OQzjXsdw6lGMg57ozKM9zirJcW2xrt9q+wPaHnDpB7ievbOcAg6on13BN4GrnvuV1\neGHp/b6P013KsYbt/7B9UpHfPbbvLn7/D23v1Wd9lvfrr+23PCMvIngtoy+lnt+vVWrBcqOkNYv3\nx4v3Gq/5FfI6q5R+3xp+y+eU7phEhdcFkh5XMd/tlXrB75bfUZJWb6mDnnVWcf6rKT3z3m3+E5Ke\nJmnf0nsLO+Q3ZdkW731C0kNt8j2rzffXrFCekHS7pP16/Lae5W3znYnSd8Y7pFlYSrNA0uaSLu9R\n3uMkrZJpmc1vWTYuyvFwl/nfLGn7Hvm+u8Nyave6WtLWFcpa/s64UgeTd3XJ91pJG5S+v4+ke7uk\n/4ukjSvW28sk/bXCb/uzpO0y7jumbA890m6l1AdGlfX/DT3yal1PH6vUdLZbvj+VtHKFcu5U1H2v\n/dZqqrDfKsr3yLYqaZ6kE3rk/xtJa/fYPqq89m35/iaSLuvj+/+WYR1Z2CbfqyV9XGlUpCzr4qBf\nSi1Xyr9hpw7pDi+l+UXFvP9c+s6BGcq6dSm/f/TxveUkfUHdzwduUXrMqbwuntUj35zHvvHyd4r3\n3ibpvi753i3p+T3yLW+nrdvNgi55t3uN97m8+sl/QYe6mGiTbz9lXjiD9W1Bu/INY7n1Wc6BnOtW\nnPe+5e1F0pjSyGPdyvDnquuSMp4DDKqelPmaQBXPg1Xt3Le8Di/UDI7THfJ/m6TFFerzoqrLvMj3\nytJ3n5NrfR2F19yKGKFfB0vauJh+d0TcninfDW2/ValzvVUl3aY02sGvI6KvYfv68Hili9mQdI3S\nCfAtSqMhzFO6OGo877ejpHNsb9ftN9t+kqTTJK1VevsKSb9Tuth9stIO91UaQMdtTndgT9bUOziL\nJf2q+PtYpZPEDSX9TOnEst95fEDNITGvU9rh3i3pCW3Srq10EbhV6e2/F+/dqXQC8hylpsLzJH3T\n9noR8SnVZ55S3WwsaVKp7q5XWqa7qvns+F6SfmJ7j4h4OHMZ/qt4SWlErd8rPZK3ldLFsJSGWP2p\n7a0i4o6ls5AkbaBm0/nrlUYR+YfSCd5qkp4oaYcizeZKI4hsF9WHxX2+UmeYyymdxFyotP08WSmI\nJ6V6/JntbZUCMt9VWvfL6beR1LjT/3hJJ9jevlu92n6v0iMHje3oPqV1cUIpOLWxpGcqndRtqrT9\n7hYRF1b8bTNm+9mSTlRap6R0Avc7peVwr6T1JT1Lad2aJ+lw2/Mi4isVsl9d0s8lPUmpDs9RWsar\nSnq20uOcUhoZ5n8l/UeXcu5Q5FVupXCZpEuV6nJrpeX5qgrlamd5SccqDbn4oNIJ6p+VWj7sJGmz\nIt3TJX2/KHPZ3yQ16uRdpfe/rxTga3VVY8L26kqd421Y+vxKpSDqbZJWUmpJsY3a7MNm4PAi3+ep\n2cJjc0mflHSg7XOVOow/OiJuyzjfbGyPSfqf0lvXK22z7Wxdmr6k4iwuUdo2W78/Xf9amu6nL5av\nq9k6V5LuUbrYuVFpP7ur0sg3P1bqVLenQR/7bO+jZr9410j6rdJxeFOl7X95pX38cba3jojrq+Tb\n4kKl7W4DNTs8vktpu2vnzmnmL0m7S9qimP6tll7P+tlvN/J8udI5j9R5BMusI1v1MqTl1ir7ue4M\nfEMpSLBE6cL6j0rHmO1LZdhU6RzgaRHxUKeMBnAOMOeuCfo00+P0FLY/L2n/0lt3Sjpf0g1K54xb\nKNXjckrnob+x/YyIuK5CWc+WtGUx/Xyl8/S5oe4oD696XkobWePO+o9bPhvX1Kjk/Ar5naXe0c1G\nxPhFA/g9H5L0VknrdUmzmdIOslGWr3dJu4LSjrOR9p+SXtAm3S5KO5lQunCuXGcVftPHW+ruIElj\nLWnmKZ3ch9LBo2tEvM2yfVCp08JXtkm7Usv/x7d8792SlmtJ81ilUS4a6ZZIem6Hsuzbq7xtvjNR\n+s54hzQLS2kay+R0lVpgNH6f0kVHuT4+lGG5zW+Z/xKlOx87t0m7m9LBqpH+Y13yfYuk90h6Qpc0\nj5X0o1J+p/Yoa/m331+Upd268GpNbUXzYaUT9DskvaJN+le1pH99lzI8X807Tg8pdbw9r8NvO7GU\n54Sk1TIsr0fqoEuaDZRaGzXSnqA2LRiUgiL/Xfo9k+rQQqllPW1su0dIWqcl3YpKAdRG2oc7rQPF\nOl2+K/R3Sbu1Sfd0Ne/Glfcb8zvku6BNWX/eWg6lE8sPtKxXu1Rc/9puzy3p/7OU/kZ1aIlRpN1U\n0sckvWSm60gpz3WVAlznt5S9vL0fr3RBOJZrvjMo70pKNzv20dTWPw+oy911pcBEI+07Ks6rvC/9\nQ4ayH1nK7+MVv/OKluVxhFr2JcU2+tVSPTTSntUl39zHvvGWct6vdI7x4jZpn1Ks642035phvc4v\n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\nAAAAwIJyJcsV/Wm6BMuTW2sPaa09o7V2nyR/nOTWSZ4/1+gAAACAhSXJ0quqmye5f5JdSV65bPFz\nk1yc5JFVdeAWhwYAAABsA5IsS+7Tv76ntXb5+ILW2oVJ/iXJNZLcbasDAwAAABafJMuSW/evp09Y\n/oX+9VZbEAsAAACwzVRrbd4xLISqenWSxyd5fGvtNSssf36S45Ic11r7v6vUc+qERUcecMABV7nF\nLW4xSLyTXHbZZUmS/fcfbkzj//nWdza1/g9e6+oDRdLZbDzJsDFddtll+da3v5tLL1+77FrxDLVt\nQ7bRUPt/6G27Wp8i3ki7L9q2Dd1Gs4ppI22ub09f16R61tPui7Zt26VvLzdNm+vb09c1TT03+8qZ\n339/1o0On1lMi1bPvGNaqa/r27OtZ3mbL0JMs6hnkWIatfl1Dh5+24Yy7zYaup4kuf5BV00y7Hnp\nLJ1xxhm55JJLzmutHbrZuiRZelMkWV6Q5JlJntlae+Eq9UxKsvxwkovSjfkyS0f0r5+f8fewRJvP\nh3bfetp8PrT71tPmW0+bz4d233rafOtp8/nYbu2+M8kFrbWbbbai7ZFW2hrn96+HTFh+8LJyK2qt\n3XGwiDZglOSZdxz7Em0+H9p962nz+dDuW0+bbz1tPh/afetp862nzedjX253Y7Is+a/+ddKYK7fs\nXyeN2QIAAADswyRZlnygf71/VV2hXarqmkmOSvKdJP+61YEBAAAAi0+SpddaOzPJe9Ldi/WkZYt/\nN8mBSd7QWrt4i0MDAAAAtgFjslzRryY5JcnLq+q+Sf4zyV2T3DvdbUK/M8fYAAAAgAXmSpYx/dUs\nd0pyQrrkytOSHJ7k5Unu3lrbPb/oAAAAgEXmEc4AAAAAA3AlCwAAAMAAJFkAAAAABiDJAgAAADAA\nSRYAAACAAUiyAAAAAAxAkgUAAABgAJIsAAAAAAOQZNlLVNWNquovqursqrq0qnZV1Uur6trzjm1v\n1bdxmzCdM+/4tquqelhVvaKqPlxVF/Tt+aY11rlHVb2rqs6rqm9X1aer6ilVdZWtinu7W0+7V9XO\nVfp+q6o3b3X8201VHVpVj6uqd1TVGVX1nao6v6o+UlWPraoV/33W1zdnve2urw+jql5UVSdV1Zf7\nNj+vqj5VVc+tqkMnrKOvb8J62lw/n52qeuRYOz5uQpmfrqqT+2PRRVX1b1X16K2OdW+yWrtX1dFr\n9PcXzivu7WIj50D72jF9/3kHwOZV1eFJTklyvSTvTPL5JHdJ8htJHlhVR7XWds8xxL3Z+UleusL8\ni7Y6kL3Is5Icma4Nv5LkiNUKV9XPJnlbkkuSvCXJeUkenOSPkxyV5JhZBrsXWVe79/4jyd+tMP8z\nA8a1tzomyZ8l+WqSDyT5UpLrJ3loktckeVBVHdNaa6MV9PVBrLvde/r65jw1ySeTvDfJuUkOTHK3\nJMcneUJV3a219uVRYX19EOtq855+PqCqunGSV6T7d/WgCWV+rS+zO8mbkuxJ8rAkJ1TV7VprT9+i\ncPca07R774NJTl5h/kdmENbeaOpzoH3ymN5aM23zKck/J2lJfn3Z/D/q579q3jHujVOSXUl2zTuO\nvW1Kcu8kt0xSSY7u+/CbJpQ9ON0fj5cmudPY/APSJR5bkofPe5u2w7TOdt/ZLz9h3nFv1ynJfdL9\ngbHfsvmHpTvxb0l+fmy+vj6fdtfXh2n3AybMf37fvn86Nk9f3/o218+Hb/9K8r4kZyZ5cd++j1tW\nZme6k87dSXaOzb92kjP6de4+723ZTtOU7T76G+f4ece7Xaf1nAPtq8d0twttc1V18yT3T9fZX7ls\n8XOTXJzkkVV14BaHBhvSWvtAa+0LrT8Cr+FhSa6b5M2ttU+M1XFJuiszkuSJMwhzr7POdmeTWmvv\nb639Q2vt8mXzz0nyqv7j0WOL9PUBbKDdGUDfT1fy1v71lmPz9PUBrLPNGd6T0yV1H5Pub/GV/K8k\nV0vyJ621XaOZrbVvJnlB//FXZhjj3miadmdr7ZPHdLcLbX/36V/fs8IfjRdW1b+kS8LcLclJWx3c\nPuBqVfWIJDdJdzD/dJIPtda+N9+w9hmj/n/iCss+lOTbSe5RVVdrrV26dWHtM25YVf87yaHp/ifu\no621T885pr3Bd/vXy8bm6euzt1K7j+jrs/Hg/nW8LfX12VqpzUf08wFU1W2SvDDJy1prH6qq+0wo\nulpff/eyMqxhHe0+cov+dq2Dk5yT5MOttS/MOs69yLTnQPvkMV2SZfu7df96+oTlX0iXZLlVJFlm\n4bAkb1w276yqekxr7YPzCGgfM7H/t9Yuq6qzktw2yc2T/OdWBraPuF8/fV9VnZzk0a21L80lom2u\nqvZP8qj+4/gfJPr6DK3S7iP6+gCq6unpxkg4JMmdktwz3R/m4wNN6usDmrLNR/TzTeqPJW9Md/vh\ncWsUX62vf7WqLk5yo6q6Rmvt28NGundZZ7uP/HI/jdfztiSP768mYnXTngPtk8d0twttf4f0r+dP\nWD6af60tiGVf87ok9013kDkwye2S/L9099i+u6qOnF9o+wz9fz6+neT3ktwx3b3j105yr3QDiR6d\n5CS3KG7YC5P803mXkQAAG15JREFUcJJ3tdb+eWy+vj5bk9pdXx/W09PdyvyUdCf7Jya5f2vt62Nl\n9PVhTdPm+vlwnpPk9kmOba19Z42y0/b1QyYsZ8l62v3rSZ6R7u/2a6a7leVBST6V5OeT/ENNeMIf\n37eec6B98piuA+39qn81zsLAWmu/29/f/7XW2rdba59prf1KugGHr55uBH/mS/+fgdbaua2157TW\nPtla+1Y/fSjdVXP/luQWSVZ8VCWTVdWTkzwt3RPiHrne1ftXfX2dVmt3fX1YrbXDWmuV7g/zh6b7\nn8tPVdUd1lGNvr4O07S5fj6MqrpLuqso/rC19tEhquxf9fVVrLfdW2ufba29qP+7/aLW2jdaayem\nSyiele5pNw9etZJ93MDnQHtlP5dk2f7WynIfvKwcszcaPPHH5xrFvkH/XyCttcvSPQY30f/Xpaqe\nlORlST6X5N6ttfOWFdHXZ2CKdl+Rvr45/R/m70h3En9okjeMLdbXZ2CNNp+0jn4+pbHbVU5P8uwp\nV5u2r1+widD2ahts9xW11i5I8lf9R/19Y1Y6B9onj+mSLNvff/Wvt5qwfDR6/KQxWxjeuf2rS2tn\nb2L/7//hvVm6QSz/eyuD2seNLkHX/6dUVU9J8idJPpPuRP+cFYrp6wObst1Xo69vUmvti+kSXLet\nquv0s/X1GZrQ5qvRz6dzULo+e5skl1RVG03pbtdKkj/v5720/7xaX79Bujb/ivFYVrWRdl+N/r45\nK50D7ZPHdAPfbn8f6F/vX1X7jT9hqKqume6St+8k+dd5BLePunv/ulcdLBbU+9MNWvbAJH+9bNmP\nJ7lGupHO95rRyreBu/Wv+v8Uquq3040H8u9J7tda+8aEovr6gNbR7qvR14dxw/519EQKfX32lrf5\navTz6Vya5LUTlt0h3XghH0l3wjm6peX96f5Of+DYvJEHjZVhso20+2r0981Z6Rxo3zymt9ZM23xK\n8s/p7mP79WXz/6if/6p5x7i3TelGwf6BFebfNN0TnVqS4+Yd53af0t0f25K8acLyg9P9r8OlSe40\nNv+AJKf06z583tux3aYp2v2uSXasMP8+SS7p173HvLdj0ad0lza3JJ9Y6XiyrKy+Pp9219c3395H\nJDlshfn7JXl+34b/MjZfX9/6NtfPZ7s/ju/b8HHL5t+sb9/dSXaOzb92kjP6de4+7/i367RKux+V\nZL8Vyj8iyeX9sWfnVsS4Haf1ngPtq8d0V7LsHX41XSd9eVXdN93jr+6a5N7pbhP6nTnGtrc6Jskz\nquoD6QbJujDJ4Ul+Kt1B411JXjK/8LavqnpIkof0Hw/rX+9eVSf077/RWnt60t0/W1WPT/K3SU6u\nqjcnOS/Jz6R7ZNzfJnnLVsW+na2n3ZO8KN2l5icn+Uo/70fS/UGeJM9urZ0y24i3t6p6dJLnpfuf\n5A8neXJVLS+2q7V2QqKvD2W97R59fQgPTPLiqvpQkjPTnVBeP93Ta26e5Jwkjx8V1tcHsa42j34+\nF621s6rqN5O8PMknquotSfYkeViSG2W4AXS5or9Msl9VnZKuvx+Q5M5J7pLutpX/3VrbNb/wFt66\nzoH22WP6vLM8pmGmJDdO9zitr6Y7QH8x3WB+q/4vnWnD7X2vdJe8fT7Jt5J8N12W9r1JHpWk5h3j\ndp2y9D8Pk6ZdK6xzVLqD+jfT3R53WpKnJrnKvLdnu0zrafckj03yj0l2Jbko3f9OfCndP5I/Nu9t\n2Q7TFO3dkpy8wnr6+ha2u74+SJv/cJJXprs16xvpTmLOT/Lxfn+s+HeKvr51ba6fz3x/jI47j5uw\n/MFJPpjuZPXifj89et5xb/dpUrsn+e10f69/uT+2XJIuGfm6JEfOO+5Fn7LBc6B97Zhe/UYDAAAA\nsAmeLgQAAAAwAEkWAAAAgAFIsgAAAAAMQJIFAAAAYACSLAAAAAADkGQBAAAAGIAkCwAAAMAAJFkA\nAAAABiDJAgAAADAASRYAAACAAUiyAAAAAAxAkgWAvV5V7aqqVlW75h0LwDSqamd/3GpVdcK84wFg\nOpIsAMxNVb127CTi8qq62bxjWo+qOqiqHlVVr6uqz1TVOVW1p6ourKovVdX7quoPqupeVeXf3BlY\ndiLaquqSqrrJFOs9Y2ydY7cg1L3e+H6YdyyLqqqeUlXHV9VT5h0LALPhDz4A5qKqDkxyzPisJMfO\nJ5r1qaqrVtWzknwxyevTxX3bJNdPctUkByW5cZL7JvnNJCcn+VJV/XpVXW0eMe9DrpbkufMOAiZ4\nSrr+KckCsJfaf94BALDPeliSay6b9+iqOr61trD/E15V10vyt0l+bGz2F5KclOS0JOel+/f1OkmO\nTHLvJDdN8oNJXp7ky0n+bgtD3hc9uqpe3Fr7/LwDAQD2LZIsAMzLY/rX7yZ5a5JfTpeMuE+6hMXC\nqaoDkrwryR37WV9J8muttXeusd590l3R8sDZRrjP+3aSayS5SpLfyxWvlAIAmDm3CwGw5arq5kl+\nvP94YpI/Glv8mCuvsTD+MEsJltOT3GWtBEuStNbe31p7UJKHJ9k9w/j2dR9J8sn+/c9X1R1XKwwA\nMDRJFgDm4dh0Y7AkyRtaa59M8tn+80Or6pBpK6qq61TV/62qz1XVxVV1XlV9vKqeXlXXGCrgqrpp\nkif0H7+X5OGtta+up47W2ltaax+eUP8VnoBUVQdU1ZOr6iNV9bV+YOCTJ6x716p6dVX9Vz/o7sVV\ndWZVvb6/ima17Tp6bMDS4zdbdmz5yf3n61bV86rqtKo6v6ouqKpT+4Fnr77a921AS3LcKJQkL9hM\nZdX5sap6flW9v6rOrqpL+/Y9q6reXFUPrqpao57jx9rl6H7efavqbVX15X6w3jP7fXjTZeseUFX/\nu6pOqaqvV9W3+7Z8xrTj+/QDND+lqt47tg2j38nzquq6G26kGamqm/Tt/rF+u/dUN7D0e6vqiVW1\nY431l/fDa/THhE9U1Tf7ffjZ/thx7Sljun1VnVBVX+z32Ver6p+r6hf65ROfBjT6fae7Wi9JblpX\nHLB5qkGYqzveHd/3gQv76ZNV9cya4nhXVT9bVW+tqv/u+9IlVfU/VfUfVfXGqvrladsDgAlaayaT\nyWQybdmU7uT3i+lOiL+Z5Gr9/N/u57UkT5iyrrsn+frYesunz6Q7qdnVf961ibj/YKzet8+gXb4f\nY5Kb9bEv356Tl62zf5JXr7L9o+mtSa4+4XuPHit3/Boxrll2PNYkt093S9WkuL6Q5GabbLedY/Wd\n2M87eWze0RPWe8ZYmWMnlHndFG3bkrw7ycGrxHj8eDxJXrhKXecluX2/3mFJPr5K2Q9N2q9j3/2g\nJF9bI/4LkvzMAH34+3Vusp5nJrlkjZhPT3KrKWI5OcnN0yVxJ9W1K8nONWJ6WpLLVqnjb5Lccuzz\nCRN+32tNx46tM963T0hyp6z+e/pUkh+YEP/Vk/zjlDE8ZbN9wWQymfblyZgsAGy1+yYZPWL3b1pr\n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\nA6rqBquNy9Jae9/yeWMncmv5ztpFcs2x9xdPLLXkognrztK3pygzHvvypNRQfi/Jo9OdLD+7qk5o\nra0ZW1X9QLoTz+v1s76cLuH3+XS3boweMZwkv5/u6pFpbsOe9iR2Mye7h6xdZKIdm1h3M2YV82ba\n8cCx9+vtz0PbVPKjtfaJqvrRdIMwH5PuFr0D0z2p6T7prvraVVXPbq3N8ulpAHs9Y7IAMHNVtX+W\nrk5JkldOGrMk3e0yo1sBrpqlsSXGjZIG+1fVNCeFB65dZLJ+oNZPj82622bqG8CFY++n2bbxBMaF\nE0utbT1/N1xj7SJXiP2iiaU2oU+Gjca4OSzT387xa1lKsLw+yc1ba7/aWnt5a+2vW2vvaK39XWvt\n7zJdYmwrjbfl8a21Wse0cwFiPnqdMR87o5jGkybr7c8Lp7V2Vmvt0eluJbtPukd/n5il/rszyRur\n6rj5RAiwd5BkAWArPCjdCe5GPGaFeWePvT98tZWr6tBs7iqWkfeOvV8p8bOVxq+imeZWlvEyZy9b\nNn6bwVoJq+tM8V0jh9fal+/cYuz98riG9KIsDVj8W1M+yegn+tfLkjxl+dOrlrnpZoKbgfHxOW47\ntyjWZxFjPj9LV7DcfIry05SZu9baJa21D7TWnt9ae1C6ZOJvZ+nKrOf0x00ANsDtQgBshfFEyeuT\n7JpinV9Klxw4sqpu31r71Niyj6VL3CTd/8j+5yr1rPbkkfV4ZZKnpLu65mer6sjW2n8MVPd6jQ9c\ne78pyo8/MWb5oLfjT0taa1yUaZ6eM3JIukFDVxv89t5j71d8xPAQWmvfrKoXJ3l+uvE7fitLj4+e\n5Pr96+7VHuNcVbdP94jcRfKpdNt3cJL7V9WB/eOmF9kHx97/XNYxds6stNYur6pPJrlnksOq6og1\nBr89eopqR7f9TH3/4Kz1V+r9QVXdOcnDklwt3UDUJ841MIBtypUsAMxUVV0nyU/3Hy9M8sTW2vFr\nTbni42uXX80yPojsr1XVVSd8dyV56hDb0Vr7YpI/7z/un+TNVXX9VVaZmdbariSf7D8eWVUTH7tb\nVXdKl4hKki/mykmPM5KMxrU5etLVJ/3VH49aZ6j/Z5W4rpulW8i+k9mf0L0sS0/k+Y2sfWXV6AqG\n61XVauPYPGezgQ2tfwrNX/YfD0myHW7/+ESSz/bvf6KqpkkeboV3jr3/jUmF+jF8HjFp+ZjRbVGL\neGvRrrH3/iMWYIMkWQCYtUeku/ojSd7WWpt2/Iq/TnerRpL80vjYK/0VJKPBZY9I8qdVdZXxlftk\nwYsy7PgpT8tSkuKIJB+rqgevtVJVHTVgDCMvGnt/QlUdscL33iTdY5FH/96/ePljYPtHuL6//3jT\ndGORLK/nwHT7Yz23CyXdfvuVVeobDXb62tWuFhlCfyXH7/cfr5HkcWusMrqypsbW+77qPC/dU1oW\n0QuydJXSM6vq6f0jiVdUVdetqmdV1Y9sTXhX1D9J6Zljs95SVQ9YbZ2quk1V/dlsI8sJWWrHJ1TV\nL60Qx9XTJbV+YPmyFZzVvx7a/z5nrqpuX1XPXi0p3CfDj+k/tlxxDCoA1kGWGoBZm/apQlfQWju3\nqt6T5CeTHJrkZ5L87ViRJ6ZLeByc7oT5LlX1hnRPgTks3e1Gd013e8yNMsAjgltrl1TVT6Z7rO89\nk9wkyd9X1RfSJX0+k+S8dLcEXCvJrdON7TF+4vo/GUBr7a1V9ZAkv5juscyfrKoTknw03eDBd0ry\n2Cw9Vvg9mXwLxkuy9Jjfl1XV3ZL8c7orXG6b5Nh0bfjmJA+fMsR/T9cGf9bH+Y50J6u36uMajWNy\nVpLfmbLOzXp1ukTZzqx9JcGfJvlfSa6S5Mn9k1nenu5JUzdO179un+Rz6a7EueNsQt6Y1tpXqurh\nSf4+3Vg7L06XJHhbutvrvp2ub9wyXSLyx9Jt68lDfH9VXSkxNcEnW2tv72P+hz5x9Zx0t3WdWFUf\nTvLudFdhXZYukXHbJPdKcrt0ff2JQ8S8ktbaN6rqqUlely5Z+ZdV9QtJ/indmC23THeMu3mSv8lS\nomLS04BOSncsS5K390mir46VP621NsgxYswhSZ6X5LlV9S9JTkn3WPcL07Xn7dL151GS6C9ba18a\nOAaAfUdrzWQymUymmUzpxuRo/fSVJPutc/2Hj63/Tyssv0e6x+m2CdNn0iVCdvWfdw20XVdN8ux0\nCZVJ37182pXk15PsP6HOdceY7j9L/nyK7/6bJFdfo67fXWX9y9M9+vXosXnHT6hntPzkJD/a7/dJ\n9Z6R7qk9m9kXO8fqO3GK8o9aIY5jJ5R9YrqT+Enxfy7d4L0nj+ZNqOf4sXWOXiO+qfrBNPuiL3e3\nJGdO2UcvTHK7TeyLaX8L49MJK9TzuHQJjKl+V2v1w3XEPbFskqenS/Ks9hu7zdjnl02o56Ak/7VK\nPceOlR3v21dqp1V+BycsW/bj69gfb8kaxwqTyWQyrT65XQiAWRq/iuWvWmuT/nd3kndmaYDSB1TV\nFa5Gaa2dku7E5oVJPp/uioJvpbvC5beS3KXN4H9kW2vfba39XrqrMY5N8oZ0J9xfT3cidmGSL6W7\nDeeF6QZ4vVlr7RVt9afUrDeOy1prj09y9ySvTZe0uDhdO5yV5E1J7ttaO6atcZtWa+25SR6Q5B/7\n7diTLkHyliQ/3rpxctYb37+nu9rj99MlvC5MNybFp9KNE/IjrbX/Xm+9m/SmLI39sarW2p8lOSrd\nCfQ5Sb6b5Nx0VwL8nyR3aq2dMaM4B9Fa+9d0V1Q9Islb0/WLi9L10/PSjYXy50l+IclhrbXT5hTq\n97XWXpPut/W0dFdgnZ3uKViXptsPH0p3Zc59s0VP9GmtvSTJ/9/eHaNIEQVhAP4rMtnAQEw8gmBo\nJGioyQaCrDcQA4/hBTyAV9jAEyiYmbjBYiwLoiAiGAiCZfBaRLF3Ax8zw/p9ydBN0xTNm2H4eV11\nPWM33knG9+N9xtSx+919L792jSXj2f7tPl8ygq/HGX2VPmd918sU3f0iYw08zFgDbzLWwPfl8zjJ\n0yQ3u/vgrN8KAE5X3b3tGgCAc6Kqfv6xeN7dt7ZZC2xSVT1K8mQ5vNvdh6ddD8D5ZCcLAAD8g2XC\n2YPl8FuSl1ssB4AtErIAAMCKqtqrqtXGxlV1IeN1m6vLqcPu/rCR4gDYOaYLAQDAuotJXlXVUUYP\nluOMXip7GZPDDpJcWa79lNFLBoD/lJAFAADOdi2/j2P/09sk+919sqF6ANhBQhYAAFj3LsmdJLeT\n3EhyOcmlJJXkY5LXSZ5ljE7+uq0iAdgNpgsBAAAATKDxLQAAAMAEQhYAAACACYQsAAAAABMIWQAA\nAAAmELIAAAAATCBkAQAAAJhAyAIAAAAwgZAFAAAAYAIhCwAAAMAEQhYAAACACYQsAAAAABMIWQAA\nAAAmELIAAAAATPADHs+4tT09xZIAAAAASUVORK5CYII=\n", "text/plain": [ "<matplotlib.figure.Figure at 0x1125a77b8>" ] }, "metadata": { "image/png": { "height": 394, "width": 556 } }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "adgroup_lengths = pd.Series([len(adgrp) for adgrp in full_keywords_df['Ad Group'].unique()])\n", "long_adgroups = sum(adgroup_lengths > 30)\n", "plt.figure(figsize=(9,6))\n", "plt.hist(adgroup_lengths, rwidth=0.9, bins=50)\n", "plt.vlines(x=30, ymin=0, ymax=10, colors='red')\n", "plt.title(str(long_adgroups) + ' ad group name lenghts > 30 (don\\'t fit in a headline)', fontsize=17)\n", "plt.xlabel('Ad Group Name Lengths', fontsize=15)\n", "plt.ylabel('Count', fontsize=15)\n", "plt.yticks(range(11))\n", "plt.xticks(range(0, 51, 5))\n", "plt.grid(alpha=0.5)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It seems our problem is not a trivial one, and there is no straightforward way of dealing with it. Ideally, we would like to have the two headlines contain the full title of the product (course) that we are promoting. \n", "I thought of different ways around it, and decided to write a simple algorithm that splits the name into two phrases, each containing at most thirty characters. \n", "\n", "The algorithm is not suitable for general use, and needs some tweaks to make it general, but it works well enough for this data set. " ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "def split_string(string, splits=2, max_len=60):\n", " \"\"\"Split `string` into `splits` words, each shorter than `max_len` / `splits`\"\"\"\n", " if len(string) < max_len / splits:\n", " return string, ''\n", " str_words = string.split(' ')\n", " result = ''\n", " for i, word in enumerate(str_words):\n", " if len(result + ' ' + word) <= max_len / splits:\n", " result += word + ' '\n", " else:\n", " break\n", " spaces = result.strip().count(' ')\n", " result2 = string[string[len(result):].index(word) + len(result):]\n", " return result.strip(), result2" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('this is a very long course', 'name that needs splitting')\n", "('short course name', '')\n" ] } ], "source": [ "print(split_string('this is a very long course name that needs splitting', 2, 60))\n", "print(split_string('short course name', 2, 60))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is not something that I do usually, but I like this technique, and I think I'll be using it when creating other campaigns. That's a nice side effect of writing a tutorial! \n", "Now let's think about the templates that we want to write. \n", "The general ad template that I use consists of the following elements (typically in this order). \n", "* **Product**: If I'm searching for 'data science course' on Google, I need to see 'data science course' in the links / ads somewhere.\n", "* **Benefits**: This is the emotinal / psychological thing that people are really after, beyond just completing the course, 'boost your career', 'stand out from the crowd'. \n", "* **Features**: Now that you've promised me the moon, show me how you are going to get me there! 'over 100 data science courses', 'learn from top experts', 'get instant feedback on your coding skills'. \n", "* **Call to action**: Ok, so you told me what you have, you motivated me to buy it, and you showed me how I'm going to get there. I'm sold. What do I do now? 'sign up for a free trial', 'sample first chapters for free', 'save 20% on annual subscriptions'\n", "\n", "It's not easy to put all these in one ad which is a tweet long, and some of them may overlap. But we will try our best." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Ad Templates: \n", "\n", "* Headline 1: This will always contain the name of the course (or the first half). \n", "* Headline 2: The second half of the course name, or one of the following: \n", " * Boost Your Data Science Career\n", " * Stand Out From the Crowd\n", " * Tackle Complex Questions\n", "* Description: each ad group will have the three variations below and they will rotate.\n", " * Learn Directly From the Top Experts in the Field. 20% off Annual Subscriptions\n", " * Get Ahead of the Curve, Master Data Science Skills. $29 / Month. Cancel Anytime\n", " * Choose From a Wide Variety of Topics Tuaght by the Best in the World. Start Now " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Crate `Campaign` Columns" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array(['SEM_Instructors', 'SEM_Technologies', 'SEM_Courses', 'SEM_Tracks'], dtype=object)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "full_keywords_df.Campaign.unique() # just to make sure we have consistent naming conventions" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>old_name</th>\n", " <th>url</th>\n", " <th>name</th>\n", " <th>Campaign</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>SEM_Courses</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " <td>Introduction to R</td>\n", " <td>SEM_Courses</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>SEM_Courses</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Intro to SQL for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-sql-...</td>\n", " <td>Intro to SQL for Data Science</td>\n", " <td>SEM_Courses</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Intermediate R</td>\n", " <td>https://www.datacamp.com/courses/intermediate-r</td>\n", " <td>Intermediate R</td>\n", " <td>SEM_Courses</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " old_name url name Campaign\n", "0 Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Intro to Python for Data Science SEM_Courses\n", "1 Introduction to R https://www.datacamp.com/courses/free-introduc... Introduction to R SEM_Courses\n", "2 Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-... Intermediate Python for Data Science SEM_Courses\n", "3 Intro to SQL for Data Science https://www.datacamp.com/courses/intro-to-sql-... Intro to SQL for Data Science SEM_Courses\n", "4 Intermediate R https://www.datacamp.com/courses/intermediate-r Intermediate R SEM_Courses" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "course_df['Campaign'] = 'SEM_Courses'\n", "course_df = course_df.rename(columns={'name_clean': 'name', 'name': 'old_name'})\n", "course_df.head()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>name</th>\n", " <th>url</th>\n", " <th>Campaign</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>Filip Schouwenaars</td>\n", " <td>https://www.datacamp.com/instructors/filipsch</td>\n", " <td>SEM_Instructors</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Jonathan Cornelissen</td>\n", " <td>https://www.datacamp.com/instructors/jonathana...</td>\n", " <td>SEM_Instructors</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Hugo Bowne-Anderson</td>\n", " <td>https://www.datacamp.com/instructors/hugobowne</td>\n", " <td>SEM_Instructors</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Nick Carchedi</td>\n", " <td>https://www.datacamp.com/instructors/nickyc</td>\n", " <td>SEM_Instructors</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Greg Wilson</td>\n", " <td>https://www.datacamp.com/instructors/greg48f64...</td>\n", " <td>SEM_Instructors</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " name url Campaign\n", "0 Filip Schouwenaars https://www.datacamp.com/instructors/filipsch SEM_Instructors\n", "1 Jonathan Cornelissen https://www.datacamp.com/instructors/jonathana... SEM_Instructors\n", "2 Hugo Bowne-Anderson https://www.datacamp.com/instructors/hugobowne SEM_Instructors\n", "3 Nick Carchedi https://www.datacamp.com/instructors/nickyc SEM_Instructors\n", "4 Greg Wilson https://www.datacamp.com/instructors/greg48f64... SEM_Instructors" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "instructor_df['Campaign'] = 'SEM_Instructors'\n", "instructor_df.head()" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>name</th>\n", " <th>url</th>\n", " <th>Campaign</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>R Programming</td>\n", " <td>https://www.datacamp.com/tracks/r-programming</td>\n", " <td>SEM_Tracks</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Importing Cleaning Data With R</td>\n", " <td>https://www.datacamp.com/tracks/importing-clea...</td>\n", " <td>SEM_Tracks</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Data Manipulation With R</td>\n", " <td>https://www.datacamp.com/tracks/data-manipulat...</td>\n", " <td>SEM_Tracks</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Python Programming</td>\n", " <td>https://www.datacamp.com/tracks/python-program...</td>\n", " <td>SEM_Tracks</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Importing Cleaning Data With Python</td>\n", " <td>https://www.datacamp.com/tracks/importing-clea...</td>\n", " <td>SEM_Tracks</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " name url Campaign\n", "0 R Programming https://www.datacamp.com/tracks/r-programming SEM_Tracks\n", "1 Importing Cleaning Data With R https://www.datacamp.com/tracks/importing-clea... SEM_Tracks\n", "2 Data Manipulation With R https://www.datacamp.com/tracks/data-manipulat... SEM_Tracks\n", "3 Python Programming https://www.datacamp.com/tracks/python-program... SEM_Tracks\n", "4 Importing Cleaning Data With Python https://www.datacamp.com/tracks/importing-clea... SEM_Tracks" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tracks_df['Campaign'] = 'SEM_Tracks'\n", "tracks_df.head()" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>name</th>\n", " <th>url</th>\n", " <th>Campaign</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>R</td>\n", " <td>https://www.datacamp.com/courses/tech:R</td>\n", " <td>SEM_Technologies</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Python</td>\n", " <td>https://www.datacamp.com/courses/tech:Python</td>\n", " <td>SEM_Technologies</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SQL</td>\n", " <td>https://www.datacamp.com/courses/tech:SQL</td>\n", " <td>SEM_Technologies</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Git</td>\n", " <td>https://www.datacamp.com/courses/tech:Git</td>\n", " <td>SEM_Technologies</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Shell</td>\n", " <td>https://www.datacamp.com/courses/tech:Shell</td>\n", " <td>SEM_Technologies</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " name url Campaign\n", "0 R https://www.datacamp.com/courses/tech:R SEM_Technologies\n", "1 Python https://www.datacamp.com/courses/tech:Python SEM_Technologies\n", "2 SQL https://www.datacamp.com/courses/tech:SQL SEM_Technologies\n", "3 Git https://www.datacamp.com/courses/tech:Git SEM_Technologies\n", "4 Shell https://www.datacamp.com/courses/tech:Shell SEM_Technologies" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tech_domain = 'https://www.datacamp.com/courses/tech:'\n", "tech_domain_list = []\n", "for tech in ['R', 'Python', 'SQL', 'Git', 'Shell']:\n", " tech_domain_list.append((tech, tech_domain + tech))\n", "tech_df = pd.DataFrame.from_records(tech_domain_list, columns=['name', 'url'])\n", "tech_df['Campaign'] = 'SEM_Technologies'\n", "tech_df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. Merge All (name, url) Data Frames" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total rows: 185\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Final URL</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to SQL for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-sql-...</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate R</td>\n", " <td>https://www.datacamp.com/courses/intermediate-r</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Final URL\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth...\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc...\n", "2 SEM_Courses Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-...\n", "3 SEM_Courses Intro to SQL for Data Science https://www.datacamp.com/courses/intro-to-sql-...\n", "4 SEM_Courses Intermediate R https://www.datacamp.com/courses/intermediate-r" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "full_ads_df = pd.concat([course_df[['Campaign', 'name', 'url']],\n", " instructor_df,\n", " tracks_df,\n", " tech_df], ignore_index=True)\n", "full_ads_df = full_ads_df.rename(columns={'name': 'Ad Group', 'url': 'Final URL'})\n", "print('total rows:', full_ads_df.shape[0])\n", "n_adgroups = full_ads_df.shape[0]\n", "full_ads_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. Generate Ads (insert templates)\n", "\n", "Just to keep track of where we are. We now have the ads data frame containg `Campaign`, `Ad Group`, and `Final URL` columns properly mapped. We need to add the `Headline 1`, `Headline 2`, and `Description` Fields. Keep in mind that for each ad group we will be adding three different ad variation. This is a good practice mainly for testing purposes, to see what people click on, which ads convert more, etc. We should end up with a data frame that has three times number of rows of the current one. \n", "\n", "Let's start by duplicating each row three times, in the `full_ads_df`:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total rows: 555\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Final URL</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " </tr>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " </tr>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Final URL\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth...\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth...\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth...\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc...\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc...\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc...\n", "2 SEM_Courses Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-...\n", "2 SEM_Courses Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-...\n", "2 SEM_Courses Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-..." ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "full_ads_df = full_ads_df.iloc[[x for x in range(n_adgroups) for i in range(3)], :] \n", "print('total rows:', full_ads_df.shape[0])\n", "full_ads_df.head(9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now add the three different descriptions we created above. " ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Final URL</th>\n", " <th>Description</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Learn Directly From the Top Experts in the Fie...</td>\n", " </tr>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Be Ahead of the Curve, Master Data Science Ski...</td>\n", " </tr>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Choose From a Wide Variety of Topics Tuaght by...</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " <td>Learn Directly From the Top Experts in the Fie...</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " <td>Be Ahead of the Curve, Master Data Science Ski...</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Final URL Description\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Learn Directly From the Top Experts in the Fie...\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Be Ahead of the Curve, Master Data Science Ski...\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Choose From a Wide Variety of Topics Tuaght by...\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc... Learn Directly From the Top Experts in the Fie...\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc... Be Ahead of the Curve, Master Data Science Ski..." ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Description = [\n", " 'Learn Directly From the Top Experts in the Field. 20% off Annual Subcriptions',\n", " 'Be Ahead of the Curve, Master Data Science Skills. $29 / Month. Cancel Anytime',\n", " 'Choose From a Wide Variety of Topics Tuaght by the Best in the World. Start Now' \n", "]\n", "Description = [x for i in range(n_adgroups) for x in Description ]\n", "full_ads_df['Description'] = Description\n", "full_ads_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we add `Headline 1` and `Headline 2` to the data frame. We are almost done!" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "benefits = [\n", " 'Boost Your Data Science Career',\n", " 'Stand Out From the Crowd',\n", " 'Tackle Complex Questions' \n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The benefits will be included as `Headline 2` in the cases where the length of the ad group is 30 or less. In this case we would have an empty field, which we will fill with one of the benefits that we have. " ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total ads: 555\n" ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Campaign</th>\n", " <th>Ad Group</th>\n", " <th>Final URL</th>\n", " <th>Description</th>\n", " <th>Headline 1</th>\n", " <th>Headline 2</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Learn Directly From the Top Experts in the Fie...</td>\n", " <td>Intro to Python for Data</td>\n", " <td>Science</td>\n", " </tr>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Be Ahead of the Curve, Master Data Science Ski...</td>\n", " <td>Intro to Python for Data</td>\n", " <td>Science</td>\n", " </tr>\n", " <tr>\n", " <th>0</th>\n", " <td>SEM_Courses</td>\n", " <td>Intro to Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intro-to-pyth...</td>\n", " <td>Choose From a Wide Variety of Topics Tuaght by...</td>\n", " <td>Intro to Python for Data</td>\n", " <td>Science</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " <td>Learn Directly From the Top Experts in the Fie...</td>\n", " <td>Introduction to R</td>\n", " <td>Boost Your Data Science Career</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " <td>Be Ahead of the Curve, Master Data Science Ski...</td>\n", " <td>Introduction to R</td>\n", " <td>Stand Out From the Crowd</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>SEM_Courses</td>\n", " <td>Introduction to R</td>\n", " <td>https://www.datacamp.com/courses/free-introduc...</td>\n", " <td>Choose From a Wide Variety of Topics Tuaght by...</td>\n", " <td>Introduction to R</td>\n", " <td>Tackle Complex Questions</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " <td>Learn Directly From the Top Experts in the Fie...</td>\n", " <td>Intermediate Python for Data</td>\n", " <td>Science</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " <td>Be Ahead of the Curve, Master Data Science Ski...</td>\n", " <td>Intermediate Python for Data</td>\n", " <td>Science</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>SEM_Courses</td>\n", " <td>Intermediate Python for Data Science</td>\n", " <td>https://www.datacamp.com/courses/intermediate-...</td>\n", " <td>Choose From a Wide Variety of Topics Tuaght by...</td>\n", " <td>Intermediate Python for Data</td>\n", " <td>Science</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Campaign Ad Group Final URL Description Headline 1 Headline 2\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Learn Directly From the Top Experts in the Fie... Intro to Python for Data Science\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Be Ahead of the Curve, Master Data Science Ski... Intro to Python for Data Science\n", "0 SEM_Courses Intro to Python for Data Science https://www.datacamp.com/courses/intro-to-pyth... Choose From a Wide Variety of Topics Tuaght by... Intro to Python for Data Science\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc... Learn Directly From the Top Experts in the Fie... Introduction to R Boost Your Data Science Career\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc... Be Ahead of the Curve, Master Data Science Ski... Introduction to R Stand Out From the Crowd\n", "1 SEM_Courses Introduction to R https://www.datacamp.com/courses/free-introduc... Choose From a Wide Variety of Topics Tuaght by... Introduction to R Tackle Complex Questions\n", "2 SEM_Courses Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-... Learn Directly From the Top Experts in the Fie... Intermediate Python for Data Science\n", "2 SEM_Courses Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-... Be Ahead of the Curve, Master Data Science Ski... Intermediate Python for Data Science\n", "2 SEM_Courses Intermediate Python for Data Science https://www.datacamp.com/courses/intermediate-... Choose From a Wide Variety of Topics Tuaght by... Intermediate Python for Data Science" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "benefits = [x for i in range(n_adgroups) for x in benefits]\n", "headlines = [split_string(x) for x in full_ads_df['Ad Group']]\n", "full_ads_df['Headline 1'] = [x[0] for x in headlines]\n", "full_ads_df['Headline 2'] = [x[1] if x[1] else benefits[i] for i, x in enumerate(headlines)]\n", "print('total ads:', full_ads_df.shape[0])\n", "full_ads_df.head(9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see \"Intro to Python for Data Science\" was long enough to take two fields, so we keep it as is, to keep it clear. In the second case \"Introduction to R\" fits in the first headline, so we insert the three different benefits that we created in `Headline 2`. \n", "And we are done! \n", "Just a quick check, to make sure that all campaign and ad group names in both data frames are the same." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ads_check = (full_ads_df[['Campaign', 'Ad Group']]\n", " .drop_duplicates()\n", " .sort_values(['Campaign', 'Ad Group'])\n", " .reset_index(drop=True))\n", "keywords_check = (full_keywords_df[['Campaign', 'Ad Group']]\n", " .drop_duplicates()\n", " .sort_values(['Campaign', 'Ad Group'])\n", " .reset_index(drop=True))\n", "\n", "all(ads_check == keywords_check)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "full_ads_df.to_csv('ads.csv', index=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the two csv files we have now, we can upload them and start running our campaigns. We will need to set daily budgets, geo-targeting and a few other parameters, but mostly we are good to go. \n", "\n", "You can check them out here: \n", "\n", "[keywords](https://github.com/eliasdabbas/datacamp_sem/blob/master/keywords.csv) \n", "[ads](https://github.com/eliasdabbas/datacamp_sem/blob/master/ads.csv)\n", "\n", "I hope you found that useful. \n", "Thanks for reading. " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.3" } }, "nbformat": 4, "nbformat_minor": 2 }