here’s-how-to-modify-your-e-commerce-campaign-for-b2b-and-b2c

While search engine optimization is still one of the most important disciplines to master, pay-per-click advertising is equally essential as a skill.

No matter if a brand is looking to attract B2B or B2C prospects, PPC is one of the most effective means of achieving this goal. That said, there is a vast chasm that separates the tactics employed for optimizing each type of campaign.

Understanding these differences, as well as the necessary PPC audience targeting strategies, is what will enable sellers to reach the right consumers.

To help delineate the necessary knowledge around the differences in B2B and B2C advertisements, today, we will explore the disparities, similarities and relevant tactics for using PPC ads to connect with buyers on both ends of the spectrum.

Targeting tactics

When initiating an advertising campaign, one of the primary considerations is how to reach the right consumers. After all, if a brand is selling homeowner’s insurance, targeting those in the 18-24 age bracket is likely to produce paltry results.

Take a look at the targeting categories in Google Ads:

Speaking to B2B advertisers, a prime tactic for ensuring that the right individuals are reached is to use social media ads to target by company position. Instead of targeting users by their age or interests as a B2C campaign might, a better route would be to target users based on their job title or industry via LinkedIn or Facebook.

However, where some overlap exists is that utilizing features like Lookalike Audiences can help both B2B and B2C brands find new users who are potentially interested in what the company offers.

No matter if targeting the average consumer or business leaders, brands should create buyer personas to better understand who they are trying to reach.

Here is an example buyer persona from Buffer:

Consider the clock

Another of the main differences in B2B and B2C advertising is that B2C sellers are trying to gain purchases as quickly as possible. However, with B2B, advertisers are attempting to generate business leads and ensure their product is considered in the prolonged purchase cycle.

To achieve this goal, brands must consider the timing of their ads.

In B2B advertising, businesses are trying to reach the key players within a company, those who make decisions or are closely connected to those with such power. This means that running ads within the nine-to-five timeframe is critical as this is when these individuals are actively engaged and show the highest intent to click-through.

While B2C consumers can potentially be targeted around the clock, the same is not true for B2B prospects. Instead, ads intended to reach business prospects should only run during business hours, not only for the aforementioned reason but also because this will help to conserve the business’s PPC budget.

Given this framework, brands should employ ad scheduling and bid modifications to alter bids for certain days of the week (Monday through Friday) and times of the day. For example, if advertisers notice that they receive the highest amount of click-throughs on Tuesday mornings, it is wise to increase the cost-per-click during this window.

To do this in Google Ads simply go to Ad schedule and click Bid adjustment for whichever time frame you want to increase or decrease:

While some sellers might feel equipped to manage such tasks, most will see more benefit from partnering with an e-commerce PPC management firm that can maximize potential impressions, clicks and conversions.

Messaging modifications

Much like targeting and timing, there are substantial differences in how advertisers will speak to B2B and B2C audiences.

The fact is that B2B buyers want to engage with brands that have evident expertise and knowledge of a given industry. This means that advertisers must showcase their acumen through relevant terminology, awareness of processes and similar traits that prospects will be interested in seeing.

For instance, if a CRM software provider is looking to reel in new users, but utilizes fluffy, emotionally-driven copy to do so, there is a significant chance that they will not engage the folks they are truly after. Instead, it is necessary to build confidence in potential users with more formal, fact-based messaging that has clear implications of how a product can improve business performance.

Take a look at how Intel communicates with its audience:

However, the exact inverse is true for B2C ads. When targeting average consumers, brands are wise to employ the most relatable voice possible by utilizing straightforward language that mirrors the audience. There is little to no place for jargon in B2C advertising.

Contrary to Intel, Gerber Childrenwear’s audience of mainly parents would appreciate copy like this:

Moreover, B2C ads should trigger emotions in consumers. Neil Patel speaks to this point, writing: “An analysis of 1,400 successful ad campaign case studies found that campaigns with purely emotional content performed about twice as well (31 percent vs. 16 percent) as those with only rational content.”

