De l'analyse des sentiments au marketing : Les nombreux avantages du Web Scraping Twitter
Raluca Penciuc le 13 avril 2023

Twitter est un site web populaire de microblogging et de réseautage social qui permet aux utilisateurs de publier et d'interagir avec des messages connus sous le nom de "tweets". Ces tweets peuvent contenir une grande variété d'informations, notamment du texte, des images et des liens, ce qui en fait une source précieuse de données pour divers cas d'utilisation.
Des chercheurs individuels aux entreprises, le web scraping Twitter peut avoir de nombreuses applications pratiques : suivi des tendances et de l'actualité, analyse des sentiments des consommateurs, amélioration des campagnes publicitaires, etc.
Bien que Twitter fournisse une API pour vous permettre d'accéder aux données, elle présente certaines mises en garde que vous devez connaître :
- la limitation du débit : vous ne pouvez effectuer qu'un certain nombre de demandes au cours d'une période donnée. Si vous dépassez ces limites, votre accès à l'API peut être temporairement suspendu ;
- disponibilité des données : vous avez accès à un ensemble limité de données, telles que les tweets, les profils d'utilisateurs et les messages directs. Certaines données, comme les tweets supprimés, ne sont pas disponibles via l'API.
Dans cet article, nous allons discuter du processus de web scraping de Twitter en utilisant Typescript et Puppeteer. Nous aborderons la mise en place de l'environnement nécessaire, la localisation et l'extraction des données, ainsi que les utilisations potentielles de ces données.
En outre, nous verrons pourquoi il est préférable d'utiliser un scraper professionnel pour le web scraping de Twitter plutôt que d'utiliser uniquement l'API de Twitter. Cet article fournira un guide étape par étape sur la manière d'effectuer un web scraping efficace de Twitter, vous permettant ainsi de collecter facilement les données dont vous avez besoin.
Conditions préalables
Avant de commencer, assurons-nous que nous disposons des outils nécessaires.
Tout d'abord, téléchargez et installez Node.js depuis le site officiel, en veillant à utiliser la version Long-Term Support (LTS). Cela permettra également d'installer 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 tout autre IDE de votre choix. Créez un nouveau dossier pour votre projet, ouvrez le terminal et exécutez la commande suivante pour créer un nouveau projet Node.js :
npm init -y
Cela créera un fichier package.json dans le répertoire de votre projet, qui contiendra des informations sur votre projet et ses dépendances.
Ensuite, nous devons installer TypeScript et les définitions de type pour Node.js. TypeScript offre un typage statique optionnel qui permet d'éviter 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 le programme :
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 fixée à "dist". De cette manière, 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.
Maintenant, créez un répertoire "src" dans votre projet, et un nouveau fichier "index.ts". C'est ici que nous conserverons le code de scraping. Pour exécuter du code TypeScript, il faut d'abord le compiler, donc pour être sûr de ne pas oublier cette étape supplémentaire, nous pouvons utiliser une commande personnalisée.
Allez dans le fichier "package.json", et éditez la section "scripts" comme ceci :
"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 récupérer les données du site web, nous utiliserons Puppeteer, une bibliothèque de navigateur sans tête pour Node.js qui vous permet de contrôler un navigateur web et d'interagir avec des sites web de manière programmatique. Pour l'installer, lancez la commande suivante dans le terminal :
npm install puppeteer
Il est fortement recommandé lorsque vous souhaitez vous assurer de l'exhaustivité de vos données, car de nombreux sites Web contiennent aujourd'hui du contenu généré de manière dynamique. Si vous êtes curieux, vous pouvez consulter avant de continuer la documentation de Puppeteer pour voir de quoi il est capable.
Localisation des données
Maintenant que votre environnement est configuré, nous pouvons commencer à extraire les données. Pour cet article, j'ai choisi de récupérer le profil Twitter de Netflix : https://twitter.com/netflix.
Nous allons extraire les données suivantes :
- le nom du profil ;
- la poignée du profil ;
- la biographie de l'utilisateur ;
- l'emplacement de l'utilisateur ;
- le site web de l'utilisateur ;
- la date d'adhésion de l'utilisateur ;
- le nombre d'utilisateurs qui suivent l'utilisateur ;
- le nombre de followers de l'utilisateur ;
- informations sur les tweets de l'utilisateur
- nom de l'auteur
- pseudonyme de l'auteur
- date de publication
- contenu du texte
- médias (vidéos ou photos)
- nombre de réponses
- nombre de retweet
- nombre de like
- nombre de vues.
Toutes ces informations sont mises en évidence dans la capture d'écran ci-dessous :

En ouvrant les outils de développement sur chacun de ces éléments, vous pourrez remarquer les sélecteurs CSS que nous utiliserons pour localiser les éléments HTML. Si vous ne savez pas comment fonctionnent les 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, naviguons jusqu'à notre URL cible, puis fermons le navigateur. Pour des raisons de simplicité et de débogage visuel, j'ouvre la fenêtre du navigateur en mode maximisé et non sans tête.
Examinons maintenant la structure du site web et extrayons progressivement la liste de données précédente :

