Retour au blog
Guides
Raluca Penciuc13 avril 202314 min de lecture

De l'analyse des sentiments au marketing : les nombreux avantages du web scraping sur Twitter

De l'analyse des sentiments au marketing : les nombreux avantages du web scraping sur Twitter

Prérequis

Avant de commencer, assurons-nous de disposer des outils nécessaires.

Tout d'abord, téléchargez et installez Node.js depuis le site officiel, en veillant à utiliser la version à support à long terme (LTS). Cela installera également automatiquement Node Package Manager (NPM), que nous utiliserons pour installer d'autres dépendances.

Pour ce tutoriel, nous utiliserons Visual Studio Code comme environnement de développement intégré (IDE), mais vous pouvez utiliser n'importe quel autre IDE de votre choix. Créez un nouveau dossier pour votre projet, ouvrez le terminal et exécutez la commande suivante pour configurer un nouveau projet Node.js :

npm init -y

Cela créera un fichier package.json dans le répertoire de votre projet, qui stockera des informations sur votre projet et ses dépendances.

Ensuite, nous devons installer TypeScript et les définitions de types pour Node.js. TypeScript offre un typage statique facultatif qui aide à prévenir les erreurs dans le code. Pour ce faire, exécutez dans le terminal :

npm install typescript @types/node --save-dev

Vous pouvez vérifier l'installation en exécutant :

npx tsc --version

TypeScript utilise un fichier de configuration appelé tsconfig.json pour stocker les options du compilateur et d'autres paramètres. Pour créer ce fichier dans votre projet, exécutez la commande suivante :

npx tsc -init

Assurez-vous que la valeur de « outDir » est définie sur « dist ». De cette façon, nous séparerons les fichiers TypeScript des fichiers compilés. Vous trouverez plus d'informations sur ce fichier et ses propriétés dans la documentation officielle de TypeScript.

Créez maintenant un répertoire « src » dans votre projet, ainsi qu'un nouveau fichier « index.ts ». C'est là que nous conserverons le code de scraping. Pour exécuter du code TypeScript, vous devez d'abord le compiler. Afin de ne pas oublier cette étape supplémentaire, nous pouvons utiliser une commande personnalisée.

Rendez-vous dans le fichier « package.json » et modifiez la section « scripts » comme suit :

"scripts": {

    "test": "npx tsc && node dist/index.js"

}

Ainsi, lorsque vous exécuterez le script, il vous suffira de taper « npm run test » dans votre terminal.

Enfin, pour extraire les données du site web, nous utiliserons Puppeteer, une bibliothèque de navigateur sans interface graphique pour Node.js qui vous permet de contrôler un navigateur web et d’interagir avec des sites web par programmation. Pour l’installer, exécutez cette commande dans le terminal :

npm install puppeteer

C'est fortement recommandé si vous souhaitez vous assurer de l'exhaustivité de vos données, car de nombreux sites web contiennent aujourd'hui du contenu généré dynamiquement. Si vous êtes curieux, vous pouvez consulter la documentation de Puppeteer avant de continuer pour découvrir toutes ses capacités.

Emplacement des données

Maintenant que votre environnement est configuré, nous pouvons commencer à nous intéresser à l'extraction des données. Pour cet article, j'ai choisi d'extraire le profil Twitter de Netflix : https://twitter.com/netflix.

Nous allons extraire les données suivantes :

  • le nom du profil ;
  • le nom d'utilisateur ;
  • la biographie de l'utilisateur ;
  • la localisation de l'utilisateur ;
  • le site web de l'utilisateur ;
  • la date d'inscription de l'utilisateur ;
  • le nombre d'abonnés de l'utilisateur ;
  • le nombre d'abonnés de l'utilisateur ;
  • informations sur les tweets de l'utilisateur - nom de l'auteur - pseudonyme de l'auteur - date de publication - contenu textuel - médias (vidéos ou photos) - nombre de réponses - nombre de retweets - nombre de mentions « J'aime » - nombre de vues.

Vous pouvez voir toutes ces informations mises en évidence dans la capture d'écran ci-dessous :

Twitter profile page with highlighted fields like account name, follower count, and a tweet with media preview

En ouvrant les Outils de développement sur chacun de ces éléments, vous pourrez repérer les sélecteurs CSS que nous utiliserons pour localiser les éléments HTML. Si vous débutez avec le fonctionnement des sélecteurs CSS, n'hésitez pas à consulter ce guide pour débutants.

Extraction des données

