Analizar y depurar un fichero Disavow con Python

Contexto del fichero disavow o de desautorización de enlaces

El tema de la desautorización de enlaces es un tema un poco controvertido en el sector SEO. Google asegura que es un herramienta avanzada que hay que utilizar con cierta cautela e incluso se ha asegurado que en el 99% de los casos no es necesaria utilizarla (Aquí un poco de info del tito Mueller sobre el tema en cuestión). En mi opinión personal esto es cierto, ya que en la mayoría de los proyectos que he gestionado no ha sido algo que considere necesario y en aquellos en los que se ha aplicado, el resultado ha sido poco significativo incluso cuando se suponía que tenia un perfil de enlaces un poco «extraño» o tóxico.

Sinceramente creo que a no ser que tengas un perfil de enlaces de puro spam, hayas aplicado incorrectamente técnicas de compra de enlaces de forma masiva y sin sentido o hayas incurrido en una penalización manual, no creo que sea algo en lo que perder mucho el tiempo.

La sintaxis para crear documentos de desautorización de enlaces es la siguiente:

# Dos URLs que se deben desautorizar
http://spam.example.com/stuff/comments.html
http://spam.example.com/stuff/paid-links.html

# Un dominio que se debe desautorizar
domain:advertises.website

Después de esta opinión personal sobre la desautorización de enlaces, me gustaría comentaros un caso concreto que he visto de interés suficiente como para escribir el post (como siempre, escribo sobre situaciones reales en las que me he visto envuelto para compartir mi experiencia y cómo lo he afrontado.)

Desautorización de enlaces sin análisis previo

El caso es que me encontré en un cliente que habían subido un fichero disavow en el que se habían desautorizado un montón de dominios de forma masiva, y para mi gusto, sin ningún tipo de estudio/análisis previo. Me encontré en dicho fichero dominios desautorizados del calibre de elpais.com o elconfidencial.com, así como otros de mucha menos autoridad pero con una afinidad estratégica muy cercana al cliente.

La afinidad entre un dominio y otro nada tiene que ver con la autoridad de ambos sino más bien sobre que ambos pertenezcan a un nicho de negocio similar y compartan ciertos rasgos entre sí (keywords, perfil de backlinks, entidades tratadas, etc.). Un despacho de abogados se puede considerar muy afín a un medio digital enfocado al ámbito legal, por ejemplo.

Para mí, en este caso, sí que era necesario depurar este fichero disavow y darle un vuelta, evitando desautorizar dominios tan potentes como estos ya que efectivamente teníamos backlinks apuntando hacia nuestro dominio. Sobre el por qué alguien había desautorizado estos dominios es algo en lo que no voy a entrar pero lógicamente carecía de ningún sentido.

Dado que de forma manual es algo que nos puede llevar bastante tiempo, quería encontrar la forma en la que de un vistazo pudiera comprobar con algún KPI la importancia/relevancia/autoridad de los dominios incluidos en dicho fichero; y es aquí donde apareció la magia de Python y la API de MOZ.

Domain/Page Authority de MOZ

La autoridad de un dominio o página es una métrica más de tantas que ofrecen algunas herramientas de SEO para evaluar de algún modo la relevancia asociada a un dominio web.

La autoridad de dominio se calcula evaluando múltiples factores, incluida la vinculación de dominios raíz y el número total de enlaces, en una única puntuación de DA. Este puntaje se puede usar al comparar sitios web o rastrear la «fuerza de clasificación» de un sitio web a lo largo del tiempo

Fuente: MOZ

Aunque es una métrica a tener en cuenta, lo cierto es que tampoco debemos guiarnos únicamente con este valor para la toma de decisiones en cualquier estrategia SEO (linkbuilding u otras) que estemos llevando a cabo, sin embargo, si me parece un valor interesante a la hora de hacernos una idea sobre la potencia de un dominio que no conocemos a priori.

Mozscape: la API de MOZ

Para evaluar de un vistazo todos los dominios que se habían incluido en el fichero disavow recurrí a Python y la API de MOZ, ya que nos permiten hacer de forma gratuita unas 2.500 request/mes; un presupuesto en principio suficiente.

