Outspoken Media just interviewed 11 famous SEO experts to answer 11 great questions about link building. We saw that Dori had gone through and answered these link building questions on her blog, so we thought we’d do the same. We hope you find these answers useful to your own link building campaigns.

1) What are a few emerging link tactics that you’ve seen in the past 12 months providing tremendous value to sites/pages? Can you give a specific example or two?

I haven’t found any new link tactics in the past 12 months that provide tremendous value. All the great tactics that truly provide tremendous value have been around for a while.

2) The SEO industry has become so stingy with linking to quality content to the point that many people who used to share a lot of it simply don’t bother, as it is not worth the cost of doing so. Is this a trend which spreads? Are we canaries in the coal mine, or is this just an issue impacting the SEO niche because it is far too saturated? What can Google do to encourage organic linking on the WWW (outside of nepotism, hype, spin, misinformation & ego-baiting)?

We don’t like to look greedy, so we don’t perform the stingy acts on our company web site. However the reason we created SpyderMate initially was as a linkbait tool. So for SpyderMate we don’t freely post external links. We feel it’s justified there however because we are offering a free and very useful service to the community.

3) What are the criteria for the “perfect link”?

The perfect link has the following attributes:

a) High authority domain & page
b) Minimal amount of external links on same page
c) Web site is very relevant to yours
d) The anchor text matches a keyword you are battling for
e) It’s located within the obvious content section of the web page
f) obviously it’s not tagged rel=”nofollow”

4) How do you go about creating a link marketing plan that will A:) Get tangible search results in a 6 to 12 month period and B:) Create sustainability for the website you are creating the plan for (i.e. keeping the links clean and adding links with long term value)

Well first you find out what keywords they should be targeting. Then you go through all their competitors ranking  for the keyword phrases and scour all their backlinks. Once you make sure you got a link from every backlink they have possible then you move on. Next you target the more lower hanging fruit longtail keywords through your list of standard directories that you submit to. This helps re-enforce your short-tail keywords with long tail variation power. After this you go for the seriously powerful links. These are your premium directory listings, your serious linkbait web sites and your powerful friend’s web sites. These sites target the more generic short-tail keywords that help raise all your longtails associated around them. The maintain sustainability you consistently create great new content and linkbait to create a consistent flow of natural inbound, quality, relevant links.

5) If you could choose a link on a lower authority page that would provide a moderate amount of targeted traffic or a link on a higher authority page that would provide absolutely no traffic – all other attributes being equal – for ranking benefits on the site you’re developing links for, which would you choose and why?

This depends entirely on how relevant and how well converting the lower authority page would be. It also depends on current rankings, whether or not there is a keyword I could boost my rankings for that would beat the conversions coming directly from this link. So if i had a site with a low conversion rate that had a high payout I would go with the lower authority link. If I had a site with a high conversion rate and a lower average payout I’d go with the high authority link.

6) Do you feel that you can conserve pagerank or that it’s still worth the effort to sculpt your links, by limiting the number of links on a page, creating them with JavaScript, passing them through a blocked page or using nofollow?

I believe you can, but also don’t believe it’s worth the effort, especially on a site that you’re constantly updating. There are other tasks you can spend your valuable time on that provide more value to your web presence.

7) Please discuss what link deprecation is and what impact it may have on a link building campaign.

I believe link deprecation to be the result of poorly built links primarily. The only reason this should occur is due to the age of the links. Actually losing links only happens to links that were easily attained and therefore easily lost. The best links are very hard to come by and last the test of time.

8) Do you think search engines are trying to find a way to depend less on link popularity and more on other algorithmic/social media factors?

I believe they are, but I don’t think it plays much of a role currently. I also think basing searches on social media is not viable for many categories of web sites. The fact that social media sites like Facebook are social make your likes inherently based around your friend’s general interests as well as your own. Even though I’m heavily interested in SEO, it does not mean I’m going to bother liking SEO articles I find interesting left and right because I know it will annoy my friends. I want to share things that I know my friends will also be interested in. This is not a system that a true search engine can be built upon. A search engine can however leverage this information is done on the right level.

9) How much do you stress internal linking on your own or clients’ sites? Do you have a quick rule of thumb or strategy to maximize the effectiveness of internal links?

I’ve found that emphasizing your traffic generating pages through inbound links is critical. Using carefully placed inbound links to associate relevant topics and re-emphasize targeted anchor text is extremely helpful, especially for that highest level page in the link scheme. Enough said.

10) What’s a successful link development strategy many overlook or dismiss?