Avant d'écrire notre script, vérifions que l'installation de Puppeteer s'est bien déroulée :

import puppeteer from 'puppeteer';

async function scrapeTwitterData(twitter_url: string): Promise<void> {

    // Launch Puppeteer

    const browser = await puppeteer.launch({

        headless: false,

    	  args: ['--start-maximized'],

    	  defaultViewport: null

    })

    // Create a new page

    const page = await browser.newPage()

    // Navigate to the target URL

    await page.goto(twitter_url)

    // Close the browser

    await browser.close()

}

scrapeTwitterData("https://twitter.com/netflix")

Ici, nous ouvrons une fenêtre de navigateur, créons une nouvelle page, accédons à notre URL cible, puis fermons le navigateur. Par souci de simplicité et pour faciliter le débogage visuel, j'ouvre la fenêtre de navigateur en mode non headless et en plein écran.

Examinons maintenant la structure du site web et extrayons progressivement la liste de données précédente :

Twitter profile page with browser devtools highlighting the HTML for the account name and handle

Au premier coup d’œil, vous avez peut-être remarqué que la structure du site web est assez complexe. Les noms de classes sont générés de manière aléatoire et très peu d’éléments HTML sont identifiés de manière unique.

Heureusement pour nous, en parcourant les éléments parents des données ciblées, nous trouvons l’attribut « data-testid ». Une recherche rapide dans le document HTML permet de confirmer que cet attribut identifie de manière unique l’élément que nous ciblons.

Par conséquent, pour extraire le nom et le pseudonyme du profil, nous allons extraire l’élément « div » dont l’attribut « data-testid » est défini sur « UserName ». Le code ressemblera à ceci :

// Extract the profile name and handle

const profileNameHandle = await page.evaluate(() => {

    const nameHandle = document.querySelector('div[data-testid="UserName"]')

    return nameHandle ? nameHandle.textContent : ""

})

const profileNameHandleComponents = profileNameHandle.split('@')

console.log("Profile name:", profileNameHandleComponents[0])

console.log("Profile handle:", '@' + profileNameHandleComponents[1])

Comme le nom et l'identifiant du profil ont le même parent, le résultat final apparaîtra concaténé. Pour y remédier, nous utilisons la méthode « split » pour séparer les données.

Twitter profile page with browser devtools highlighting the HTML for the bio section

Nous appliquons ensuite la même logique pour extraire la biographie du profil. Dans ce cas, la valeur de l'attribut « data-testid » est « UserDescription » :

// Extract the user bio

const profileBio = await page.evaluate(() => {

    const location = document.querySelector('div[data-testid="UserDescription"]')

    return location ? location.textContent : ""

})

console.log("User bio:", profileBio)

Le résultat final est décrit par la propriété « textContent » de l'élément HTML.

Twitter profile page with browser devtools highlighting the HTML for location, website, and join date fields

En passant à la section suivante des données du profil, nous trouvons la localisation, le site web et la date d’inscription sous la même structure.

// Extract the user location

const profileLocation = await page.evaluate(() => {

    const location = document.querySelector('span[data-testid="UserLocation"]')

    return location ? location.textContent : ""

})

console.log("User location:", profileLocation)

// Extract the user website

const profileWebsite = await page.evaluate(() => {

    const location = document.querySelector('a[data-testid="UserUrl"]')

    return location ? location.textContent : ""

})

console.log("User website:", profileWebsite)

// Extract the join date

const profileJoinDate = await page.evaluate(() => {

    const location = document.querySelector('span[data-testid="UserJoinDate"]')

    return location ? location.textContent : ""

})

console.log("User join date:", profileJoinDate)

Pour obtenir le nombre d'abonnés et d'abonnements, nous devons adopter une approche légèrement différente. Consultez la capture d'écran ci-dessous :

Twitter profile page with browser devtools highlighting the HTML for following and follower counts

Il n'y a pas d'attribut « data-testid » et les noms de classe sont toujours générés de manière aléatoire. Une solution serait de cibler les éléments d'ancrage, car ils fournissent un attribut « href » unique.

// Extract the following count

const profileFollowing = await page.evaluate(() => {

    const location = document.querySelector('a[href$="/following"]')

    return location ? location.textContent : ""

})

console.log("User following:", profileFollowing)

// Extract the followers count

const profileFollowers = await page.evaluate(() => {

    const location = document.querySelector('a[href$="/followers"]')

    return location ? location.textContent : ""

})

