soy Kseso y esto EsCSS

Cómo añadí el marcado de datos estructurados al blog (schema·org)

Un detallado post sobre qué son y cómo añadir el marcado de datos estructurados [schema.org] al blog. Incluye códigos, su significado y capturas de pantalla de su resultado. Incluye el porqué de cuatro errores en las plantillas de blogger y cómo corregirlos (errores por idBlog, idPost, image_url y falta de author)

Cómo añadí el marcado de datos estructurados al blog (schema·org)

·Por Kseso ✎ 24
Cómo añadir marcado de datos estructurados al blog (schema.org)

A estas alturas seguro que estás saturado de oír sobre la conveniencia y los beneficios derivados de usar datos estructurados en tus páginas.

Así que no seré yo quien te lo repita una vez más. No. La intención de este artículo es contarte un poco cómo los he añadido yo a este blog y mi experiencia en el proceso.

Adicción que he hecho después de estar en funcionamiento, el blog. Así que me he tenido que incluirlos en un código ya existente. Como seguro que es tu caso. El código del blog ya existe y lo que menos te apetece es tocar demasiado su código.

Pero antes de entrar en materia un poco de info previa para quienes no conozcan el tema de los datos estructurados.

Qué son los datos estructurados y su marcado

Añadir marcado de datos estructurados no es otra cosa que etiquetar las partes más significativas de una página para que los buscadores (sus arañas) sepan qué es cada parte de la información y así puedan mostrarlas en sus resultados de búsqueda. Son los famosos snippets o fragmentos enriquecidos.

Existen distintos lenguajes para añadir esa metainformación. El más extendido y usado es el creado por Google, Yahoo y Bing conocido como Schema.org.

Schema.org

Schema.org es un sistema de microdatos desarrollado, y por lo tanto "entendido", por los tres grandes buscadores: Google, Yahoo y Bing.

Se basa en en el uso de una serie de esquemas itemtype predefinidos y una serie de propiedades itemprop para indicar qué es qué.

Se puede acotar scoped el alcance del esquema aplicado en un elemento con ayuda del atributo itemscope. Por ejemplo, en la portada del blog desearás que cada "resumen" de los distintos post sean tenidos en cuenta como individuales:

<article itemtype='http://schema.org/BlogPosting' itemprop='blogPost' itemscope='itemscope'> Todo el contenido del resumen: título, snipet, imagen... <article> Tantos article más como contenga el índex

De esta manera identificará a cada resumen y sus datos marcados como un BlogPosting diferente. Lo mismo sería si tienes una organización con una relación de personas (cada una con su información). Usarías un esquema acotado para cada persona y uno de organización que los englobaría.

Hay esquemas específicos según sea la naturaleza de los datos: para marcar un blog, una página webs, si la información es sobre personas, organizaciones... En función del esquema de marcado declarado hay una serie de propiedades que se podrán usar. Y cada una de éstas admiten un tipo de valor.

También hay esquemas que no pueden contener (o ser contenidos) otros esquemas dentro de ellos. Así, si declaras un tipo de esquema de Web en el html, por ejemplo, no podrás declarar en la página otro de Blog.

Este marcado estructurado de los datos se puede añadir de tres posibles formas:

  1. Como atributos en la etiqueta de apertura del elemento
  2. Como metadatos usando la etiqueta <meta.../>
  3. En JSON

Yo sólo he experimentado con los dos primeros. Así que el tercero no lo mencionaré ;-)

Schema.org al blog

todos los esquemas usados en el blogComo me lo he tomado como un juego y experimento y nada impide usar distintos esquemas, yo he decido dejarlos en seis, como puedes ver en la imagen adjunta:

  1. Blog
  2. Website
  3. Blogposting
  4. Organization
  5. Person
  6. hatom

Éste último, hatom, no pertenece a Schema.org, usa un sistema distinto. Volveré a él al final. Sólo te adelanto que es el responsable del error tan común en blogger de falta author

Schema Blog

Para introducir los datos relativos al esquema blog decidí añadirla al final de todo, en el footer, y así no tener que andar tocando nada del código ya existente.

Toda la información se facilita mediante el uso de metas. El código final usado es el siguiente:

<div class='hide' itemprop='Blog' itemscope='itemscope' itemtype='http://schema.org/Blog'> <meta itemprop='name' content='KsesoCss'/> <meta itemprop='inLanguage' content='ES-es'/> <meta itemprop='url' content='http://ksesocss.blogspot.com/'/> <meta itemprop='headline' content='Todo Css en español'/> <meta itemprop='alternativeHeadline' content='KsesoCss. "El mejor blog sobre Css en español" Dicen.'/> <meta itemprop='description' content='Css a fondo. Especificaciones W3c en español. Tutoriales, demos y ejemplos. Información contrastada y artículos originales'/> <meta itemprop='image' name='logo' content='http://2.bp.blogspot.com/-Q2ZLxt2jVvM/U4IuJDCZ1zI/AAAAAAAAJUw/40-c9PLBra0/s45/kseso2014.png'/> <meta itemprop='Audience' content='Diseñadores, maquetadores, frontend y amantes de las cosas hechas con buen gusto y mejor estilo'/> <meta itemprop='creator' content='Kseso'/> <meta itemprop='author' content='Kseso'/> <meta itemprop='editor' content='Kseso'/> <meta itemprop='contributor' content='El Furoya'/> <meta itemprop='license' content='Beerware para las ideas y demos'/> <meta itemprop='publishingPrinciples' content='http://ksesocss.blogspot.com/p/licencias.html'/> </div>

Como ves, en la caja padre (el div) y escapado itemscope='itemscope' indico el tipo de esquema usado y a continuación, dentro de cada meta una propiedad y su valor. El resultado de ello y la interpretación que de ello hace Google visto mediante su herramienta para probar los datos estructurados:

captura datos del schema blog
captura datos del schema blog

Marcado estructurado de cada post:
Schema BlogPosting

Este esquema de marcado quizás sea el que exige un poco más de atención, pues tenemos que aprovechar el marcado y elementos ya existentes en la plantilla del blog.

Así que para no liarme mejor lo vemos paso a paso. Nota que aunque los códigos sean los propios de blogger nada impide añadir los atributos y sus valores en cualquier otro sistema de blog. Sólo es cuestión de buscar las equivalencias.

Lo primero es buscar dónde comienza cada post o artículo, así tendremos la información tanto en las páginas que muestran cada artículo individual como en las que listan varios de ellos, como el índex o el las búsquedas. Y a la vez estructuramos cada uno como independiente de los demás.

En mi plantilla este comienzo es el siguiente:

<b:includable id='post' var='post'> <article class='post hentry' añadir los siguientes atributos itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'>

Con los atributos itemtype itemprop e itemscope y sus valores le estamos indicando al buscador que el contenido de este article está marcado con el esquema correspondiente a los posts de un blog, que es un post (y puede haber más) y que no se propague más allá de su cierre.

A continuación busco el código correspondiente al título del artículo. En mi plantilla hay dos, dependiendo si es una página de tipo item <b:if cond='data:blog.pageType == "item"'> lo etiqueto como h1 y si no lo es <b:if cond='data:blog.pageType != "item"'> (como el índex) lleva un h2.

Así que toca marcar cada uno de ellos como sigue. Sólo incluyo uno de los dos hnº como ejemplo:

<h1 itemprop='headline' class='post-title entry-title'> ...resto del código ya existente... </h1>

A continuación del título en mi plantilla muestro la fecha de publicación, ambos dentro de un elemento header. Así que uso esta fecha para marcarlo como fecha de publicación en este esquema:

<span itemprop='datePublished' rel='bookmark' class='published' expr:title='data:post.timestampISO8601'> <data:post.timestamp/> </span>

Cada artículo verás que lo encabezo con un texto (más o menos acertado según la inspiración del momento) con un párrafo resumen del post. Es el texto añadido a través de Descripción de búsqueda. Así que lo uso para marcarlo como "description":

<p itemprop='description' class='intro-post'> <data:blog.metaDescription/></p>

Pero esta descripción tiene un pequeño fallo: si es una página de tipo item muestra ese resumen pero si es otro tipo de página, como el índex o resultado de búsquedas, blogger la ignora y en su lugar muestra la descripción incluida el el head de la página en el meta del mismo nombre. Si la hay.

Así que después de cerrar el header del post y antes de abrir el elemento que contendrá todo el artículo (o su snippet) aprovecho para incluir esta serie de datos estructurados y repetir, con otro nombre itemprop='about', la descripción:

<meta itemprop='author' content='Kseso' name='Kseso'/> <meta itemprop='editor' content='Kseso' name='Kseso'/> <meta itemprop='creator' content='Kseso' name='Kseso'/> <meta itemprop='alternativeHeadline' content='KsesoCss. "El mejor blog sobre Css en español" Dicen.'/> <meta itemprop='url' expr:content='data:post.canonicalUrl'/> <meta itemprop='url' expr:content='data:post.url'/> <b:if cond='data:blog.pageType != "index"'> <meta itemprop='about' name='Sinopsis' expr:content='data:blog.metaDescription'/> <b:else/> <meta itemprop='about' name='Sinopsis' expr:content='data:post.snippet'/> </b:if> <meta itemprop='image' expr:content='data:post.firstImageUrl'/>

Lo que es el propio contenido de cada artículo, que blogger lo genera mediante <data:post.body/> y sus condicionales, yo lo tengo dentro de un elemento section y es a este elemento al que añado la información para indicarle lo que es a los buscadores.

