Complex Website

Lecho

Step-by-step instructions for scraping content from Lecho.

This guide provides step-by-step instructions for scraping content from Lecho, a popular website for news and articles. Because some articles on Lecho are behind a paywall, we will demonstrate how to use MrScraper's manual scraper feature to access and scrape the content using your own cookies.

Scenario

In this scenario, you need to extract article content from Lecho, including articles that are locked behind a paywall. Because the articles require authentication to access premium content, you cannot use the standard AI scraper. Instead, you will use the Manual Workflow with your own cookies to authenticate and extract the full article content, including title, subtitle, authors, publication date, and the complete article text.

Step 1: Create a New Map Scraper to List All Articles

First, we will create a map scraper to list all articles from Lecho. Here's how to do it:

  1. Navigate to your MrScraper dashboard.
  2. Choose Map Scraper.
  3. Input https://www.lecho.be/ into the URL field, then press submit to start crawl the website.
Map Scraping Result
{
  "count": 1000,
  "urls": [
    "http://www.lecho.be",
    "http://www.lecho.be/disclaimer",
    "https://abo.lecho.be/avantage-festif",
    "https://abonnement.lecho.be",
    "https://abonnement.lecho.be/business-pack",
    "https://abonnement.lecho.be/pack-corporate-grandes-entreprises",
    "https://aide.lecho.be",
    "https://api.lecho.be",
    "https://esgdistrict.lecho.be",
    "https://images.lecho.be/view",
    "https://invest.immo.lecho.be/fr/acceuil",
    "https://investisseur.lecho.be",
    "https://investisseur.lecho.be/analyse/marches-emergents/les-bourses-d-europe-de-l-est-en-plein-essor-une-opportunite-pour-les-investisseurs/10633775.html",
    "https://investisseur.lecho.be/analyse/secteur/le-prix-de-l-or-franchit-la-barre-des-4-000-dollars-vers-les-5-000-et-comment-reagir-en-tant-qu-investisseur/10623919.html",
    "https://investisseur.lecho.be/analyse/secteur/visa-et-mastercard-geants-du-monde-des-paiements-conviennent-ils-a-votre-portefeuille/10633995.html",
    "https://investisseur.lecho.be/dossier/incontournables-2024/decouvrez-demain-l-equipe-de-l-investisseur-de-belegger-et-laissez-vous-inspirer-a-finance-avenue/10633904.html",
    "https://investisseur.lecho.be/dossiers/termes-concepts/quand-vendre-une-action-decouvrez-nos-5-regles-d-or/10619711.html",
    "https://investisseur.lecho.be/dossiers/trackers/les-trackers-d-octobre-4-facons-d-investir-de-maniere-diversifiee/10633467.html",
    "https://journal.lecho.be",
    "... more urls ...",
  ]
}

Filter Article Links Using Include Patterns

As you can see, the map scraper returns a list of URLs from Lecho. But the article links are mixed up with other links. To filter out only the article links, you can follow these steps:

  1. Click the ellipsis button (three dots) at the top right corner, and select AI Scraper API Access or Select Code at the top right corner. Then toggle the Activate API trigger? to on.
  2. Copy the code provided. It will look something like this:
cURL Command
curl -X POST "https://api.app.mrscraper.com/api/v1/scrapers-ai-rerun" \
  -H "accept: application/json" \
  -H "x-api-token: Bearer" \
  -H "content-type: application/json" \
  -d '{
  "scraperId": "4f6a04d6-7dc9-4676-bddf-edd19497b1c6",
  "url": "https://www.lecho.be/",
  "maxDepth": 2,
  "maxPages": 50,
  "limit": 1000,
  "includePatterns": "",
  "excludePatterns": ""
}' \
  -k
  1. Add these regex patterns to the includePatterns field to filter only article links:
cURL Command
curl -X POST "https://api.app.mrscraper.com/api/v1/scrapers-ai-rerun" \
  -H "accept: application/json" \
  -H "x-api-token: Bearer" \
  -H "content-type: application/json" \
  -d '{
  "scraperId": "4f6a04d6-7dc9-4676-bddf-edd19497b1c6",
  "url": "https://www.lecho.be/",
  "maxDepth": 2,
  "maxPages": 50,
  "limit": 1000,
  "includePatterns": "lecho\\.be\\/[^\\/]+\\/[^\\/]+\\/[^\\/]+\\/\\d+\\.html$",
  "excludePatterns": ""
}' \
  -k

Note

The regex pattern lecho\.be\/[^\/]+\/[^\/]+\/[^\/]+\/\d+\.html$ is designed to match article URLs on Lecho. It looks for URLs that have three segments after the domain followed by a numeric identifier and ending with .html.

You can adjust the regex pattern based on the specific URL structure of the articles you want to scrape.

  1. Run the modified cURL command in your terminal or postman to get the filtered list of article links in the result page. You can also call the endpoint from the Rerun an AI Scraper Page.

You can retrieve the article by creating a new scraper that uses the link returned from the step above. But if the article is locked behind a paywall, you can use our manual scraper and use your cookie to give access to the scraper. Here's how to do it:

  1. Navigate to your MrScraper dashboard.
  2. click Scraper in the left sidebar, and click New Manual Scraper + at the top to create a new manual scraper.
  3. Input the link of the page into the URL field, then press submit.
  4. Add a Delay step, and give it 1000 milliseconds.
  5. Add an Inject JavaScript step. Give it 15 second timeout and paste this code into the code field :
Custom JavaScript to scrape Lecho
(() => {
  function waitAndClick(selector, timeout = 10000) {
    return new Promise(resolve => {
      const el = document.querySelector(selector);
      if (el) {
        el.click();
        setTimeout(resolve, 100); // reduced from 300ms
        return;
      }
      
      const observer = new MutationObserver(() => {
        const el = document.querySelector(selector);
        if (el) {
          observer.disconnect();
          el.click();
          setTimeout(resolve, 100);
        }
      });
      
      observer.observe(document.body, { childList: true, subtree: true });
      setTimeout(() => {
        observer.disconnect();
        resolve();
      }, timeout);
    });
  }

  function waitFor(selector, timeout = 10000) {
    return new Promise(resolve => {
      const el = document.querySelector(selector);
      if (el) {
        resolve(el);
        return;
      }
      
      const observer = new MutationObserver(() => {
        const el = document.querySelector(selector);
        if (el) {
          observer.disconnect();
          resolve(el);
        }
      });
      
      observer.observe(document.body, { childList: true, subtree: true });
      setTimeout(() => {
        observer.disconnect();
        resolve(null);
      }, timeout);
    });
  }

  return (async () => {
    // Click consent button if present (non-blocking)
    waitAndClick('#didomi-notice-disagree-button');

    // Wait for critical element only
    const titleElement = await waitFor('h1[itemprop="headline"]', 5000);
    
    // Extract all data synchronously (no more waiting)
    const canonicalElement = document.querySelector('link[rel="canonical"]');
    const canonicalUrl = canonicalElement?.href || null;

    const title = titleElement?.textContent.trim() || null;

    const subtitleElement = document.querySelector('.ac_paragraph.ac_paragraph--first');
    const subtitle = subtitleElement?.textContent.trim() || null;

    const dateElement = document.querySelector('.c-articlehead__detail__createddate time');
    const date = dateElement?.getAttribute('datetime')?.trim() || null;
    const readableDate = dateElement?.textContent.trim() || null;

    const authorElements = document.querySelectorAll('.c-articlehead__detail__authors [itemprop="name"]');
    const authors = Array.from(authorElements, a => a.textContent.trim()).filter(Boolean);

    const articleContainer = document.querySelector('.c-articlecontents');
    let article = '';
    let isPaywalled = false;

    if (articleContainer) {
      const paragraphs = articleContainer.querySelectorAll('.ac_paragraph, .o-articleinset-left, .o-articleinset-center');
      article = Array.from(paragraphs)
        .filter(el =>
          !el.classList.contains('c-adcontainer') &&
          !el.classList.contains('o-articleinset-center') &&
          !el.querySelector('.c-adcontainer, #blue-billy-wig, [data-react-id="next-best-offer"]')
        )
        .map(el => el.textContent.trim())
        .filter(Boolean)
        .join('\n\n');

      isPaywalled = !!articleContainer.querySelector('.ac_paragraph.paywalled-content');
    }

    return {
      canonicalUrl,
      title,
      subtitle,
      date,
      readableDate,
      authors,
      article,
      isPaywalled
    };
  })();
})();