console.log("User followers:", profileFollowers)

Pour que le code fonctionne avec n'importe quel profil Twitter, nous avons défini le sélecteur CSS de manière à cibler les éléments d'ancrage dont l'attribut « href » se termine respectivement par « /following » ou « /followers ».

Passons maintenant à la liste des tweets : nous pouvons à nouveau identifier facilement chacun d'entre eux à l'aide de l'attribut « data-testid », comme indiqué ci-dessous :

Twitter timeline with browser devtools highlighting the HTML for tweet articles in the feed

Le code est identique à ce que nous avons fait jusqu'à présent, à l'exception de l'utilisation de la méthode « querySelectorAll » et de la conversion du résultat en un tableau JavaScript :

// Extract the user tweets

const userTweets = await page.evaluate(() => {

    const tweets = document.querySelectorAll('article[data-testid="tweet"]')

    const tweetsArray = Array.from(tweets)

    return tweetsArray

})

console.log("User tweets:", userTweets)

Cependant, même si le sélecteur CSS est sans aucun doute correct, vous avez peut-être remarqué que la liste obtenue est presque toujours vide. Cela s’explique par le fait que les tweets sont chargés quelques secondes après le chargement de la page.

La solution simple à ce problème consiste à ajouter un temps d'attente supplémentaire après avoir accédé à l'URL cible. Une option consiste à tester différentes durées fixes en secondes, tandis qu'une autre consiste à attendre qu'un sélecteur CSS spécifique apparaisse dans le DOM :

await page.waitForSelector('div[aria-label^="Timeline: "]')

Ici, nous demandons donc à notre script d'attendre qu'un élément « div » dont l'attribut « aria-label » commence par « Timeline: » soit visible sur la page. Le snippet précédent devrait désormais fonctionner parfaitement.

Twitter tweet header with browser devtools highlighting the HTML for the author name and timestamp

Pour continuer, nous pouvons identifier les données concernant l’auteur du tweet comme précédemment, en utilisant l’attribut « data-testid ».

Dans l'algorithme, nous allons parcourir la liste des éléments HTML et appliquer la méthode « querySelector » à chacun d'entre eux. De cette façon, nous pouvons mieux nous assurer que les sélecteurs que nous utilisons sont uniques, car le champ d'application ciblé est beaucoup plus restreint.

// Extract the user tweets

const userTweets = await page.evaluate(() => {

    const tweets = document.querySelectorAll('article[data-testid="tweet"]')

    const tweetsArray = Array.from(tweets)

    return tweetsArray.map(t => {

        const authorData = t.querySelector('div[data-testid="User-Names"]')

        const authorDataText = authorData ? authorData.textContent : ""

        const authorComponents = authorDataText.split('@')

        const authorComponents2 = authorComponents[1].split('·')

        return {

            authorName: authorComponents[0],

            authorHandle: '@' + authorComponents2[0],

            date: authorComponents2[1],

        }

    })

})

console.log("User tweets:", userTweets)

Les données concernant l'auteur apparaîtront également concaténées ici ; pour nous assurer que le résultat a du sens, nous appliquons la méthode « split » à chaque section.

Twitter timeline with browser devtools highlighting the HTML element containing the tweet text

Le contenu textuel du tweet est assez simple :

const tweetText = t.querySelector('div[data-testid="tweetText"]')
Twitter timeline with browser devtools highlighting the HTML for an attached tweet image

Pour les photos du tweet, nous allons extraire une liste d’éléments « img », dont les éléments parents sont des éléments « div » dont l’attribut « data-testid » est défini sur « tweetPhoto ». Le résultat final sera l’attribut « src » de ces éléments.

const tweetPhotos = t.querySelectorAll('div[data-testid="tweetPhoto"] > img')

const tweetPhotosArray = Array.from(tweetPhotos)

const photos = tweetPhotosArray.map(p => p.getAttribute('src'))
Twitter tweet actions row with browser devtools highlighting buttons for reply, retweet, and like counts

Et enfin, la section des statistiques du tweet. Le nombre de réponses, de retweets et de mentions « J'aime » est accessible de la même manière, via la valeur de l'attribut « aria-label », après avoir identifié l'élément grâce à l'attribut « data-testid ».

Pour obtenir le nombre de vues, nous ciblons l'élément d'ancrage dont l'attribut « aria-label » se termine par la chaîne « Views. View Tweet analytics ».

const replies = t.querySelector('div[data-testid="reply"]')