Linkbait. People tend to dismiss serious linkbait strategies because of the time/costs associated with them. They tend to think other efforts are easier, but nothing generates those truly unique, critical, powerful links like well done linkbait.

11)  What have you been most WRONG about over the course of your link building/SEO career?

I was most wrong about how effective thousands of links coming from the same authoritative web site, with different anchor text, coming from different pages, linking to different pages were at lifting my rankings. Domain diversity is absolutely crucial. I’ve learned that one great warrior is better than thousands of mediocre warriors.

Android is one of the fastest growing operating systems and software stacks for smartphones. OS smartphones ranked second among all smartphone OS handsets sold in the U.S. in the first quarter of 2010. Dell, HTC, Motorola and Samsung are some of the manufacturers using this operating system.

Google opened the source code under an Apache License and now the Android SDK allows developers to write managed code in the Java language, controlling the device via Google-developed Java libraries. The SDK includes a comprehensive set of development tools, such as a debugger, libraries, a handset emulator, documentation, sample code and tutorials.

Applications that run in the background are supported in Android in contrast to iPhone. All of these facts stimulate me to research this interesting technology. I decided to make an application running in the background and receiving the GPS location. The main goal was to inform the user not to write or read text messages while he or she is driving.

It is pretty easy to develop an Android application, especially if you are using Eclipse. You can download the latest version of the Android SDK from the official site. There are a lot of tutorials explaining how to install and use the SDK in Eclipse, if you are a beginner you can take a look at the developer’s guide.

Utilizing the GPS Module

As you may know smartphones have a gps module which provides the current location. This feature is used in many applications. To implement it you have to modify the AndroidManifest.xml, this is the main configuration file.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

With these permissions you tell to the application that the GPS module will be used and the application is given access to it.

The Activity Class

There is one very important class in an application’s overall lifecycle – the Activity class. It takes care of creating a window for you in which you can place your UI. But this activity is not enough to create a background process. It should be used in conjunction with another object from the ContextWrapper to achieve the behavior of an application component that runs in the background and doesn’t interact with the user. This functionality is given up by the Service class. Both the activity and the service should be declared in the android manifest file.

My idea is to create a main activity class and in the onCreate() method call the service. But when something is started, there needs to be a way to stop it. The best place to stop the service is in the onDestroy() method of the main activity. These two methods are overridden because the main activity has to extend the Activity.java class. They look like:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    try {
        // setup and start MainService
        MainService.setMainActivity(this);
        Intent svc = new Intent(this, MainService.class);
        startService(svc);
    } catch (Exception e) {
    Log.e(">>>", "Problem creating the UI", e);
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // stop MainService
    Intent svc = new Intent(this, MainService.class);
    stopService(svc);
}

This is how the activity class looks. The service class will manipulate the GPS module data. It will get the location in a time interval, get the speed or calculate it if it is not available and finally if the speed is more than 10km/h it will inform the user not to use the phone’s features while driving in order to avoid accidents.

The Service Class

As the main activity extends Activity.java class, the service should extend the Service.java class. The service is started in the overridden method onCreate() of the class.

// service business logic
private void startService() {
    TimerTask timerTask = new TimerTask(){
        public void run() {
            getUpdateLocation();
        }
    };
    timer.scheduleAtFixedRate(timerTask,0,UPDATE_TIME_INTERVAL);
    Log.i(getClass().getSimpleName(), "Timer started!");
}

First a timer object is needed which will take care of calling the service. To schedule the timer you have to prepare a TimerTask instance, which calls your business logic. In the example it is called the getUpdateLocation() method, its functionality will be explained later in this post. The timer is scheduled with the timer task and the specified time interval passed as a constant.

Lets take a look at the getUpdateLocation() method. As the title says, the method gets the location from the LocationManager.java class. The locations are separated by providers, so they are filtered with a criteria. Some GPS providers send the speed of the device. But if the speed is not received, it can easily be calculated as a quotient of the distance between the current and previous location with the subtraction of the two times.

private void getUpdateLocation() {
    Log.i(getClass().getSimpleName(), "background task - start");
    // get the system location service
    locationManager = (LocationManager)
                               getSystemService(Context.LOCATION_SERVICE);
    // create criteria to filter the providers
    Criteria criteria = new Criteria();
    bestProvider = locationManager.getBestProvider(criteria, false);
    Location location = locationManager.getLastKnownLocation(bestProvider);

    date = new Date();
    if(currentLocation == null){
        currentLocation = locationManager.getLastKnownLocation(bestProvider);
        currentDate = new Date();
    }

    float speed = calculateSpeed(location);
    // speed > (10km/h = 2.78m/s)
    if(speed > SPEED_LEVEL){
        Looper.prepare();
        Context context = getApplicationContext();
        CharSequence text = MESSAGE;
        int duration = Toast.LENGTH_LONG;
        Toast toast = Toast.makeText(context, text, duration);
        toast.show();
        Looper.loop();
    }
    // set the currentLocation and currentDate with new values
    // for the next iteration
    currentLocation = location;
    currentDate = date;
    Log.i(getClass().getSimpleName(), "background task - end");
}