<section itemprop='articleBody' class='post-body entry-content widenbt' expr:id='"post-body-" + data:post.id'> <!-- condicionales --> <data:post.body/> <code>

Y para finalizar con este esquema sólo resta marcar las etiquetas o labels de cada artículo como tales con el atributo itemprop='keywords'. Para ello buscamos el loop que las genera.

<b:loop values='data:post.labels' var='label'> <a class='hide' expr:href='data:label.url' rel='tag'> <span itemprop='keywords'><!--aquí el marcado--> <data:label.name/> </span></a> ... resto código del loop </b:loop>

itemprop keywordsEste marcado estructurado de las etiquetas lo modifiqué después de la captura de pantalla siguiente. Actualmente se ve como en la imagen adyacente.

El resultado de todo este trabajo es el que ves en la imagen que tienes a continuación. Creo que como en las anteriores te será fácil identificar cada "itemprop" usado en el marcado estructurado de los datos en la info que muestra la herramienta de Google para probarlos:

captura datos del schema blogPosting
captura datos del schema blogPosting

Junto a todo esto, en la redacción de los artículos estoy añadiendo a cada imagen empleada, además del atributo alt el itemprop="image". Esto como es lógico hay que hacerlo a mano para cada una.

A continuación por seguir cierto orden están los esquemas website, person y organization. Y tras ellos ellos dos temas relacionados con los schemas usados en muchas plantillas Blogger y su marcado de datos estructurados:
Errores comunes en el schema de blogger y cómo corregirlos:
blogId error
postId error
image_url error

Schema Website

Para indicarle a los buscadores la información relativa a la página web o sitio uso la misma fórmula que en el "esquema blog". El código, el que ves a continuación. Nota que de nuevo sólo hago uso del elemento meta

<div itemprop='WebSite' itemscope='itemscope' itemtype='http://schema.org/WebSite' class='hide'> <meta itemprop='name' content='KsesoCss'/> <meta itemprop='url' content='http://ksesocss.blogspot.com/'/> <meta itemprop='inLanguage' content='ES-es'/> <meta itemprop='headline' content='Todo Css en español'/> <meta itemprop='alternativeHeadline' content='KsesoCss. "El mejor blog sobre Css en español" Dicen.'/> <meta itemprop='description' content='Descripción del sitio'/> <meta itemprop='image' name='logo' content='ruta/img.ext'/> <meta itemprop='Audience' content='A quienes estén dirigidos los contenidos'/> <meta itemprop='creator' content='Kseso'/> <meta itemprop='author' content='Kseso'/> <meta itemprop='editor' content='Kseso'/> <meta itemprop='license' content='Beerware para las ideas y demos'/> <meta itemprop='publishingPrinciples' content='http://ksesocss.blogspot.com/p/licencias.html'/> </div>

El resultado de este código, visto como antes en la herramienta de Google, es:

captura datos schema website
captura datos del schema website

Schema Person

Para darle información a los buscadores y sus arañas sobre quién es el humano causante del ruido que estas páginas añaden a internet uso un esquema de persona con la siguiente información:

<div class='hide' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta itemprop='name' content='Kseso'/> <meta itemprop='description' content='Ramajero y Argonauta virtual, Enredique Amanuense de Css. Por una web con mucho estilo, para usuarios con buen gusto.'/> <meta itemprop='url' content='https://plus.google.com/107861690109455510615' rel='author'/> <meta itemprop='image' name='logo' content='//lh3.googleusercontent.com/-UHCyvCtjlTI/AAAAAAAAAAI/AAAAAAAAGj4/LvWpIQqIOAg/s120-c/photo.jpg'/> <a itemprop='sameAs' href='http://twitter.com/kseso'> <span itemprop='name'>@Kseso en Twitter</span> </a> <a itemprop='sameAs' href='https://www.facebook.com/pages/Ksesocss-Blog/320233214829392'> <span itemprop='name'>Kseso Blog en Facebook</span> </a> <a itemprop='sameAs' href='http://plus.google.com/107861690109455510615'> <span itemprop='name'>Don Kseso en Google+</span> </a> <a itemprop='sameAs' href='https://dl.dropboxusercontent.com/u/166051553/humans.txt'> <span itemprop='name'>Humans.txt porque somos humanos</span> </a> <a itemprop='sameAs' href='http://codepen.io/Kseso/'> <span itemprop='name'>@Kseso en Codepen</span> </a> </div>

Observarás que en este esquema además introducir la información en los meta´s hago uso de la otra vía que mencionaba antes: atributos en elementos. En concreto para indicar otras "identidades" o alias (y su enlace) por el que soy conocido (escasamente) en la cosadigital. Es el itemprop='sameAs'.