Lo que vamos a hacer con Python y la API de MOZ es leer nuestro fichero .txt y extraer en bulk todas las métricas de Autoridad de cada uno de los dominios o links incluidos en dicho fichero. El objetivo es evaluar si tiene sentido lo que estamos desautorizando o bien habría que eliminar de este fichero aquellas fuentes que sí queremos que nos traspasen su autoridad.

Para utilizar la API de MOZ necesitas crearte una cuenta e incluir un método de pago. Las condiciones para utilizar de forma gratuita la API son las siguientes:
- Hasta un máximo de 2.500 request/mes
- 1 petición cada 10 segundos.

Os dejo un post interesante de Daniel Heredia sobre la utilización de la API de MOZ y Python.

Análisis y depuración con Python

Flujo del script

Para hacernos un mapa mental de lo que vamos a hacer os dejo un gráfico del flujo que vamos a seguir:

Instalación de librerías necesarias

Las librerías que he utilizado son las siguientes:

import pandas as pd
from mozscape import Mozscape
import time
from tqdm import tqdm

Para instalar la de Mozscape primero he tenido que correr este código en consola:

(+ info aquí)

py -m pip install git+https://github.com/seomoz/SEOmozAPISamples.git#egg=mozscape"&"subdirectory=python

Leer el fichero disavow inicial con Python

Lo primero que vamos a hacer va a ser leer nuestro fichero disavow (nos lo tenemos que descargar) teniendo en cuenta la sintaxis que puede tener y vamos a guardar todos los dominios en una lista.

# Leemos nuestro fichero disavow actual y almacenamos todas las fuentes (dominios y/o URLs) en la lista "sources"
disavow = open('disavow.txt', 'r')
sources = []

for domain in tqdm(disavow):
    if domain[0]=='D' or domain[0]=='d':
        sources.append(domain.replace('domain:', '').replace('Domain:', '').strip())
    elif domain[0]=='\n':
        pass
    elif domain[0]=='#':
        pass
    elif domain[6]=='/':
        sources.append(domain.strip())

Una vez tenemos todos los dominios y/o links guardados en la lista «sources», tendríamos que recorrerla e ir haciendo las llamadas a la API para recoger los valores que nos interesan.

En este caso, si se trata de una URL concreta, haré una request para recoger el valor de Page Authority y si es un dominio llamaré a la métrica Domain Authority.

Como comentaba, a la versión gratuita solo es posible hacer llamadas de una en una dejando un lapso de tiempo de 10 segundos entre ellas. Sin embargo, he visto que haciendo 5/6 llamadas consecutivas la API sigue funcionando (y gratis 😛), asi que tendremos que splitear la lista original en sublistas de 5 dominios, de esa forma lo hacemos mucho más rápido.

# spliteamos la lista original en sublistas de 5 dominios
n = 5
i = 0
split_sources = [sources[i:i + n] for i in range(0, len(sources), n)]

Request a la API de MOZ

Ya con esta nueva lista, tendríamos que recorrerla e ir llamando a la API de Moz. Como he comentado, si se trata de una URL recojo la métrica de Page Authority y si es un dominio/subdominio recojo el Domain Authority.

# Credenciales de MOZ para usar la libreria
client = Mozscape(access_id, secret_key)
authorities = []


for indices in tqdm(split_sources ):
    for domain in indices:
        if domain[6]=='/':
            authority = client.urlMetrics(
                [domain],
                Mozscape.UMCols.pageAuthority)
            authorities.append(authority[0]['upa'])
        else:
            authority = client.urlMetrics(
                [domain],
                Mozscape.UMCols.domainAuthority)
            authorities.append(authority[0]['pda'])

    time.sleep(10)

Creación de Dataframe y fichero CSV

Todos los dominios ya los tengo en la lista «sources» y sus valores de autoridad en la lista «authorities» con lo que me creo un DataFrame con dichos valores, ordenados de forma ascendente según autoridad, y me lo exporto a un CSV:

df = pd.DataFrame({
    'Source': sources,
    'Source Authority': authorities
})

dfordenado = df.sort_values('Source Authority',ascending=False)
dfordenado.to_csv('moz-results.csv')

Con esto ya podremos trabajar con el fichero CSV y darle esta apariencia:

Analisis disavow python

Creación de un nuevo fichero disavow depurado