The user will be informed with a warning message, if the calculated speed is more than a specified value. This is the business logic, but there is one other thing to take care of – the timer. The timer is started in the onCreate() method of the service class. It will be stopped in the onDestroy() method of the service class. The cancel() method will then be called in order to cancel the timer and remove any scheduled tasks.

if (timer != null) {
    timer.cancel();
}

The completed application will run in the background and inform the user with a message that his or her speed is more than 2.78 meters per second.

Google recently announced that they began factoring in web site load time into their search engine ranking algorithm. This has brought the importance of a fast load time front and center even though “fewer than 1% of search queries will change as a result” of this update. MentorMate.com likely was not impacted very significantly from this change to Google’s ranking algorithm; however, we still decided to optimize our site’s load time. We did this because Google showed that “delays of under a half-second impact business metrics.” It’s better to be safe than sorry, and besides, it’s not that difficult to optimize your site. We recommend you do the same with your web site, here are nine easy ways to do just that:

1. Place scripts near closing body tag

Scripts block parallel downloads. While a script is downloading, the browser will wait until it finishes before downloading anything else. This is why it is recommended that you get everything else out of the way before having your users download scripts.

2. Remove any broken links/references

Simply enter your web page URL in Pingdom’s Full Page Test Tool. This tool will color-code any broken links/references in red so you can easily identify them.

3. Specify image dimensions

When image dimensions are not specified in the HTML and/or CSS of a web page, the web server must spend resources calculating these dimensions. It’s particularly important to specify the dimensions of images being used in conjunction with heavy Javascript image-manipulation libraries.

4. Localize external file references to minimize DNS requests

This is another spot in which Pingdom’s Full Page Test Tool comes in handy. Enter your URL and look for any file references to external domains. If possible, copy these files onto your own domain and link to them locally to minimize DNS requests.

5. Minimize slow-loading libraries and external widgets

Libraries and external widgets need to be chosen carefully. Sometimes it’s worth sacrificing load time for particularly engaging/useful libraries and/or widgets; however, sometimes you can live without the library and/or widget. For example, on MentorMate.com we felt it was worth sacrificing Sexybuttons for better load time, but sacrificing load time for Shadowbox. External widgets can be particularly bad for a site’s load time because of their reliance on external servers.

6. Compress your files using gzip

These days, gzip comes pre-installed on most web servers. You can check to see if gzip is running your server quite easily using Google’s Firebug “Speed Test” extension. Open Firefox, visit the web site you’d like to optimize, and open Firebug. From there, click “Analyze Performance” and select the “Resources” tab. Then you can expand any file to view details. If gzip is installed and compressing the element you are viewing, you will see what is highlighted in red in the following screenshot:

Google's Firebug "Page Speed" extension gzip enabled

7. Merge Javascript and CSS files or use Minify

Depending on the complexity of your site, it can be quite time consuming to merge all CSS into one file. The same goes for Javascript. This is why Minify was developed.  Minify “combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers.” Then you simply need to install it on your site, select the files you want it to serve together, change some references in your header, and you’re good to go. This will minimize your site’s load time by further helping to reduce HTTP requests.

8. Load cross-browser compatibility hacks only for necessary browsers

For instance, we use the IE PNG FIX for IE6 PNG transparency compatibility. This script adds an extra second to web site load time. For this reason, we only load the script if the visitor is running IE6 by applying an IE6-specific stylesheet which references the IE PNG FIX. This way the extra load time will only impact IE6 visitors.

We simply added the following line of code to our header:

<!--[if IE 6]><link rel="stylesheet" type="text/css" href="css/ie6.css" /><![endif]-->

9. Use CSS sprites to minimize HTTP Requests

A CSS sprite is the combination of multiple images into one. Then CSS is used to reference different aspects of the image where needed. This reduces load time by minimizing all of these images into only one  HTTP request. CSS Tricks has a good example of CSS sprites.

JFreeChart is an open-source Java based library which allows the creation of complex charts in a simple way.

Supported chart types include:

  • X-Y charts (line, spline and scatter);
  • Pie charts;
  • Gantt charts;
  • Bar charts (horizontal and vertical, stacked and independent);
  • Single valued (thermometer, compass, speedometer).

