Un crawler est un programme qui parcourt automatiquement le web pour collecter des informations. Dans ce tutoriel, nous allons créer un crawler en JavaScript qui cherche des noms de domaines expirés. Ceci est particulièrement utile pour les référenceurs web qui cherchent à profiter des avantages SEO des domaines expirés.
Pré-requis:
- Avoir une connaissance de base en JavaScript.
- Node.js installé sur votre machine.
Étapes du Tutoriel:
- Mise en place des dépendances
- Nous allons utiliser plusieurs modules :
https
,http
,fs
,htmlparser2
, eturl
. - Installez les dépendances avec npm :bashCopy code
npm install htmlparser2
- Nous allons utiliser plusieurs modules :
- Configuration initiale
- Définissez la configuration de base, notamment le nombre de requêtes simultanées, le nombre maximal de tentatives, le délai entre les tentatives, etc.
- Si vous souhaitez restreindre le crawler à un seul site, configurez
RESTRICT_TO_SITE
àtrue
et fournissez l’URL de départ.
- Liste noire des domaines
- Pour éviter de crawler certains sites tels que Google, Facebook, etc., nous avons créé une liste noire
BLACKLISTED_DOMAINS
. - Vous pouvez ajouter ou supprimer des domaines de cette liste selon vos besoins.
- Pour éviter de crawler certains sites tels que Google, Facebook, etc., nous avons créé une liste noire
- Fonctions utilitaires
isValidUrl(url)
: Vérifie si une URL est valide.isBlacklisted(url)
: Vérifie si une URL appartient à un domaine de la liste noire.shouldCrawlUrl(url)
: Vérifie si une URL doit être crawlée (en fonction de la restriction du site).
- Fetching des pages
- La fonction
fetchPage(url)
est responsable de la récupération du contenu HTML d’une URL donnée. - Si une erreur de type « ERR_NAME_NOT_RESOLVED » se produit (ce qui signifie que le domaine est probablement expiré), l’URL est enregistrée.
- La fonction
- Extraction des liens
extractLinks(html, baseUrl)
: extrait tous les liens d’une page web. Il convertit également les URL relatives en URL absolues.
- Crawling
- La fonction
crawl()
est le cœur du crawler. Elle parcourt les URL, vérifie si elles ont été visitées ou non, et ajoute les nouvelles URL à la file d’attente. - Le crawler s’exécute de manière récursive jusqu’à ce qu’il n’y ait plus d’URL à visiter ou jusqu’à atteindre la profondeur maximale définie.
- La fonction
- Exécution
- Lancez le crawler avec
node votreFichier.js
. - Une fois le crawling terminé, vous trouverez une liste des noms de domaines expirés dans le fichier
expired_domains.txt
.
- Lancez le crawler avec
Le code
const https = require('https');
const http = require('http');
const fs = require('fs');
const htmlparser = require('htmlparser2');
const urlModule = require('url');
const MAX_CONCURRENT_REQUESTS = 5;
const MAX_RETRIES = 1;
const RETRY_DELAY = 5000; // Delay in milliseconds (5 seconds)
const EXPIRED_DOMAINS_FILE = 'expired_domains.txt';
const MAX_DEPTH = 10; // Par exemple, pour 2 niveaux de profondeur
const START_URL = false;
if(START_URL) {
const INITIAL_HOSTNAME = new URL(START_URL).hostname;
}
const RESTRICT_TO_SITE = false; // Mettez à false pour crawler le web entier
//si START_URL est FALSE, on charge le fichier urls.txt et on le parse pour l'ajouter à urlsToVisit
let urlsToVisit = [];
if (START_URL === false) {
// Vérifier si le fichier urls.txt existe
if (fs.existsSync('urls.txt')) {
const urls = fs.readFileSync('urls.txt', 'utf8');
const urlsArray = urls.split('\n').map(url => url.trim()); // Nettoyer chaque URL
urlsArray.forEach(url => {
// Ajouter seulement les URLs valides à urlsToVisit
if (isValidUrl(url)) {
urlsToVisit.push({ url: url, depth: 0 });
}
});
} else {
console.error("Le fichier urls.txt n'existe pas.");
}
} else {
urlsToVisit = [{ url: START_URL, depth: 0 }];
}
let visitedUrls = new Set();
let checkedDomains = new Set();
// Blacklist of domain patterns
const BLACKLISTED_DOMAINS = [
/google\./,
/apple\./,
/adobe\./,
/youtube\./,
/facebook\./,
/twitter\./,
/linkedin\./,
/pinterest\./,
/bing\./,
/yahoo\./,
/instagram\./,
/amazon\./,
/tiktok\./,
/gouv\./,
// Add more as needed
];
function isBlacklisted(url) {
const hostname = urlModule.parse(url).hostname || '';
return BLACKLISTED_DOMAINS.some(pattern => pattern.test(hostname));
}
function logExpiredDomain(domain) {
fs.appendFileSync(EXPIRED_DOMAINS_FILE, domain + '\n');
}
async function fetchPage(url, retries = MAX_RETRIES) {
return new Promise((resolve, reject) => {
const requester = url.startsWith('https:') ? https : http;
requester.get(url,{
rejectUnauthorized: false // Ajoutez cette ligne pour ignorer les erreurs de certificat SSL
},(res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
resolve(data);
});
}).on('error', async (err) => {
if (err.message.includes('ERR_NAME_NOT_RESOLVED')) {
console.log(`Domain expired or inaccessible: ${url}`);
logExpiredDomain(new URL(url).hostname);
}else{
//ignore all others erros
resolve("");
}
reject(err);
});
});
}
function isValidUrl(url) {
try {
// Tentez de créer un nouvel objet URL
new URL(url);
// Assurez-vous que l'URL commence par http:// ou https://
// et qu'elle n'a pas de protocole imbriqué.
const pattern = /^https?:\/\/(?!https?:\/\/)/;
return pattern.test(url);
} catch (err) {
// Si une erreur se produit lors de la création de l'objet URL,
// cela signifie que l'URL est invalide.
return false;
}
}
function shouldCrawlUrl(url) {
const hostname = new URL(url).hostname;
return !RESTRICT_TO_SITE || hostname === INITIAL_HOSTNAME;
}
function addUrlToQueue(url, currentDepth) {
if (isValidUrl(url) && shouldCrawlUrl(url) && !visitedUrls.has(url) && currentDepth <= MAX_DEPTH) {
urlsToVisit.push({ url: url, depth: currentDepth + 1 });
}
}
function extractLinks(html, baseUrl) {
const links = [];
const parser = new htmlparser.Parser({
onopentag: (name, attribs) => {
if (name === "a" && attribs.href) {
// Convert relative URLs to absolute URLs
let absoluteUrl = urlModule.resolve(baseUrl, attribs.href);
// Check if the URL is a malformed one with nested protocols
if (absoluteUrl.includes("https://https/") || absoluteUrl.includes("http://http/")) {
return; // Skip this URL
}
// Check if the URL is valid and not blacklisted
if (isValidUrl(absoluteUrl) && !isBlacklisted(absoluteUrl)) {
links.push(absoluteUrl);
}
}
}
}, { decodeEntities: true });
parser.write(html);
parser.end();
return links;
}
async function crawl() {
while (urlsToVisit.length > 0) {
const currentUrlObj = urlsToVisit.shift();
const currentUrl = currentUrlObj.url;
const currentDepth = currentUrlObj.depth;
//console.log(`Visiting ${currentUrl}`);
const domain = new URL(currentUrl).hostname;
if (!visitedUrls.has(currentUrl)) {
if (!checkedDomains.has(domain)) {
checkedDomains.add(domain);
visitedUrls.add(currentUrl);
const html = await fetchPage(currentUrl);
const newUrls = extractLinks(html, currentUrl);
for (const newUrl of newUrls) {
if (!visitedUrls.has(newUrl)) {
addUrlToQueue(newUrl, currentDepth);
}
}
}
}
const promises = [];
for (let i = 0; i < Math.min(MAX_CONCURRENT_REQUESTS - 1, urlsToVisit.length); i++) {
const nextUrlObj = urlsToVisit.shift();
const nextUrl = nextUrlObj.url;
console.log(`Visiting ${nextUrl}`);
const nextDomain = new URL(nextUrl).hostname;
if (!visitedUrls.has(nextUrl)) {
if (!checkedDomains.has(nextDomain)) {
checkedDomains.add(nextDomain);
visitedUrls.add(nextUrl);
promises.push(fetchPage(nextUrl).then(html => extractLinks(html, nextUrl)));
}
}
}
const newUrlsArrays = await Promise.all(promises);
for (const newUrls of newUrlsArrays) {
for (const newUrl of newUrls) {
if (!visitedUrls.has(newUrl)) {
addUrlToQueue(newUrl, currentDepth + 1);
}
}
}
}
}
crawl().then(() => {
console.log('Crawling completed.');
}).catch((err) => {
console.error('An error occurred:', err);
});
Visible sur Gist
Conseils pour les référenceurs web:
- Avant d’acheter un domaine expiré, assurez-vous de vérifier son historique, son classement, son profil de backlink, etc.
- Utilisez un outil comme Wayback Machine pour voir les anciennes versions du site. Cela vous donnera une idée du contenu précédent du site.
- Faites attention aux sanctions de Google. Si un domaine a été pénalisé dans le passé, il peut ne pas être bénéfique pour le SEO.
Conclusion: Avec ce crawler, vous avez un outil puissant pour trouver des domaines expirés qui peuvent être bénéfiques pour vos efforts de référencement. Adaptez le code selon vos besoins et bonne chance dans vos recherches! Vous trouverez une version améliorée dans La Tribu™