Y de nuevo el resultado:

captura datos del schema person
captura datos del schema person

Esquema "Organization"

Este esquema lo añado usando atributos en elementos ya existentes en el blog. Aprovecho que tanto en el índex como en cada uno de los artículos tengo una lista con enlaces a distintas cuentas, servicios o redes sociales (los svg´s sobre el formulario de suscripción) para introducir el marcado necesario.

El mecanismo es el mismo usado con los enlaces en el itemtype='.../Person' anterior, junto a un elemento link para introducir la dirección de la web como valor url

<ul class='social-list' itemscope='itemscope' itemtype='http://schema.org/Organization'> <meta itemprop='name' content='KsesoCss'/> <meta itemprop='founder' content='Kseso'/> <meta itemprop='logo' content='ruta/imagen/kseso2014.png'/> <link itemprop='url' href='http://ksesocss.blogspot.com/'/> <li> <a itemprop='sameAs' href='http://twitter.com/kseso' title='@Kseso en twitter'> <svg...> <use xlink:href='...> </svg> <span itemprop='name' class='hide'>@Kseso en Twitter</span> </a> </li> Resto de items con su marcado

Creo que en este esquema de la organización es un buen sitio para incluir el archivo humans.txt Que si no lo tienes no es mala idea contar con él. El resultado, una vez procesado el marcado, el que ves en la imagen:

captura datos del schema organization
captura datos del schema organization

Errores comunes en el schema de blogger

Hasta ahora todo ha sido añadir marcado estructurado al contenido del blog y hacerlo de tal manera que no surjan errores o advertencias al probarlo en la herramienta que Google tiene disponible.

Sin embargo casi todas las plantillas que conozco vienen de serie con dos o tres datos que arrojan errores. Son muy típicos:

Error: Page contains property "blogid" which is not part of the schema
Error: Page contains property "postid" which is not part of the schema
Error: Page contains property "image_url" which is not part of the schema

Los dos primeros son generados por los siguientes metas, ya que ni el valor blogId ni el postId están admitidos para el atributo itemprop.

<meta expr:content='data:blog.blogId' itemprop='blogId'/> <meta expr:content='data:post.id' itemprop='postId'/>

Todo se arregla buscado estos dos metas en tu plantilla y eliminándolos.

Respecto al tercero está generado por el siguiente código:

<meta expr:content='data:post.firstImageUrl' itemprop='image_url'/>

Atención: no debes borrarlo. A diferencia del anterior en este caso la solucción no está en eliminar todo el meta. Sólo, repito, únicamente tienes que cambiar el valor image_url del atributo itemprop por image. Quedando así:

<meta expr:content='data:post.firstImageUrl' itemprop='image'/>

Microformatos hAtom

A diferencia de los datos estructurados mediante la metodología Schema.org este tipo de marcado de Microformatos hAtom usa los valores del atributo class=''.

Es el responsable del error Falta Author en el marcado de microformatos hAtom.

error por falta de author en marcado hAtom
error por falta de author en marcado hAtom

Por basarse en valores del atributo class y el anidamiento de elementos descendientes nada se consigue añadiendo el autor con el atributo itemprop='author', como es lógico. Además no ayuda nada el hecho de que los cambios introducidos en tu código en esta vista no se muestran instantánemente en las herramientas de webmaster de Google. (que para usarla has de estar logueado y sólo tienes acceso a la info de tu propio blog).

Sin embargo si los probamos mediante este otro enlace de las mismas herramientas de prueba de datos estructurados sí refleja los cambios al momento de hacerlo. Sólo tienes que introducir la dirección de la página que quieras testar. De aquí son las imágenes capturas de pantalla de este post.

Captura datos estructurados hAtom
Captura datos estructurados hAtom
A estas alturas de mis juegos y experimentos con el marcado de datos estructurados ya me pasaba como a ti con la lectura de este post: ambos estamos cansados de tanta extensión.

Así que corté por lo sano y busqué en la propia página del marcado hAtom un ejemplo y lo añadí tal cual al final del article que contiene cada post:

<address class='vcard'> <a class='url fn' href='ruta a G+' rel='author'>Kseso</a> </address>

Ahora a la espera de ver si desaparece el error en las Herramientas para webmasters de Google

Colofón y resultados

Mejor dejamos la evaluación para otro momento y post. Que si has llegado hasta aquí prestando un mínimo de atención a todo lo expuesto dudo que tengas ganas de más. Saturación que lo llaman. Yo también xD

Así que eso será materia para otra entrada.

avatar del Editor del blog

Ramajero Argonauta, Enredique Amanuense de CSS.
#impoCSSible inside
Dicen que, en español, EsCss es el mejor blog de CSS. Posíblemente exageren.
@Kseso EsCss Don Kseso Kseso