I am going to explain how to use the chart library and how to create a bar chart with a custom color. To create a chart with JFreeChart library, first you have to make a Dataset object. The chart is generated from this data collection. Next you have to instate the class DefaultCategoryDataset, which is passed to the chart object. In the code below as a data source is used ArrayList, which may come from data base.

// load the data
double[] data = new double[jobTitles.size()];
for (int i = 0; i < jobTitles.size(); i++) {
     data[i] = jobTitles.get(i).getCount();
}
// create the data set
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for (int i = 0; i < data.length; i++) {
     dataset.setValue(data[i], "Profit1", jobTitles.get(i).getTitle());
}

The next step is to do an object from chart type. The library uses a class named JFreeChart to display the chart. This chart object is an instance, which comes from the method createBarChart of the static class ChartFactory. The method takes as a parameter the formed collection (dataset). There are many properties, which you can pass to the constructor, such as labels, orientation, etc. It is possible to change colors, size, scale and many other characteristics.

You have ready object now and the next step is to send the chart to the web page. For this purpose, the library includes a class named ChartUtilities that provides several methods for saving charts to files or writing them out to streams in JPEG or PNG format. The methods from this class can be used for creating JPEG images for static web pages. The goal, however, is not to save the chart in a hard drive; the goal is to pass the graphic to the application. To do that, you can use the servlet technology and display the dynamic data stream in the jsp pages.

Consequently the next step is to generate BufferedImage from your chart object and to transfer this stream through the session. You should also write the image in the http response writer.

// create the chart
final JFreeChart chart = ChartFactory.createBarChart("Allocation of Duties",
                 "Values", dataset, PlotOrientation.VERTICAL, true, true, true);
// set custom color
GradientPaint gradientpaint0 = new GradientPaint(0.0F, 0.0F,
                 new Color(209, 228, 246), 0.0F, 0.0F, new Color(82, 141, 201));
BarRenderer r = (BarRenderer)chart.getCategoryPlot().getRenderer();
r.setSeriesPaint(0, gradientpaint0);
ChartRenderingInfo info = null;
HttpSession session = request.getSession();
try {
     // create RenderingInfo object
     response.setContentType("text.html");
     info = new ChartRenderingInfo(new StandardEntityCollection());
     BufferedImage chartImage = chart.createBufferedImage(640, 400, info);
     session.setAttribute("chartImage", chartImage);
     PrintWriter writer = new PrintWriter(response.getWriter());
     ChartUtilities.writeImageMap(writer, "imageMap", info, false);
     writer.flush();
} catch (Exception e) {
     e.printStackTrace();
}

Finally you should write the servlet. This is a Java object, which dynamically answers requests and built response to the client. Each servlet has two main methods – doGet(HttpServletRequest request, HttpServletResponse response) and doPost(HttpServletRequest request, HttpServletResponse response). You need to get the chart stream from the session in the servlet. The image is set to an attribute named chartImage in the session. You have to get http session instance from the request and then to retrieve the previously set attribute.