Note

This script is tailored for the lecho.be platform. If you need to scrape a different website, please contact us to help you create the appropriate custom JavaScript.

  1. Save workflow by pressing the Save button at the bottom of the page.
  2. Press the ellipsis button (three dots) at the top right corner, and select Set Cookies Settings. Then add your Lecho cookie to the scraper.

Tip

You can get your Lecho cookie easily by using Cookie-Editor Chrome extension.

  1. Press Run Scraper to start scraping.
  2. Example output:
Scrape Result
{
  "data": {
    "canonicalUrl": "https://www.lecho.be/economie-politique/belgique/federal/le-plan-de-l-arizona-pour-remettre-100-000-malades-de-longue-duree-a-l-emploi/10637620.html",
    "title": "Le plan de l'Arizona pour remettre 100.000 malades de longue durée à l'emploi",
    "subtitle": "Une grande partie de l'effort budgétaire doit venir de la remise au travail des malades de longue durée. Frank Vandenbroucke (Vooruit) lance une quatrième vague de mesures.",
    "date": "2025-11-25 18:03",
    "readableDate": "25 novembre 2025Aujourd'hui à 18:03",
    "authors": [
      "Corentin Di Prima"
    ],
    "article": "Une grande partie de l'effort budgétaire doit venir de la remise au travail des malades de longue durée. Frank Vandenbroucke (Vooruit) lance une quatrième vague de mesures.\n\nPrès d'un quart de l'effort budgétaire visé par l'Arizona (9,2 milliards d'euros) doit provenir de la remise à l'emploi d'une partie des malades de longue durée. Autant dire que Frank Vandenbroucke et Vooruit ont en mains un fameux levier qui doit déterminer le succès ou l'échec de l'assainissement des finances publiques au niveau fédéral.\n\nPour y parvenir, le ministre de la Santé devra mettre en œuvre une 4e vague de réformes, après les trois premières entamées sous la Vivaldi et poursuivies sous cette législature. Frank Vandenbroucke entend continuer à \"responsabiliser tous les acteurs\" afin que, d'ici 2030, les projections actuelles (682.061 malades de longue durée) se traduisent dans les faits par près de 100.000 cas de moins (588.613), peut-on lire dans une note diffusée par son cabinet ce mardi.\n\nSur le plan de la prévention, une enveloppe supplémentaire de 17,9 millions d'euros est prévue pour les soins psychologiques de première ligne. Il est également question d'un \"plan burn out \"afin de mobiliser toute l'expertise et toutes les forces de notre pays\". Un renforcement du contrôle de l'obligation pour les entreprises d'élaborer un plan de réintégration des malades est également en vue.\n\nFrank Vandenbroucke annonce par ailleurs l'introduction d'une semaine de congé de naissance supplémentaire à prendre par l'un des deux partenaires, dans le cadre du déploiement progressif d'un \"crédit familial\".\n\nQu'en est-il du retour au travail des malades?\n\n\"Il y aura un renouvellement annuel obligatoire de la demande de reconnaissance de l'incapacité de longue durée.\"Frank VandenbrouckeMinistre de la Santé (Vooruit)\n\nLes médecins (et les patients) plus encadrés\n\nLe travail des médecins sera davantage encadré, annonce le ministre de la Santé. Lors de la 3e vague de mesures, la durée maximale de prescription avait été limitée à trois mois lors de la première année d'incapacité. Le gouvernement veut poursuivre dans cette voie, en limitant la durée de la première attestation à trois semaines.\n\nLes malades depuis plus d'un an devront par ailleurs consulter leur médecin au moins une fois par an, afin de déterminer si la personne peut reprendre, même partiellement, le travail. Un certificat électronique circonstancié devra être établi. \"Il y aura donc un renouvellement annuel obligatoire de la demande de reconnaissance de l'incapacité de longue durée\", sous peine de perdre ses indemnités.\n\nPar ailleurs, seuls les médecins traitants (qui gèrent le dossier médical global du patient) ou les spécialistes pourront prescrire une incapacité de travail.\n\nLes mutuelles sous la menace de sanctions\n\nL'impact de ces décisions aura un impact sur les mutuelles. Pour leur permettre de gérer l'afflux de certificats supplémentaires, il leur est demandé de revoir leur organisation \"par exemple en élargissant les mandats des membres de l'équipe multidisciplinaire du médecin-conseil.\" Les mutuelles seront auditées par l'Inami, avec \"des conséquences sur le financement\" selon des critères de performance dans le retour au travail qui restent à préciser.\n\nLe ministre de la Santé estime que les mutuelles devront réexaminer les dossiers de 218.000 malades dans plusieurs groupes cibles (jeunes, etc.) d'ici 2029, parmi lesquels 17% sortiraient de l'incapacité de travail, soit 37.000 personnes.\n\nUne entreprise remboursera aux mutuelles 30% de l'indemnité versée au travailleur malade pendant cinq mois.\n\nLes employeurs contribueront davantage\n\nCinq mesures concernent également les employeurs. Il s'agit notamment d'étendre la \"contribution de solidarité\" due par les entreprises aux 4e et 5e mois d'incapacité. Cela signifie qu'une entreprise remboursera aux mutuelles 30% de l'indemnité versée au travailleur malade pendant cinq mois. Cette cotisation tombe dès la reprise, même partielle, du travail. \"Et ces revenus reviennent également dans leur intégralité aux employeurs, via une réduction générale des charges\", assure le gouvernement.\n\nIl annonce par ailleurs une augmentation de la prime de reprise du travail versée aux employeurs, prime qui a connu peu de succès jusqu'ici. Le Fonds Retour au Travail, qui est aussi un flop jusqu'ici, fera l'objet d'une réévaluation. Enfin, un projet pilote sera lancé dans le but de favoriser la réintégration d'une personne au sein d'un même secteur d'activité.\n\n\"Nous ne maîtrisons pas tous les facteurs et il y aura toujours une augmentation naturelle due, par exemple, au relèvement de l’âge de la pension, à la suppression de la pension médicale ou encore au durcissement des règles du système de chômage. Mais cette augmentation sera en tout cas fortement réduite\" via ces mesures, estime Frank Vandenbroucke. \"C'est notre objectif.\" Réaliste?",
    "isPaywalled": true
  }
}