Una vez tengamos este fichero, ya podríamos hacer una depuración más consistente teniendo en cuenta los valores de autoridad, ya que podríamos ver de un vistazo el potencial de cada uno de ellos. Con esto, podríamos identificar dominios muy potentes que están incluidos y que en principio no deberíamos desautorizar. Este proceso sí recomiendo hacerlo a mano puesto que puede haber fuentes con baja autoridad que en principio no tenemos por qué desautorizar y por tanto deberíamos eliminarlas del fichero.

Una vez eliminemos aquellos que no nos interesan que estén en el nuevo fichero disavow, nos creamos un nuevo fichero CSV dejando únicamente la columna de los dominios (sin encabezados) que queremos incluir en el nuevo fichero disavow.

Algo así:

Con este fichero ya podremos crear nuestro nuevo fichero disavow:

# Usando este nuevo CSV, creo mi nuevo disavow.
filteredcsv = open('new-sources-filtered.csv', 'r')
filteredDisavow = open('newdisavow.txt', 'w')

# Strings que voy rellenando en el loop
textLinks = '#Links \n'
textDomains = '#Domains \n'

for source in filteredcsv:
    if source[6]=='/':
        textLinks += source

    else:
        textDomains += 'domain:'+source

# Escribo mi nuevo fichero disavow
filteredDisavow.write(textLinks+'\n'+textDomains)

Esto creará un nuevo fichero .txt donde ya estarían incluidos todos los dominios y URLs que aún queremos desautorizar, habiendo eliminado ya los que no nos interesan:

nuevo-fichero-disavow

Script completo para analizar nuestro fichero disavow

import pandas as pd
from mozscape import Mozscape
import time
from tqdm import tqdm

# Leemos nuestro fichero disavow actual y almacenamos todas las fuentes (dominios y/o URLs) en la lista "sources"
disavow = open('disavow.txt', 'r')
sources = []

for domain in tqdm(disavow):
    if domain[0]=='D' or domain[0]=='d':
        sources.append(domain.replace('domain:', '').replace('Domain:', '').strip())
    elif domain[0]=='\n':
        pass
    elif domain[0]=='#':
        pass
    elif domain[6]=='/':
        sources.append(domain.strip())


# Spliteamos la lista original en sublistas de 5 dominios
n = 5
i = 0
split_sources = [sources[i:i + n] for i in range(0, len(sources), n)]

# Credenciales de MOZ para usar la libreria
client = Mozscape(access_id, secret_key)

authorities = []


for indices in tqdm(split_sources ):
    for domain in indices:
        if domain[6]=='/':
            authority = client.urlMetrics(
                [domain],
                Mozscape.UMCols.pageAuthority)
            authorities.append(authority[0]['upa'])
        else:
            authority = client.urlMetrics(
                [domain],
                Mozscape.UMCols.domainAuthority)
            authorities.append(authority[0]['pda'])

    time.sleep(10)


df = pd.DataFrame({
    'Source': sources,
    'Source Authority': authorities
})

dfordenado = df.sort_values('Source Authority',ascending=False)
dfordenado.to_csv('moz-results.csv')

Y una vez tenga mi nuevo CSV depurado…

# Usando este nuevo CSV depurado, creo mi nuevo disavow.
filteredcsv = open('new-sources-filtered.csv', 'r')
filteredDisavow = open('newdisavow.txt', 'w')

# Strings que voy rellenando en el loop
textLinks = '#Links \n'
textDomains = '#Domains \n'

for source in filteredcsv:
    if source[6]=='/':
        textLinks += source

    else:
        textDomains += 'domain:'+source

# Escribo mi nuevo fichero disavow
filteredDisavow.write(textLinks+'\n'+textDomains)

Sobre el autor

Alex Romero Lopez, Consultor SEO en España
alejandro.romerolopez95@gmail.com Web Otros artículos del autor

Especialista SEO con gran foco en el área técnica. Entusiasta de la programación, en especial Python y Javascript, y la aplicación de ésta en el ámbito SEO para automatizar procesos o profundizar en ciertos ámbitos como el web scraping o el uso de APIs. He trabajado en proyectos SEO de muy diferente tamaño y sector lo que me permite obtener una perspectiva 360º de cómo trabajarlo.

1 comentario en «Analizar y depurar un fichero Disavow con Python»

Deja un comentario