Aujourd’hui, on va s’attaquer à un sujet plutôt basique dans notre quête d’audits SEO complètement automatisés, et c’est Screaming Frog qu’on va utiliser pour crawler un site et en récupérer des données.
Je vais faire le code en python, puisque je suis à l’aise avec ce langage, et sur PC, parce que c’est aujourd’hui ce que je maîtrise le plus, mais comme Screaming Frog a une version cli pour Mac et Linux aussi, il est possible de faire un script similaire à destination des utilisateurs UNIX.
Pour que ça marche, il faut faire une chose indispensable :
Assurez vous que dans le dossier de l’exécutable Screaming Frog, vous ayez inséré le fichier spider.config avec les lignes suivantes :
eula.accepted=[VERSION EULA A ACCEPTER, (15 aujourd'hui)]
Quelles étapes dans mon code ?
Mon code va commencer par aller chercher le chemin qui mène à ScreamingFrogSEOSpiderCli.exe. C’est important parce que vous n’avez peut être pas installé Screaming Frog au même endroit que moi.
def find_file(volumes, filename):
for volume in volumes:
for dirpath, dirnames, filenames in os.walk(volume):
if filename in filenames:
return os.path.join(dirpath, filename)
return None
Il va également aller chercher une configuration prédéfinie que j’ai appelée “SEO Spider Config.seospiderconfig” quand je l’ai enregistrée depuis l’interface graphique de Screaming Frog. Pour être tout à fait transparent, j’ai essayé d’ouvrir .seospiderconfig pour modifier des valeurs, et pour moi c’est imbitable, je suppose que c’est un langage que je ne connais pas et je ne peux pas l’exploiter. Peut être que ça changera, je mettrai alors cet article à jour.
Je vérifie quand même qu’on est bien sur Windows, c’est pour pouvoir avoir un code adapté à terme à tous les OS.
Définissons donc quelques fonctions :
def create_hosts_list():
hosts = []
while True:
host = input("Veuillez entrer le host à vérifier (ou appuyez sur entrée pour terminer) : ")
if host == '':
break
hosts.append(host)
with open('hosts.txt', 'w') as f:
for host in hosts:
f.write(f"{host}\n")
Cette fonction va créer un fichier texte pour le ou les hosts à vérifier. Et surtout elle est simple d’utilisation : vous rentrez un host (www.exemple.com par exemple), vous faites “entrée” et un autre si vous en avez un deuxième à vérifier etc… jusqu’à ce que vous n’en ayez plus, et dans ce cas vous faites “entrée” sur une zone vide et la fonction sort de la boucle et va créer le fichier “host.txt”.
Cette fonction, je vais l’appeler dans la fonction main() que je vais définir au fur et à mesure :
def main():
confupdate = "N"
if os.path.exists("hosts.txt"):
confupdate = input("hosts.txt up to date ? Y/N: ")
if confupdate != "Y":
create_hosts_list()
Bon, ensuite, pour ma part, j’ai décidé de lancer la récupération de datas sur Babbar et Yourtext Guru avant de faire le crawl, ça me permet de récupérer des urls, dont la home, qui est (théoriquement) la page la plus courte en nombre de caractères, mais on pourrait décider de faire un autre script pour tester la home : Il suffirait sans doute d’appeler une url théorique en ajoutant https://+le host pour l’avoir, non ? Et si le site a bien fait les choses et que ce n’est pas la home, il devrait y avoir une redirection, non ? Si ce n’est pas le cas, ce sera sans doute une chose à noter au client.
En full auto, ça donne ça avec requests:
import requests
def check_home_page(host):
url = f"https://{host}"
try:
response = requests.get(url, allow_redirects=False)
if response.status_code == 200:
print(f"Host: {host} - Home Page: {response.url}")
return response.url
elif response.status_code in (301, 302):
redirect_url = response.headers.get('Location')
if redirect_url:
print(f"Host: {host} - Redirected to: {redirect_url}")
# Suivre la redirection pour obtenir la page finale
final_response = requests.get(redirect_url, allow_redirects=True)
if final_response.status_code == 200:
print(f"Final URL after redirection: {final_response.url}")
return final_response.url
else:
print(f"Failed to retrieve final URL: Status Code {final_response.status_code}")
return None
else:
print(f"Host: {host} - No Location header found for redirection")
return None
else:
print(f"Host: {host} - Status Code: {response.status_code}")
return None
except requests.RequestException as e:
print(f"Host: {host} - Error: {e}")
return None
Et j’ajoute les lignes suivante dans le main() :
homes = []
with open('hosts.txt', 'r') as file:
hosts = file.readlines()
for host in hosts:
host = host.strip()
if host:
home_page = check_home_page(host)
if home_page:
homes.append(home_page)
print(homes) # juste pour être sûr
Et sinon, avec un input user, ça donne ça :
homes = []
with open('hosts.txt', 'r') as file:
hosts = file.readlines()
for host in hosts:
host = host.strip()
if host:
home_page = input(f"Quel home pour {host} ? : ")
if home_page:
homes.append(home_page)
print(homes)
Mais bon, la version automatique devrait fonctionner (mais on ne sait jamais).
J’ai donc la liste des home sur lesquelles je vais lancer Screaming Frog, que faire ensuite ?
Comme Screaming Frog se lance en CLI, il me faut une fonction pour le lancer, avec les arguments qui correspondent. Voilà comment je résous le sujet :
import subprocess
def execute_file(filepath, args):
subprocess.run([filepath] + args, check=True)
Simple, basique, comme je vais trouver le chemin du fichier à exécuter, et que je vais rentrer les arguments, c’est tout ce dont j’ai besoin pour lancer le CLI.
Les arguments, d’ailleurs, parlons-en :
Voilà ceux dont je me sers le plus :
args = ['--crawl', home,
'--headless',
'--config', seospiderconfigpath,
'--export-tabs', 'Internal:HTML',
'--bulk-export', 'Links:All Outlinks',
'--save-report', 'Crawl Overview',
'--output-folder', sfdir,
'--save-crawl']
home, seospiderconfigpath et sfdir sont des variables : la home, parce que je veux réutiliser le code pour d’autres sites, le seospiderconfigpath, c’est la configuration dont j’ai parlé plus haut, et le sfdir, c’est le chemin où je vais stocker les datas du crawl Screaming Frog.
sfdir, c’est un peu ma structure personnelle. Vous pouvez décider de ne pas mettre cette ligne, personnellement je préfère créer un dossier pour chaque host, histoire de les ranger un peu mieux et de les retrouver facilement. (Et si vous lancez plusieurs crawl de suite, c’est important pour tout sortir).
Je vais donc définir une fonction pour lancer le crawl de screaming frog :
def run_screaming_frog_windows(home, host, error_dict):
volumes = ['C:\\', 'D:\\']
sfdir = os.path.join(host, "SFDatas")
filename = "ScreamingFrogSEOSpiderCli.exe"
seospiderconfig = "SEO Spider Config.seospiderconfig"
file_path = find_file(volumes, filename)
seospiderconfigpath = find_file(volumes, seospiderconfig)
if not os.path.isdir(sfdir):
os.makedirs(sfdir)
current_directory = os.getcwd()
crawl_desti = os.path.join(current_directory, sfdir)
root_directory = '/'
args = ['--crawl', home,
'--headless',
'--config', seospiderconfigpath,
'--export-tabs', 'Internal:HTML',
'--bulk-export', 'Links:All Outlinks',
'--save-report', 'Crawl Overview',
'--output-folder', sfdir,
'--save-crawl']
execute_file(file_path, args)
Attention, cette fonction n’est donc valable QUE SUR WINDOWS.
Le code complet :
import os
import subprocess
import requests
def run_screaming_frog_windows(home, host):
volumes = ['C:\\', 'D:\\']
sfdir = os.path.join(host, "SFDatas")
filename = "ScreamingFrogSEOSpiderCli.exe"
seospiderconfig = "SEO Spider Config.seospiderconfig"
file_path = find_file(volumes, filename)
seospiderconfigpath = find_file(volumes, seospiderconfig)
if not os.path.isdir(sfdir):
os.makedirs(sfdir)
args = ['--crawl', home,
'--headless',
'--config', seospiderconfigpath,
'--export-tabs', 'Internal:HTML',
'--bulk-export', 'Links:All Outlinks',
'--save-report', 'Crawl Overview',
'--output-folder', sfdir,
'--save-crawl']
execute_file(file_path, args)
def execute_file(filepath, args):
try:
subprocess.run([filepath] + args, check=True)
except subprocess.CalledProcessError as e:
print(f"Error executing file: {e}")
def check_home_page(host):
url = f"https://{host}"
try:
response = requests.get(url, allow_redirects=False)
if response.status_code == 200:
print(f"Host: {host} - Home Page: {response.url}")
return response.url
elif response.status_code in (301, 302):
redirect_url = response.headers.get('Location')
if redirect_url:
print(f"Host: {host} - Redirected to: {redirect_url}")
final_response = requests.get(redirect_url, allow_redirects=True)
if final_response.status_code == 200:
print(f"Final URL after redirection: {final_response.url}")
return final_response.url
else:
print(f"Failed to retrieve final URL: Status Code {final_response.status_code}")
return None
else:
print(f"Host: {host} - No Location header found for redirection")
return None
else:
print(f"Host: {host} - Status Code: {response.status_code}")
return None
except requests.RequestException as e:
print(f"Host: {host} - Error: {e}")
return None
def create_hosts_list():
hosts = []
while True:
host = input("Veuillez entrer le host à vérifier (ou appuyez sur entrée pour terminer) : ")
if host == '':
break
hosts.append(host)
with open('hosts.txt', 'w') as f:
for host in hosts:
f.write(f"{host}\n")
def find_file(volumes, filename):
for volume in volumes:
for dirpath, dirnames, filenames in os.walk(volume):
if filename in filenames:
return os.path.join(dirpath, filename)
return None
def main():
confupdate = "N"
if os.path.exists("hosts.txt"):
confupdate = input("hosts.txt up to date ? Y/N: ")
if confupdate != "Y":
create_hosts_list()
with open('hosts.txt', 'r') as file:
hosts = file.readlines()
for host in hosts:
host = host.strip()
if host:
home = check_home_page(host)
if home:
run_screaming_frog_windows(home, host)
if __name__ == "__main__":
main()
Ici, interne_html.csv, aperçu_crawl.csv et tous_les_liens_sortants.csv sont donc disponibles dans le dossier /{host}/SFDatas/
Amusez vous bien !
PS : je peux peut être être tenté de faire de même avec d’autres crawlers, faites moi signe si ça vous intéresse !