// Process the HTTP Get request
public void mdoGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// get the chart from session
HttpSession session = request.getSession();
BufferedImage chartImage = (BufferedImage)session.getAttribute(“chartImage”);
// set the content type so the browser can see this as a picture
response.setContentType(“image.png”);
// send the picture
PngEncoder encoder = new PngEncoder(chartImage, false, 0, 9);
response.getOutputStream().write(encoder.pngEncode());
}
// Process the HTTP Post request
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(re

The idea here is to parse the XML response from a Wunderground Data Feed Web Service, which provides information for current weather conditions. The conceptual part of target XML follows. More details about XML and available weather API can be found on the Wunderground Data Feed site.

<current_observation>
  <credit>Weather Underground NOAA Weather Station</credit>
  <credit_URL>http://wunderground.com/</credit_URL>
  <image>
    <url>http://icons.wunderground.com/graphics/wu2/logo_130x80.png</url>
    <title>Weather Underground</title>
    <link>http://wunderground.com/</link>
  </image>
  +<display_location></display_location>
  +<observation_location></observation_location>
  <station_id>KSFO</station_id>
  <observation_time>Last Updated on July 14, 9:56 AM PDT</observation_time>
  ...
  <local_time>July 14, 10:01 AM PDT</local_time>
  ...
  <local_epoch>1247590877</local_epoch>
  <weather>fog</weather>
  <temperature_string>52 F (11 C)</temperature_string>
  <temp_f>52</temp_f>
  <temp_c>11</temp_c>
  <relative_humidity>76%</relative_humidity>
  ...
  <icon>fog</icon>
  ...
</current_observation>

For processing this XML we decided to use DOM parser for the following reasons:

  • To save the structure of data (more flexible usage for presentation and further processing)
  • To ensure good maintainability and adaptability (external provider may change XML structure)
  • The XML is not so large that it would not cause memory issues.
Figure 1 the DOM presents an XML document as a tree-structure

DOM Tree

The DOM presents an XML document as a tree-structure. The parser creates a tree object out of the document. The user accesses data by traversing the tree. The API allows for constructing, accessing and manipulating the structure and content of XML documents (Figure 2).

Figure 2 Using a DOM Tree.

For our implementation we use a very simple and effective parser - XMLTreeParser/XMLTreeNode. The implementation consists of two classes: XMLTreeParser and XMLTreeNode. It uses NSXMLParser to read the document and for every one <element> tag creates a XMLTreeNode object. That way, it creates the tree that contains nodes of type XMLTreeNode.

To create a parser, simply instantiate the XMLTreeParser class:

XMLTreeParser* parser = [[XMLTreeParser alloc] init];

After that start parsing – call the parse method and provide the XML data:

&lt;strong&gt;XMLTreeNode* root = [parser parse:xmlData];

Now you can traverse through the tree manually looking for things.

NSArray* items = [stuffindChildren:@&quot;icon&quot;];

Architecture of an XML application using DOM

The DOM model is easiest to understand. On Figure 3, a basic architecture of an XML application using DOM is presented:

Figure 3 Architecture of an XML application using DOM

So here’s what happens: A parser reads the XML file and builds a DOM document to match the XML file. From that point until a save is performed, all interaction between the application and XML hits the DOM document rather than the corresponding XML file. It’s interesting to note that almost all XML parsers use SAX. Before you build a DOM document you must detect events such as the start of an element (start tag encountered), end of an element (end tag encountered) and/or a new attribute (name followed by equal sign followed by quoted string encountered). DOM can be thought of as an extra abstraction to lessen the programmer’s workload, at the expense of memory usage.

Modifications are made directly to the DOM document. Elements can be added, deleted, renamed, and rearranged. Text nodes can be added, delete,d or changed. Elements can be moved either within the same level, or promoted or demoted to different levels.

Choose a design pattern for implementation

Choosing the appropriate pattern is a critical step. The Dynamic Document architectural pattern was used in our case. This pattern contains XML not typed by DTD or schema, but follows assessors for underlying program objects. It allows for unlimited extension by multiple, uncoordinated parties at the cost of lack of type-checking, and it’s simple to implement.

Figure 4 Map the XML document to object model.

The DOM API is used to read information from an XML document. There is an easier way of getting around using DOM for modifying and saving the XML data — creating an object model for the information in the document. You can create this object model by giving it a DOM object that holds all the XML document information. That is, the XML document should be mapped to an object model.

Figure 4 shows how to map the Current Observation XML document to a set of related classes (object model). The <current_observation> element is mapped to the CurrentObservation class; the <display_location> element is mapped to the DisplayLocation class. The <observation_location> element is mapped to the ObservationLocation class. Single elements (without sub elements or others attributes) are mapped to the parent element class properties.  Each class can be instantiated using a method or Factory implementation by providing a part of XML tree, with root – class element. In this the exampleinit method was used -(id)initWithData:(NSData *)data;.

Here is a partial code listing from CurrentObservation.h:

  @interface WUCurrWeather : NSObject {
   NSString *credit;
   ...
   Image *image;
   DisplayLocation *displayLocation;
   ...
   NSString *icon;
  ...
  }

  @property(nonatomic, retain) NSString *credit;
  @property(nonatomic, retain) Image *image;
  @property(nonatomic, retain) DisplayLocation *displayLocation;
  ...
  @property(nonatomic, retain) NSString *icon;
  ...

  - (id)initWithData:(NSData *)data;

  -(NSString *)description;
  @end;
and Image.h files

@interface Image : NSObject {
NSString *url;
NSString *title;
NSString *link;
}
@property(nonatomic, retain) NSString *url;
@property(nonatomic, retain) NSString *title;
@property(nonatomic, retain) NSString *link;
- (id)initWidthData:(XMLTreeNode *)root;
- (NSString *) description;
@end
Now you can use your object model to manage data as you wish – save it in a database or show it in a GUI for example.

Example

Below is an example of a View Based iPhone Application that calls a Wunderground Data Feed Web Service to get current weather conditions. The update button repeats the call and refreshes the screen when new data arrives. The screenshots below visualize the application’s behavior:

In the provided source code you can find all these steps implemented.