This is a crucial dichotomy to recognize when producing B2B and B2C ads.

Negative keyword distinctions

In addition to targeting the audience on their proper characteristics, both B2C and B2B advertisers must understand what elements to exclude in order to reach the most relevant consumers.

The fact is that negative keywords are extremely helpful in weeding out irrelevant searches that eat up advertisers’ budgets. Naturally, the keywords and negative keywords that sellers employ are highly dependent on their specific industry and niche; however, there are some through lines that can be established for both B2B and B2C advertising efforts.

For instance, B2B brands offering a technological solution might want to exclude phrases that are commonly paired with the term “technology” such as:

  • Careers
  • Jobs
  • Hiring
  • Laws
  • Reviews
  • Free

Similarly, B2C retailers who sell new products can also immediately disqualify specific words and phrases that are not applicable to their efforts, such as:

  • Commercial
  • Bulk
  • Used

To do this in Google Ads go to Keywords and click Negative Keywords

However, to get to the core of which terms a business should add to their negative keyword lists, it is best to consult Google’s search term report to uncover phrases that drive impressions and clicks but are wholly irrelevant or fail to convert.

Despite all the differences between B2B and B2C advertising methodologies, there are some commonalities that the two marketing efforts share.

Shared traits

While B2B and B2C ads can be quite different, there are some core components to each that remain the same.

For instance, no matter which type of audience is the target, it is necessary for advertisers to conduct in-depth keyword research to understand which terms and phrases will reach their customers.

Similarly, when advertising through Google, relevance is a significant component of campaign success. Therefore, utilizing compelling landing pages that closely match the ad’s offer is necessary for both B2B and B2C spaces. When there is congruence between an ad and its destination, campaigns will earn a higher quality score.

Moreover, given that consumers are prone to shopping cart abandonment and that B2B customers require a more extended courting period than other types of consumers, developing a retargeting strategy is also a fundamental aspect of campaign success shared across B2B and B2C efforts.

Bagsy decided to utilize Facebook for their retargeting efforts:

While there are plenty of differences between targeting everyday consumers and business prospects, when it comes right down to it, PPC best practices remain intact no matter who is being targeted.

No matter if ads are used in the B2B or B2C realm, it is vital for advertisers to understand the audiences to which they speak. This means that developing buyer personas and conducting market research are key elements for promoting the awareness needed to employ the right language, messaging, targeting tactics and other vital PPC campaign components.

Once this crucial piece of information has been procured, use the strategies outlined above to help your ad campaign reach and resonate with its respective buyers.


Opinions expressed in this article are those of the guest author and not necessarily Marketing Land. Staff authors are listed here.



About The Author

Ronald Dod is the chief marketing officer and co-founder of Visiture, an end-to-end e-commerce marketing agency focused on helping online merchants acquire more customers through the use of search engines, social media platforms, marketplaces and their online storefronts. His passion is helping leading brands use data to make more effective decisions in order to drive new traffic and conversions.



how-to-modify-nodes-in-an-abstract-syntax-tree

One of the more powerful concepts I’ve stumbled across recently is the idea of abstract syntax trees, or ASTs. If you’ve ever studied alchemy, you may recall that the whole motivation for alchemists was to discover some way to transform not-gold into gold through scientific or arcane methods.

ASTs are kind of like that. Using ASTs, we can transform Markdown into HTML, JSX into JavaScript, and so much more.

Why are ASTs useful?

Early in my career, I tried to change files using a find-and-replace method. This ended up being fairly complicated, so I tried using regular expressions. I ended up abandoning the idea because it was so brittle; the app broke all the time because someone would enter text in a way I hadn’t anticipated and it would break my regular expressions causing the whole app to fall down.

The reason this was so hard is that HTML is flexible. That makes it extremely hard to parse using regular expressions. String-based replacement like this is prone to breaking because it might miss a match, match too much, or do something weird that results in invalid markup that leaves the page looking janky.