Dès le premier coup d'œil, vous avez peut-être remarqué que la structure du site web est assez complexe. Les noms de classe 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 naviguant dans 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 du profil et l'identifiant, nous allons extraire l'élément "div" dont l'attribut "data-testid" est défini sur "UserName". Le code se présente comme suit :
// 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])
Étant donné que le nom du profil et l'identifiant du profil ont le même parent, le résultat final semblera concaténé. Pour y remédier, nous utilisons la méthode "split" pour séparer les données.

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.

En passant à la section suivante des données du profil, nous trouvons la localisation, le site web et la date d'adhésion dans 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 de followers et de suiveurs, il faut adopter une approche légèrement différente. Regardez la capture d'écran ci-dessous :

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 consisterait à 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 soit disponible pour n'importe quel profil Twitter, nous avons défini le sélecteur CSS de manière à ce qu'il cible les éléments d'ancrage dont l'attribut "href" se termine par "/following" ou "/followers" respectivement.
En passant à la liste des tweets, nous pouvons à nouveau facilement identifier chacun d'entre eux à l'aide de l'attribut "data-testid", comme indiqué ci-dessous :

Le code n'est pas différent de 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 certainement correct, vous avez peut-être remarqué que la liste résultante 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 navigué jusqu'à l'URL cible. Une option consiste à jouer avec un nombre fixe de 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 : "]')
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. L'extrait précédent devrait maintenant fonctionner parfaitement.

Nous pouvons ensuite identifier les données relatives à l'auteur du tweet comme auparavant, en utilisant l'attribut "data-testid".
Dans l'algorithme, nous parcourons la liste des éléments HTML et appliquons la méthode "querySelector" à chacun d'entre eux. De cette manière, nous pouvons mieux nous assurer que les sélecteurs que nous utilisons sont uniques, car la portée ciblée est beaucoup plus petite.
// 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 relatives à l'auteur apparaîtront concaténées ici aussi, aussi pour s'assurer que le résultat a un sens, nous appliquons la méthode "split" à chaque section.

Le contenu textuel du tweet est assez simple :
const tweetText = t.querySelector('div[data-testid="tweetText"]')

Pour les photos du tweet, nous extrairons une liste d'éléments "img", dont les parents sont des éléments "div" avec l'attribut "data-testid" 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'))

Et enfin, la section des statistiques du tweet. Le nombre de réponses, de retweets et de likes sont accessibles de la même manière, à travers la valeur de l'attribut "aria-label", après avoir identifié l'élément avec 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$="Vues. Voir 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 la valeur numérique uniquement. L'extrait de code complet permettant d'extraire les données relatives aux 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)
Après avoir exécuté l'ensemble du script, 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'
}
]
Augmentation de l'échelle
Si le scraping de Twitter peut sembler facile au début, le processus peut devenir plus complexe et plus 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 à grande échelle commence à être limité en taux ou même bloqué.
L'une des façons de surmonter ces difficultés et de poursuivre le scraping à grande échelle consiste à utiliser une API de scraping. Ce type de service offre un moyen simple et fiable d'accéder aux données de sites web tels que twitter.com, sans qu'il soit nécessaire de construire et d'entretenir son propre scraper.
WebScrapingAPI est un exemple de ce type de produit. Son mécanisme de rotation de proxy évite complètement les blocs, et sa base de connaissances étendue permet de randomiser les données du navigateur de manière à ce qu'il ressemble à un utilisateur réel.
L'installation est simple et rapide. Il vous suffit d'ouvrir un compte pour recevoir votre clé API. Elle est accessible depuis votre tableau de bord et sert à authentifier les demandes que vous envoyez.

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 tête, 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 d'effectuer un web scrape efficace de Twitter à l'aide de TypeScript. Nous avons couvert les étapes de la mise en place de l'environnement nécessaire, de la localisation et de l'extraction des données, et des utilisations potentielles de ces informations.
Twitter est une source précieuse de données pour ceux qui cherchent à connaître le sentiment des consommateurs, à surveiller les médias sociaux et à faire de la veille économique. Cependant, il est important de noter que l'utilisation de l'API Twitter seule peut ne pas être suffisante 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 Twitter peut fournir des informations précieuses et constituer un atout majeur pour toute entreprise ou tout individu cherchant à acquérir un avantage concurrentiel.
Nouvelles et mises à jour
Restez au courant des derniers guides et nouvelles sur le web scraping en vous inscrivant à notre lettre d'information.
We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Articles connexes

Découvrez une comparaison détaillée entre Scrapy et Beautiful Soup, deux outils de scraping web de premier plan. Comprenez leurs caractéristiques, leurs avantages et leurs inconvénients, et découvrez comment ils peuvent être utilisés ensemble pour répondre aux besoins de différents projets.


Découvrez comment extraire et organiser efficacement des données pour le web scraping et l'analyse de données grâce à l'analyse de données, aux bibliothèques d'analyse HTML et aux métadonnées schema.org.


Les sélecteurs XPath sont-ils meilleurs que les sélecteurs CSS pour le web scraping ? Découvrez les points forts et les limites de chaque méthode et faites le bon choix pour votre projet !