const repliesText = replies ? replies.getAttribute("aria-label") : ''

const retweets = t.querySelector('div[data-testid="retweet"]')

const retweetsText = retweets ? retweets.getAttribute("aria-label") : ''

const likes = t.querySelector('div[data-testid="like"]')

const likesText = likes ? likes.getAttribute("aria-label") : ''

const views = t.querySelector('a[aria-label$="Views. View Tweet analytics"]')

const viewsText = views ? views.getAttribute("aria-label") : ''

Comme le résultat final contiendra également des caractères, nous utilisons la méthode « split » pour extraire et renvoyer uniquement la valeur numérique. L'extrait de code complet permettant d'extraire les données des tweets est présenté ci-dessous :

// Extract the user tweets

const userTweets = await page.evaluate(() => {

    const tweets = document.querySelectorAll('article[data-testid="tweet"]')

    const tweetsArray = Array.from(tweets)

    return tweetsArray.map(t => {

        

        // Extract the tweet author, handle, and date

        const authorData = t.querySelector('div[data-testid="User-Names"]')

        const authorDataText = authorData ? authorData.textContent : ""

        const authorComponents = authorDataText.split('@')

        const authorComponents2 = authorComponents[1].split('·')

        // Extract the tweet content

        const tweetText = t.querySelector('div[data-testid="tweetText"]')

        // Extract the tweet photos

        const tweetPhotos = t.querySelectorAll('div[data-testid="tweetPhoto"] > img')

        const tweetPhotosArray = Array.from(tweetPhotos)

        const photos = tweetPhotosArray.map(p => p.getAttribute('src'))

        // Extract the tweet reply count

        const replies = t.querySelector('div[data-testid="reply"]')

        const repliesText = replies ? replies.getAttribute("aria-label") : ''

        // Extract the tweet retweet count

        const retweets = t.querySelector('div[data-testid="retweet"]')

        const retweetsText = retweets ? retweets.getAttribute("aria-label") : ''

        // Extract the tweet like count

        const likes = t.querySelector('div[data-testid="like"]')

        const likesText = likes ? likes.getAttribute("aria-label") : ''

        // Extract the tweet view count

        const views = t.querySelector('a[aria-label$="Views. View Tweet analytics"]')

        const viewsText = views ? views.getAttribute("aria-label") : ''

        return {

            authorName: authorComponents[0],

            authorHandle: '@' + authorComponents2[0],

            date: authorComponents2[1],

            text: tweetText ? tweetText.textContent : '',

            media: photos,

            replies: repliesText.split(' ')[0],

            retweets: retweetsText.split(' ')[0],

            likes: likesText.split(' ')[0],

            views: viewsText.split(' ')[0],

        }

    })

})

console.log("User tweets:", userTweets)

Une fois le script exécuté, votre terminal devrait afficher quelque chose comme ceci :

Profile name: Netflix

Profile handle: @netflix

User bio:

User location: California, USA

User website: netflix.com/ChangePlan

User join date: Joined October 2008

User following: 2,222 Following

User followers: 21.3M Followers

User tweets: [

  {

    authorName: 'best of the haunting',

    authorHandle: '@bestoffhaunting',

    date: '16 Jan',

    text: 'the haunting of hill house.',

    media: [

      'https://pbs.twimg.com/media/FmnGkCNWABoEsJE?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmnGkk0WABQdHKs?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmnGlTOWABAQBLb?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmnGlw6WABIKatX?format=jpg&name=360x360'

    ],

    replies: '607',

    retweets: '37398',

    likes: '170993',

    views: ''

  },

  {

    authorName: 'Netflix',

    authorHandle: '@netflix',

    date: '9h',

    text: 'The Glory Part 2 premieres March 10 -- FIRST LOOK:',

    media: [

  	'https://pbs.twimg.com/media/FmuPlBYagAI6bMF?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmuPlBWaEAIfKCN?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmuPlBUagAETi2Z?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmuPlBZaEAIsJM6?format=jpg&name=360x360'

    ],

    replies: '250',

    retweets: '4440',

    likes: '9405',

    views: '656347'

  },

  {

    authorName: 'Kurtwood Smith',

    authorHandle: '@tahitismith',

    date: '14h',

    text: 'Two day countdown...more stills from the show to hold you over...#That90sShow on @netflix',

    media: [

  	'https://pbs.twimg.com/media/FmtOZTGaEAAr2DF?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmtOZTFaUAI3QOR?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmtOZTGaAAEza6i?format=jpg&name=360x360',

  	'https://pbs.twimg.com/media/FmtOZTGaYAEo-Yu?format=jpg&name=360x360'

    ],

    replies: '66',

    retweets: '278',

    likes: '3067',

    views: ''

  },

  {

    authorName: 'Netflix',

    authorHandle: '@netflix',

    date: '12h',

    text: 'In 2013, Kai the Hatchet-Wielding Hitchhiker became an internet sensation   -- but that viral fame put his questionable past squarely on the radar of authorities. \n' +

  	'\n' +

  	'The Hatchet Wielding Hitchhiker is now on Netflix.',

    media: [],

    replies: '169',

    retweets: '119',

    likes: '871',

    views: '491570'

  }

]