ASTs, on the other hand, turn HTML into something far more structured, which makes it much simpler to dive into a text node and do replacements on only that text, or to mess with elements without needing to deal with the text at all.

This makes AST transformation safer and less error-prone than a purely string-based solution.

What are ASTs used for?

To start, let’s take a look at a minimal document using a couple lines of Markdown. This will be saved as a file called home.md, which we’ll save in the content folder of our website.

# Hello World!

![cardigan corgi]() An adorable corgi!

Some more text goes here.

Assuming we know Markdown, we can infer that when this Markdown is parsed, it’ll end up being an

that says, “Hello World!” and a

that says, “This is some Markdown.”

But how does it get transformed from Markdown to HTML?

That’s where ASTs come in!

Because it supports multiple languages, we’re going to use the unist syntax tree specification and, more specifically, the project unified.

Install the dependencies

First, we need to install the dependencies required to parse the Markdown into an AST and convert it to HTML. To do that, we need to make sure we’ve initialized the folder as a package. Run the following command in your terminal:

# make sure you’re in your root folder (where `content` is)
# initialize this folder as an npm package
npm init

# install the dependencies
npm install unified remark-parse remark-html

If we assume our Markdown is stored in home.md, we can get the AST with the following code:

const fs = require('fs');
const unified = require('unified');
const markdown = require('remark-parse');
const html = require('remark-html');

const contents = unified()
  .use(markdown)
  .use(html)
  .processSync(fs.readFileSync(`${process.cwd()}/content/home.md`))
  .toString();

console.log(contents);

This code takes advantage of Node’s built-in fs module, which allows us to access and manipulate the filesystem. For more information on how this works, check out the official docs.

If we save this as src/index.js and use Node to execute this script from the command line, we’ll see the following in our terminal:

$ node src/index.js 

Hello World!

cardigan corgi An adorable corgi!

Some more text goes here.

We tell unified to use remark-parse to turn the Markdown file into an AST, then to use remark-html to turn the Markdown AST into a HTML — or, more specifically, it turns it into something called a VFile. Using the toString() method turns that AST into an actual string of HTML we can display in the browser!

Thanks to the hard work of the open-source community, remark does all the hard work of turning Markdown into HTML for us. (See the diff)

Next, let’s look at how this actually works.

What does an AST look like?

To see the actual AST, let’s write a tiny plugin to log it:

const fs = require('fs');
const unified = require('unified');
const markdown = require('remark-parse');
const html = require('remark-html');

const contents = unified()
	.use(markdown)
  .use(() => tree => console.log(JSON.stringify(tree, null, 2)))
	.use(html)
	.processSync(fs.readFileSync(`${process.cwd()}/content/home.md`))
	.toString();

The output of running the script will now be:

{
  "type": "root",
  "children": [
    {
      "type": "heading",
      "depth": 1,
      "children": [
        {
          "type": "text",
          "value": "Hello World!",
          "position": {}
        }
      ],
      "position": {}
    },
    {
      "type": "paragraph",
      "children": [
        {
          "type": "image",
          "title": null,
          "url": "",
          "alt": "cardigan corgi",
          "position": {}
        },
        {
          "type": "text",
          "value": " An adorable corgi!",
          "position": {}
        }
      ],
      "position": {}
    },
    {
      "type": "paragraph",
      "children": [
        {
          "type": "text",
          "value": "Some more text goes here.",
          "position": {}
        }
      ],
      "position": {}
    }
  ],
  "position": {}
}

Note that the position values have been truncated to save space. They contain information about where the node is in the document. For the purposes of this tutorial, we won’t be using this information. (See the diff)

This is a little overwhelming to look at, but if we zoom in we can see that each part of the Markdown becomes a type of node with a text node inside it.

For example, the heading becomes:

{
  "type": "heading",
  "depth": 1,
  "children": [
    {
      "type": "text",
      "value": "Hello World!",
      "position": {}
    }
  ],
  "position": {}
}

Here’s what this means:

  • The type tells us what kind of node we’re dealing with.
  • Each node type has additional properties that describe the node. The depth property on the heading tells us what level heading it is — a depth of 1 means it’s an

    tag, 2 means

    , and so on.

  • The children array tells us what’s inside this node. In both the heading and the paragraph, there’s only text, but we could also see inline elements here, like .

This is the power of ASTs: We’ve now described the Markdown document as an object that a computer can understand. If we want to print this back to Markdown, a Markdown compiler would know that a “heading” node with a depth of 1 starts with #, and a child text node with the value “Hello” means the final line should be # Hello.

How AST transformations work

Transforming an AST is usually done using the visitor pattern. It‘s not important to know the ins and outs of how this works to be productive, but if you’re curious, JavaScript Design Patterns for Humans by Soham Kamani has a great example to help explain how it works. The important thing to know is that the majority of resources on AST work will talk about “visiting nodes,” which roughly translates to “find part of the AST so we can do stuff with it.” The way this works practice is that we write a function that will be applied to AST nodes matching our criteria.

A few important notes about how it works:

  • ASTs can be huge, so for performance reasons we will mutate nodes directly. This runs counter to how I would usually approach things — as a general rule I don’t like to mutate global state — but it makes sense in this context.
  • Visitors work recursively. That means that if we process a node and create a new node of the same type, the visitor will run on the newly created node as well unless we explicitly tell the visitor not to.
  • We’re not going to go too deep in this tutorial, but these two ideas will help us understand what’s going on as we start to mess with the code.

How do I modify the HTML output of the AST?

What if we want to change the output of our Markdown, though? Let’s say our goal is to wrap image tags with a figure element and supply a caption, like this:

cardigan corgi
An adorable corgi!

To accomplish this, we’ll need transform the HTML AST — not the Markdown AST — because Markdown doesn’t have a way of creating figure or figcaption elements. Fortunately, because unified is interoperable with multiple parsers, we can do that without writing a bunch of custom code.

Convert a Markdown AST to an HTML AST

To convert the Markdown AST to an HTML AST, add remark-rehype and switch to rehype-stringify for turning the AST back to HTML.

npm install remark-rehype rehype-stringify

Make the following changes in src/index.js to switch over to rehype:

const fs = require('fs');
const unified = require('unified');
const markdown = require('remark-parse');
const remark2rehype = require('remark-rehype');
const html = require('rehype-stringify');

const contents = unified()
	.use(markdown)
  .use(remark2rehype)
	.use(() => tree => console.log(JSON.stringify(tree, null, 2)))
	.use(html)
	.processSync(fs.readFileSync('corgi.md'))
	.toString();

console.log(contents);

Note that the HTML variable changed from remark-html to rehype-stringify — both turn the AST into a format that can be stringified to HTML

If we run the script, we can see the image element now looks like this in the AST:

{
  "type": "element",
  "tagName": "img",
  "properties": {
    "src": "https://images.dog.ceo/breeds/corgi-cardigan/n02113186_1030.jpg",
    "alt": "cardigan corgi"
  },
  "children": [],
  "position": {}
}

This is the AST for the HTML representation of the image, so we can start changing it over to use the figure element. (See the diff)

Write a plugin for unified

To wrap our img element with a figure element, we need to write a plugin. In unified, plugins are added with the use() method, which accepts the plugin as a first argument and any options as a second argument:

.use(plugin, options)

The plugin code is a function (called an “attacher” in unified jargon) that receives option. These options are used to create a new function (called a “transformer”) that receives the AST and does work to, er, transform it. For more details on plugins, check out the plugin overview in the unified docs.

The function it returns will receive the entire AST as its argument, and it doesn’t return anything. (Remember, ASTs are mutated globally.) Create a new file called img-to-figure.js in the same folder as index.js, then put the following inside:

module.exports = options => tree => {
  console.log(tree);
};

To use this, we need to add it to src/index.js:

const fs = require('fs');
const unified = require('unified');
const markdown = require('remark-parse');
const remark2rehype = require('remark-rehype');
const html = require('rehype-stringify');
const imgToFigure = require('./img-to-figure');

const contents = unified()
  .use(markdown)
  .use(remark2rehype)
  .use(imgToFigure)
  .processSync(fs.readFileSync('corgi.md'))
  .toString();

console.log(contents);

If we run the script, we’ll see the whole tree logged out in the console:

{
  type: 'root',
  children: [
    {
      type: 'element',
      tagName: 'p',
      properties: {},
      children: [Array],
      position: [Object]
    },
    { type: 'text', value: '\n' },
    {
      type: 'element',
      tagName: 'p',
      properties: {},
      children: [Array],
      position: [Object]
    }
  ],
  position: {
    start: { line: 1, column: 1, offset: 0 },
    end: { line: 4, column: 1, offset: 129 }
  }
}

(See the diff)

Add a visitor to the plugin

Next, we need to add a visitor. This will let us actually get at the code. Unified takes advantage of a number of utility packages, all prefixed with unist-util-*, that allow us to do common things with our AST without writing custom code.

We can use unist-util-visit to modify nodes. This gives us a visit helper that takes three arguments:

  • The entire AST we’re working with
  • A predicate function to identify which nodes we want to visit
  • A function to make any changes to the AST we want to make

To install, run the following in your command line:

npm install unist-util-visit

Let’s implement a visitor in our plugin by adding the following code:

const visit = require('unist-util-visit');

  module.exports = options => tree => {
    visit(
      tree,
      // only visit p tags that contain an img element
      node =>
        node.tagName === 'p' && node.children.some(n => n.tagName === 'img'),
      node => {
        console.log(node);
      }
    );
};

When we run this, we can see there’s only one paragraph node logged:

{
  type: 'element',
  tagName: 'p',
  properties: {},
  children: [
    {
      type: 'element',
      tagName: 'img',
      properties: [Object],
      children: [],
      position: [Object]
    },
    { type: 'text', value: ' An adorable corgi!', position: [Object] }
  ],
  position: {
    start: { line: 3, column: 1, offset: 16 },
    end: { line: 3, column: 102, offset: 117 }
  }
}

Perfect! We’re getting only the paragraph node that has the image we want to modify. Now we can start to transform the AST!

(See the diff)

Wrap the image in a figure element

Now that we have the image attributes, we can start to change the AST. Remember, because ASTs can be really large, we mutate them in place to avoid creating lots of copies and potentially slowing our script down.

We start by changing the node’s tagName to be a figure instead of a paragraph. The rest of the details can stay the same for now.

Make the following changes in src/img-to-figure.js:

const visit = require('unist-util-visit');

module.exports = options => tree => {
  visit(
    tree,
    // only visit p tags that contain an img element
    node =>
    node.tagName === 'p' && node.children.some(n => n.tagName === 'img'),
    node => {
      node.tagName = 'figure';
    }
  );
};

If we run our script again and look at the output, we can see that we’re getting closer!

Hello World!

cardigan corgiAn adorable corgi!

Some more text goes here.

(See the diff)

Use the text next to the image as a caption

To avoid needing to write custom syntax, we’re going to use any text passed inline with an image as the image caption.

We can make an assumption that usually images don’t have inline text in Markdown, but it’s worth noting that this could 100% cause unintended captions to appear for people writing Markdown. We’re going to take that risk in this tutorial. If you’re planning to put this into production, make sure to weigh the trade-offs and choose what’s best for your situation.

To use the text, we’re going to look for a text node inside our parent node. If we find one, we want to grab its value as our caption. If no caption is found, we don’t want to transform this node at all, so we can return early.

Make the following changes to src/img-to-figure.js to grab the caption:

const visit = require('unist-util-visit');

module.exports = options => tree => {
  visit(
    tree,
    // only visit p tags that contain an img element
    node =>
    node.tagName === 'p' && node.children.some(n => n.tagName === 'img'),
    node => {
      // find the text node
      const textNode = node.children.find(n => n.type === 'text');
 
      // if there’s no caption, we don’t need to transform the node
      if (!textNode) return;
 
      const caption = textNode.value.trim();
 
      console.log({ caption });
      node.tagName = 'figure';
    }
  );
};

Run the script and we can see the caption logged:

{ caption: 'An adorable corgi!' }

(See the diff)

Add a figcaption element to the figure

Now that we have our caption text, we can add a figcaption to display it. We could do this by creating a new node and deleting the old text node, but since we’re mutating in place it’s a little less complicated to just change the text node into an element.

Elements don’t have text, though, so we need to add a new text node as a child of the figcaption element to display the caption text.

Make the following changes to src/img-to-figure.js to add the caption to the markup:

const visit = require('unist-util-visit');

module.exports = options => tree => {
  visit(
    tree,
    // only visit p tags that contain an img element
    node =>
      node.tagName === 'p' && node.children.some(n => n.tagName === 'img'),
    node => {
      // find the text node
      const textNode = node.children.find(n => n.type === 'text');

      // if there’s no caption, we don’t need to transform the node
      if (!textNode) return;

      const caption = textNode.value.trim();
      // change the text node to a figcaption element containing a text node
      textNode.type = 'element';
      textNode.tagName = 'figcaption';
      textNode.children = [
        {
          type: 'text',
          value: caption
        }
      ];

      node.tagName = 'figure';
    }
  );
};

If we run the script again with node src/index.js, we see the transformed image wrapped in a figure element and described with a figcaption!

Hello World!

cardigan corgi
An adorable corgi!

Some more text goes here.

(See the diff)

Save the transformed content to a new file

Now that we’ve made a bunch of transformations, we want to save those adjustments to an actual file so we can share them.

Since the Markdown doesn’t include a full HTML document, we’re going to add one more rehype plugin called rehype-document to add the full document structure and a title tag.

Install by running:

npm install rehype-document

Next, make the following changes to src/index.js:

const fs = require('fs');
const unified = require('unified');
const markdown = require('remark-parse');
const remark2rehype = require('remark-rehype');
const doc = require('rehype-document');
const html = require('rehype-stringify');

const imgToFigure = require('./img-to-figure');

const contents = unified()
	.use(markdown)
	.use(remark2rehype)
	.use(imgToFigure)
    .use(doc, { title: 'A Transformed Document!' })
	.use(html)
	.processSync(fs.readFileSync(`${process.cwd()}/content/home.md`))
	.toString();

 const outputDir = `${process.cwd()}/public`;

  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir);
  }
 
  fs.writeFileSync(`${outputDir}/home.html`, contents);

Run the script again and we’ll be able to see a new folder in root called public, and inside that we’ll see home.html. Inside, our transformed document is saved!




A Transformed Document!



	

Hello World!

cardigan corgi
An adorable corgi!

Some more text goes here.

(See the diff)

If we open public/home.html in a browser, we can see our transformed Markdown rendered as a figure with a caption.

Holy buckets! Look at that adorable corgi! And we know it’s adorable because the caption tells us so.

What to do next

Transforming files using ASTs is extremely powerful — with it, we’re able to create pretty much anything we can imagine in a safe way. No regexes or string parsing required!

From here, you can dig deeper into the ecosystem of plugins for remark and rehype to see more of what’s possible and get more ideas for what you can do with AST transformation, from building your own Markdown-powered static site generator; to automating performance improvements by modifying code in-place; to whatever you can imagine!

AST transformation is a coding superpower. Get started by checking out this demo’s source code — I can’t wait to see what you build with it! Share your projects with me on Twitter.