{ "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", "![](adgroup_diagram.png)" ] }, { "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 `\" 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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
nameurl
0Filip Schouwenaarshttps://www.datacamp.com/instructors/filipsch
1Jonathan Cornelissenhttps://www.datacamp.com/instructors/jonathana...
2Hugo Bowne-Andersonhttps://www.datacamp.com/instructors/hugobowne
3Nick Carchedihttps://www.datacamp.com/instructors/nickyc
4Greg Wilsonhttps://www.datacamp.com/instructors/greg48f64...
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupKeywordCriterion Type
0SEM_InstructorsFilip Schouwenaarsfilip schouwenaars courseExact
1SEM_InstructorsFilip Schouwenaarsfilip schouwenaars coursePhrase
2SEM_InstructorsFilip Schouwenaars+filip +schouwenaars +courseBroad
3SEM_InstructorsFilip Schouwenaarsfilip schouwenaars coursesExact
4SEM_InstructorsFilip Schouwenaarsfilip schouwenaars coursesPhrase
\n", "
" ], "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 \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, # 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 \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, # 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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupKeywordCriterion Type
0SEM_CampaignData Sciencedata science courseExact
1SEM_CampaignData Sciencedata science coursePhrase
2SEM_CampaignData Science+data +science +courseBroad
3SEM_CampaignData Sciencedata science tutorialExact
4SEM_CampaignData Sciencedata science tutorialPhrase
5SEM_CampaignData Science+data +science +tutorialBroad
6SEM_CampaignMachine Learningmachine learning courseExact
7SEM_CampaignMachine Learningmachine learning coursePhrase
8SEM_CampaignMachine Learning+machine +learning +courseBroad
9SEM_CampaignMachine Learningmachine learning tutorialExact
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupKeywordCriterion Type
0SEM_TechnologiesRr data scienceExact
1SEM_TechnologiesRr data sciencePhrase
2SEM_TechnologiesR+r +data +scienceBroad
3SEM_TechnologiesRr programmingExact
4SEM_TechnologiesRr programmingPhrase
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
nameurlname_clean
0Intro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Intro to Python for Data Science
1Introduction to Rhttps://www.datacamp.com/courses/free-introduc...Introduction to R
2Intermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...Intermediate Python for Data Science
3Intro to SQL for Data Sciencehttps://www.datacamp.com/courses/intro-to-sql-...Intro to SQL for Data Science
4Intermediate Rhttps://www.datacamp.com/courses/intermediate-rIntermediate R
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupKeywordCriterion Type
0SEM_CoursesIntro to Python for Data Scienceintro to python for data scienceExact
1SEM_CoursesIntro to Python for Data Scienceintro to python for data sciencePhrase
2SEM_CoursesIntro to Python for Data Science+intro +to +python +for +data +science +Broad
3SEM_CoursesIntro to Python for Data Scienceintro to python for data science learnExact
4SEM_CoursesIntro to Python for Data Scienceintro to python for data science learnPhrase
5SEM_CoursesIntro to Python for Data Science+intro +to +python +for +data +science +learnBroad
6SEM_CoursesIntro to Python for Data Scienceintro to python for data science courseExact
7SEM_CoursesIntro to Python for Data Scienceintro to python for data science coursePhrase
8SEM_CoursesIntro to Python for Data Science+intro +to +python +for +data +science +courseBroad
9SEM_CoursesIntro to Python for Data Scienceintro to python for data science coursesExact
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
nameurl
0R Programminghttps://www.datacamp.com/tracks/r-programming
1Importing Cleaning Data With Rhttps://www.datacamp.com/tracks/importing-clea...
2Data Manipulation With Rhttps://www.datacamp.com/tracks/data-manipulat...
3Python Programminghttps://www.datacamp.com/tracks/python-program...
4Importing Cleaning Data With Pythonhttps://www.datacamp.com/tracks/importing-clea...
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupKeywordCriterion Type
0SEM_TracksR Programmingr programmingExact
1SEM_TracksR Programmingr programmingPhrase
2SEM_TracksR Programming+r +programming +Broad
3SEM_TracksR Programmingr programming learnExact
4SEM_TracksR Programmingr programming learnPhrase
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupKeywordCriterion Type
0SEM_InstructorsFilip Schouwenaarsfilip schouwenaars courseExact
1SEM_InstructorsFilip Schouwenaarsfilip schouwenaars coursePhrase
2SEM_InstructorsFilip Schouwenaars+filip +schouwenaars +courseBroad
3SEM_InstructorsFilip Schouwenaarsfilip schouwenaars coursesExact
4SEM_InstructorsFilip Schouwenaarsfilip schouwenaars coursesPhrase
\n", "
" ], "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", "![](text_ad_diagram.png)" ] }, { "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": [ "" ] }, "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
old_nameurlnameCampaign
0Intro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Intro to Python for Data ScienceSEM_Courses
1Introduction to Rhttps://www.datacamp.com/courses/free-introduc...Introduction to RSEM_Courses
2Intermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...Intermediate Python for Data ScienceSEM_Courses
3Intro to SQL for Data Sciencehttps://www.datacamp.com/courses/intro-to-sql-...Intro to SQL for Data ScienceSEM_Courses
4Intermediate Rhttps://www.datacamp.com/courses/intermediate-rIntermediate RSEM_Courses
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
nameurlCampaign
0Filip Schouwenaarshttps://www.datacamp.com/instructors/filipschSEM_Instructors
1Jonathan Cornelissenhttps://www.datacamp.com/instructors/jonathana...SEM_Instructors
2Hugo Bowne-Andersonhttps://www.datacamp.com/instructors/hugobowneSEM_Instructors
3Nick Carchedihttps://www.datacamp.com/instructors/nickycSEM_Instructors
4Greg Wilsonhttps://www.datacamp.com/instructors/greg48f64...SEM_Instructors
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
nameurlCampaign
0R Programminghttps://www.datacamp.com/tracks/r-programmingSEM_Tracks
1Importing Cleaning Data With Rhttps://www.datacamp.com/tracks/importing-clea...SEM_Tracks
2Data Manipulation With Rhttps://www.datacamp.com/tracks/data-manipulat...SEM_Tracks
3Python Programminghttps://www.datacamp.com/tracks/python-program...SEM_Tracks
4Importing Cleaning Data With Pythonhttps://www.datacamp.com/tracks/importing-clea...SEM_Tracks
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
nameurlCampaign
0Rhttps://www.datacamp.com/courses/tech:RSEM_Technologies
1Pythonhttps://www.datacamp.com/courses/tech:PythonSEM_Technologies
2SQLhttps://www.datacamp.com/courses/tech:SQLSEM_Technologies
3Githttps://www.datacamp.com/courses/tech:GitSEM_Technologies
4Shellhttps://www.datacamp.com/courses/tech:ShellSEM_Technologies
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupFinal URL
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...
2SEM_CoursesIntermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...
3SEM_CoursesIntro to SQL for Data Sciencehttps://www.datacamp.com/courses/intro-to-sql-...
4SEM_CoursesIntermediate Rhttps://www.datacamp.com/courses/intermediate-r
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupFinal URL
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...
2SEM_CoursesIntermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...
2SEM_CoursesIntermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...
2SEM_CoursesIntermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupFinal URLDescription
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Learn Directly From the Top Experts in the Fie...
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Be Ahead of the Curve, Master Data Science Ski...
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Choose From a Wide Variety of Topics Tuaght by...
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...Learn Directly From the Top Experts in the Fie...
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...Be Ahead of the Curve, Master Data Science Ski...
\n", "
" ], "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": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CampaignAd GroupFinal URLDescriptionHeadline 1Headline 2
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Learn Directly From the Top Experts in the Fie...Intro to Python for DataScience
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Be Ahead of the Curve, Master Data Science Ski...Intro to Python for DataScience
0SEM_CoursesIntro to Python for Data Sciencehttps://www.datacamp.com/courses/intro-to-pyth...Choose From a Wide Variety of Topics Tuaght by...Intro to Python for DataScience
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...Learn Directly From the Top Experts in the Fie...Introduction to RBoost Your Data Science Career
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...Be Ahead of the Curve, Master Data Science Ski...Introduction to RStand Out From the Crowd
1SEM_CoursesIntroduction to Rhttps://www.datacamp.com/courses/free-introduc...Choose From a Wide Variety of Topics Tuaght by...Introduction to RTackle Complex Questions
2SEM_CoursesIntermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...Learn Directly From the Top Experts in the Fie...Intermediate Python for DataScience
2SEM_CoursesIntermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...Be Ahead of the Curve, Master Data Science Ski...Intermediate Python for DataScience
2SEM_CoursesIntermediate Python for Data Sciencehttps://www.datacamp.com/courses/intermediate-...Choose From a Wide Variety of Topics Tuaght by...Intermediate Python for DataScience
\n", "
" ], "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 }