Mise à l&#x27;échelle

Si le scraping de Twitter peut sembler facile au premier abord, le processus peut devenir plus complexe et difficile à mesure que vous développez votre projet. Le site web met en œuvre diverses techniques pour détecter et empêcher le trafic automatisé, de sorte que votre scraper à plus grande échelle commence à être limité en débit, voire bloqué.

Une façon de surmonter ces difficultés et de continuer à scraper à grande échelle consiste à utiliser une API de scraping. Ce type de services offre un moyen simple et fiable d’accéder aux données de sites web tels que twitter.com, sans avoir à créer et à maintenir votre propre scraper.

WebScrapingAPI est un exemple de ce type de produit. Son mécanisme de rotation de proxys évite complètement les blocages, et sa base de connaissances étendue permet de randomiser les données du navigateur afin qu'il ressemble à un véritable utilisateur.

La configuration est rapide et facile. Il vous suffit de créer un compte pour recevoir votre clé API. Celle-ci est accessible depuis votre tableau de bord et sert à authentifier les requêtes que vous envoyez.

Dashboard quickstart guide showing three steps: API access key, API Playground, and integration into your application

Comme vous avez déjà configuré votre environnement Node.js, nous pouvons utiliser le SDK correspondant. Exécutez la commande suivante pour l'ajouter aux dépendances de votre projet :

npm install webscrapingapi

Il ne reste plus qu'à envoyer une requête GET pour recevoir le document HTML du site web. Notez que ce n'est pas la seule façon d'accéder à l'API.

import webScrapingApiClient from 'webscrapingapi';

const client = new webScrapingApiClient("YOUR_API_KEY");

async function exampleUsage() {

    const api_params = {

        'render_js': 1,

        'proxy_type': 'residential',

        'wait_for_css': 'div[aria-label^="Timeline: "]',

        'timeout': 30000

    }

    const URL = "https://twitter.com/netflix"

    const response = await client.get(URL, api_params)

    if (response.success) {

        console.log(response.response.data)

    } else {

        console.log(response.error.response.data)

    }

}

exampleUsage();

En activant le paramètre « render_js », nous envoyons la requête à l'aide d'un navigateur sans interface graphique, comme vous l'avez fait précédemment dans ce tutoriel.

Après avoir reçu le document HTML, vous pouvez utiliser une autre bibliothèque pour extraire les données qui vous intéressent, comme Cheerio. Vous n'en avez jamais entendu parler ? Consultez ce guide pour vous aider à démarrer !

Conclusion

Cet article a présenté un guide complet sur la manière de scraper efficacement Twitter à l'aide de TypeScript. Nous avons abordé les étapes de configuration de l'environnement nécessaire, la localisation et l'extraction des données, ainsi que les utilisations potentielles de ces informations.

Twitter est une source de données précieuse pour ceux qui cherchent à mieux comprendre le sentiment des consommateurs, à surveiller les réseaux sociaux et à obtenir des informations stratégiques. Cependant, il est important de noter que l'utilisation de l'API Twitter seule peut ne pas suffire pour accéder à toutes les données dont vous avez besoin, et c'est pourquoi l'utilisation d'un scraper professionnel est une meilleure solution.

Dans l'ensemble, le web scraping de Twitter peut fournir des informations précieuses et constituer un atout majeur pour toute entreprise ou tout particulier cherchant à acquérir un avantage concurrentiel.

À propos de l'auteur
Raluca Penciuc, Développeur full-stack @ WebScrapingAPI
Raluca PenciucDéveloppeur full-stack

Raluca Penciuc est développeuse Full Stack chez WebScrapingAPI ; elle conçoit des robots de collecte de données, améliore les techniques de contournement et recherche des moyens fiables de réduire le risque de détection sur les sites cibles.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.