Get another page detail

To get another page detail, you can either follow the same steps above, or you can do it using cURL command, here's how to do it:

  1. Click the ellipsis button (three dots) at the top right corner, and select Manual Scraper API Access.
  2. Copy the cURL command provided.
  3. Replace the URL in the cURL command with the URL of the page you want to scrape.
cURL Command
curl -X POST "https://api.app.mrscraper.com/api/v1/scrapers-manual-rerun" \
  -H "accept: application/json" \
  -H "x-api-token: Token" \
  -H "content-type: application/json" \
  -d '{
  "scraperId": "<scraper_id>",
  "url": "https://www.lecho.be/avis-financiers/convocations-d-assemblee/db-vermogensfondsmandat-avis-important-convoquant-l-assemblee-generale-extraordinaire-des-actionnaires/10637970.html",
  "workflow": [
    {
      "type": "delay",
      "data": {
        "duration": 1000
      }
    },
    {
      "type": "script",
      "data": {
        "timeout": 15,
        "name": "data",
        "code": "(() => {\n  function waitAndClick(selector, timeout = 10000) {\n    return new Promise(resolve => {\n      const el = document.querySelector(selector);\n      if (el) {\n        el.click();\n        setTimeout(resolve, 100); // reduced from 300ms\n        return;\n      }\n      \n      const observer = new MutationObserver(() => {\n        const el = document.querySelector(selector);\n        if (el) {\n          observer.disconnect();\n          el.click();\n          setTimeout(resolve, 100);\n        }\n      });\n      \n      observer.observe(document.body, { childList: true, subtree: true });\n      setTimeout(() => {\n        observer.disconnect();\n        resolve();\n      }, timeout);\n    });\n  }\n\n  function waitFor(selector, timeout = 10000) {\n    return new Promise(resolve => {\n      const el = document.querySelector(selector);\n      if (el) {\n        resolve(el);\n        return;\n      }\n      \n      const observer = new MutationObserver(() => {\n        const el = document.querySelector(selector);\n        if (el) {\n          observer.disconnect();\n          resolve(el);\n        }\n      });\n      \n      observer.observe(document.body, { childList: true, subtree: true });\n      setTimeout(() => {\n        observer.disconnect();\n        resolve(null);\n      }, timeout);\n    });\n  }\n\n  return (async () => {\n    // Click consent button if present (non-blocking)\n    waitAndClick('\''#didomi-notice-disagree-button'\'');\n\n    // Wait for critical element only\n    const titleElement = await waitFor('\''h1[itemprop=\"headline\"]'\'', 5000);\n    \n    // Extract all data synchronously (no more waiting)\n    const canonicalElement = document.querySelector('\''link[rel=\"canonical\"]'\'');\n    const canonicalUrl = canonicalElement?.href || null;\n\n    const title = titleElement?.textContent.trim() || null;\n\n    const subtitleElement = document.querySelector('\''.ac_paragraph.ac_paragraph--first'\'');\n    const subtitle = subtitleElement?.textContent.trim() || null;\n\n    const dateElement = document.querySelector('\''.c-articlehead__detail__createddate time'\'');\n    const date = dateElement?.getAttribute('\''datetime'\'')?.trim() || null;\n    const readableDate = dateElement?.textContent.trim() || null;\n\n    const authorElements = document.querySelectorAll('\''.c-articlehead__detail__authors [itemprop=\"name\"]'\'');\n    const authors = Array.from(authorElements, a => a.textContent.trim()).filter(Boolean);\n\n    const articleContainer = document.querySelector('\''.c-articlecontents'\'');\n    let article = '\'''\'';\n    let isPaywalled = false;\n\n    if (articleContainer) {\n      const paragraphs = articleContainer.querySelectorAll('\''.ac_paragraph, .o-articleinset-left, .o-articleinset-center'\'');\n      article = Array.from(paragraphs)\n        .filter(el =>\n          !el.classList.contains('\''c-adcontainer'\'') &&\n          !el.classList.contains('\''o-articleinset-center'\'') &&\n          !el.querySelector('\''.c-adcontainer, #blue-billy-wig, [data-react-id=\"next-best-offer\"]'\'')\n        )\n        .map(el => el.textContent.trim())\n        .filter(Boolean)\n        .join('\''\\n\\n'\'');\n\n      isPaywalled = !!articleContainer.querySelector('\''.ac_paragraph.paywalled-content'\'');\n    }\n\n    return {\n      canonicalUrl,\n      title,\n      subtitle,\n      date,\n      readableDate,\n      authors,\n      article,\n      isPaywalled\n    };\n  })();\n})();"
      }
    }
  ],
  "record": false,
  "paginator": {
    "type": "query_pagination",
    "max_page": 1,
    "enabled": false
  }
}' \
  -k
  1. Run the cURL command in your terminal or postman to get the scrape result. You can also call the endpoint from the Rerun a Manual Scraper Page.