Comment collecter des données sur Twitter – guide 2023

Twitter est devenu une plateforme indispensable pour recueillir des informations en temps réel sur les tendances, les actualités et le sentiment des utilisateurs. Dans cet article, nous discuterons de la façon de collecter des données sur Twitter de manière à mettre en évidence des informations exploitables et à les analyser efficacement.
18 min read
How to scrape Twitter

En tant que plateforme importante de réseaux sociaux, Twitter abrite certains des contenus les plus intéressants sur Internet et propose une quantité considérable de données qui peuvent servir aux entreprises désireuses de comprendre et d’étendre leurs marchés.

Bien que vous puissiez accéder à ces données par programmation via l’API Twitter, le débit est limité et le processus d’application prend beaucoup de temps. En outre, Twitter a récemment annoncé la fin de l’accès gratuit à l’API et a augmenté considérablement le coût de ses plans d’API, ce qui rend la méthode API inaccessible pour un grand nombre de petites et moyennes entreprises. Cependant, le web scraping peut vous aider à éviter ces désagréments et à extraire facilement ce dont vous avez besoin.

Le web scraping est le processus de capture et de stockage de gros volumes de données provenant de sites web et d’applications web à l’aide de scripts automatisés (bots). Dans cet article, vous apprendrez à collecter des données sur Twitter en utilisant Python et Selenium – association couramment employée pour le web scraping.    

Collecter des données sur Twitter avec Selenium

 

Ce tutoriel vous aidera d’abord à comprendre quelles sont les données à extraire, puis vous montrera comment le faire étape par étape.

Prérequis

Avant de commencer, vous avez besoin d’avoir une copie locale de Python installée sur votre système. La dernière distribution stable (3.11.2 au moment de la rédaction de cet article) fera l’affaire.

Une fois Python installé, vous devez installer les dépendances suivantes via pip, qui est le gestionnaire de packages officiel de Python :

  • Selenium
  • Webdriver Manager

Vous pouvez exécuter les commandes suivantes pour installer les dépendances :

pip install selenium
pip install webdriver_manager

Ce que vous allez collecter

 

Il est aussi important de décider ce qu’il faut collecter que de mettre en œuvre correctement le script de web scraping. En effet, Selenium vous donnera accès à une page web complète de l’application Twitter, page qui contient beaucoup de données et dont tous les éléments ne vous seront probablement pas utiles. Cela signifie que vous devez vous assurer que vous comprenez et définissez clairement ce que vous recherchez avant de commencer à écrire un script en Python.

Pour les besoins de ce tutoriel, vous allez extraire les données suivantes d’un profil utilisateur :

  • Nom
  • Nom d’utilisateur
  • Emplacement
  • Site web
  • Date d’inscription
  • Nombre d’abonnements
  • Nombre d’abonnés
  • Tweets

Scraping d’un profil utilisateur

 

Pour commencer à collecter des données sur une page de profil utilisateur, vous devez créer un nouveau fichier script Python nommé profile-page.py. Vous pouvez utiliser la commande suivante pour le créer sur les systèmes *nix :

touch profile-page.py

Sur les systèmes non-*nix, vous pouvez simplement créer le fichier à l’aide de votre gestionnaire de fichiers (Explorateur Windows, par exemple).

Configuration de Selenium

 

Après avoir créé un nouveau fichier script Python, vous devez importer les modules suivants dans votre script :

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

Ensuite, vous devez configurer un nouveau WebDriver Selenium (qui est fondamentalement un navigateur web automatisé que votre script va contrôler) :

driver= webdriver.Chrome(service=Service(ChromeDriverManager().install()))

Avant de charger la page web et d’en extraire des informations, vous devez définir l’URL de la page web. Comme les URL des pages de profil Twitter dépendent des noms d’utilisateur, vous devez ajouter le code suivant à votre script pour créer une URL de page de profil à partir du nom d’utilisateur considéré :

username = "bright_data"
URL = "https://twitter.com/" + username + "?lang=en"

Chargez ensuite la page web :

driver.get(URL)

Attente du chargement d’une page

 

Vous ne pouvez pas continuer à extraire les données de cette page tant qu’elles ne sont pas complètement chargées. Bien qu’il existe quelques méthodes déterministes pour savoir si une page HTML est entièrement chargée (comme la vérification de document.readyState), cela n’est pas efficace dans le cas d’une application web monopage (SPA) comme Twitter. Dans un tel cas, vous devez attendre que les appels d’API côté client soient terminés et que les données soient rendues sur la page web avant de pouvoir les collecter.  

Pour ce faire, vous devez ajouter le code suivant à votre script :

try:
    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, '[data-testid="tweetss"]')))
except WebDriverException:
    print("Tweets did not appear! Proceeding after timeout")

Ce code fait attendre le pilote web qu’un élément avec un attribut data-testid="tweet" soit chargé sur la page web avant de continuer. Nous choisissons cet élément et cet attribut particuliers parce que cet attribut ne sera présent que dans les tweets contenus sous un profil ; et si ces tweets sont chargés, cela indique que le reste de la page a également été chargé :

Attente du chargement d’une page

Remarque : vous devez faire attention lorsque vous décidez comment marquer la page comme chargée. L’extrait de code précédent fonctionne pour un profil public avec au moins un tweet. Cependant, il échoue dans tous les autres cas, et une exception WebDriverException est alors générée. Dans de tels cas, le script se poursuit au terme du délai de temporisation considéré (qui, dans ce cas, est de dix secondes).

Extraction des informations

 

Vous êtes maintenant prêt à aborder la partie la plus importante du tutoriel : extraire des informations. Toutefois, pour extraire des données de la page web chargée, vous devez apprendre la structure de la page sur laquelle vous collectez des données :

Nom

Si vous ouvrez Chrome DevTools et recherchez le code source de l’élément name sur la page, vous devriez voir quelque chose comme ceci :

<span class="css-901oao css-16my406 r-poiln3 r-bcqeeo r-qvutc0">Bright Data</span>
Extraction des informations

L’élément name est encapsulé dans une balise span et est affecté à un groupe de classes générées de façon (manifestement) aléatoire. Cela signifie que vous ne pouvez pas vous fier à ces noms de classe pour identifier la balise du conteneur de l’élément du nom de l’utilisateur sur la page de profil. Vous devrez rechercher quelque chose de statique.

Si vous montez dans la hiérarchie de la source HTML pour l’élément name, vous trouverez une balise div qui contient à la fois le nom et le nom d’utilisateur (sous plusieurs couches de spans). La balise de départ du conteneur div ressemble à ceci :

<div class="css-1dbjc4n r-6gpygo r-14gqq1x" data-testid="UserName">

Il y a des noms de classe générés de manière aléatoire, mais aussi également un autre attribut appelé data-testid. data-testid est un attribut HTML principalement utilisé dans les tests d’interface utilisateur pour identifier et localiser des éléments HTML afin d’exécuter des tests automatisés. Vous pouvez utiliser cet attribut pour sélectionner le conteneur div contenant le nom de l’utilisateur. Cependant, cet attribut contient également le nom d’utilisateur (c’est-à-dire l’identifiant Twitter). Cela signifie que vous devez diviser le texte au niveau du retour à la ligne, puis extraire le premier élément (qui est le nom de l’utilisateur) :

name = driver.find_element(By.CSS_SELECTOR,'div[data-testid="UserName"]').text.split('\n')[0]
Bio, emplacement, site web et date d’inscription
 

De la même manière que vous avez identifié le sélecteur approprié pour l’élément de nom, vous devez trouver les sélecteurs appropriés pour les autres points de données. Vous remarquerez que les éléments de bio, d’emplacement, de site web et de date d’inscription sont tous associés à des data-testid. Cela facilite l’écriture de sélecteurs CSS pour trouver les éléments et en extraire les données :

bio = driver.find_element(By.CSS_SELECTOR,'div[data-testid="UserDescription"]').text
location = driver.find_element(By.CSS_SELECTOR,'span[data-testid="UserLocation"]').text
website = driver.find_element(By.CSS_SELECTOR,'a[data-testid="UserUrl"]').text
join_date = driver.find_element(By.CSS_SELECTOR,'span[data-testid="UserJoinDate"]').text
Nombre d’abonnés et de comptes suivis
 

Si vous regardez le nombre d’abonnés et le nombre de comptes suivis, vous remarquerez qu’ils n’ont pas de data-testid associés, ce qui signifie que vous devez faire preuve de créativité pour les identifier et les sélectionner correctement.

Le fait de remonter la hiérarchie ne nous sera d’aucune aide, car aucun de leurs parents proches n’a de valeur d’attribut statique associée. Dans ce cas, vous devez faire appel à XPath.

XPath signifie XML Path Language. Il s’agit d’un langage utilisé pour pointer (ou créer des références) vers des balises dans des documents XML. Vous pouvez écrire, à l’aide de XPath, un sélecteur qui recherche un conteneur SPAN avec le texte 'Following’, puis remonter d’un niveau dans sa hiérarchie pour en localiser le nombre (puisque le texte 'Following' et la valeur de comptage sont tous deux encapsulés dans des balises de conteneur séparées) :

following_count = driver.find_element(By.XPATH, "//span[contains(text(), 'Following')]/ancestor::a/span").text

De même, vous pouvez écrire un sélecteur basé sur XPath pour le nombre d’abonnés :

followers_count = driver.find_element(By.XPATH, "//span[contains(text(), 'Followers')]/ancestor::a/span").text
Tweets

Heureusement, chaque tweet a un conteneur parent avec une valeur data-testid égale à “tweet” (vous vous en êtes servi précédemment pour vérifier si les tweets avaient été chargés). Vous pouvez utiliser la méthode find_elements() à la place de find_element() de Selenium pour collecter tous les éléments qui correspondent au sélecteur donné :

tweets = driver.find_elements(By.CSS_SELECTOR, '[data-testid="tweet"]')

Tout imprimer

 

Pour imprimer tout ce que vous avez extrait sur le stdout, utilisez le code suivant :

print("Name\t\t: " + name)
print("Bio\t\t: " + bio)
print("Location\t: " + location)
print("Website\t\t: " + website)
print("Joined on\t: " + join_date)
print("Following count\t: " + following_count)
print("Followers count\t: " + followers_count)

Pour imprimer le contenu des tweets, vous devez parcourir tous les tweets en boucle et extraire le texte depuis le conteneur de texte du tweet (un tweet comporte d’autres éléments, tels que l’avatar, le nom d’utilisateur, l’heure et les boutons d’action, outre le contenu principal). Voici comment vous pouvez utiliser un sélecteur CSS pour ce faire :

for tweet in tweets:
    tweet_text = tweet.find_element(By.CSS_SELECTOR,'div[data-testid="tweetText"]').text
    print("Tweet text\t: " + tweet_text)

Exécutez le script à l’aide de la commande suivante :

python profile-page.py

La sortie obtenue devrait ressembler à ceci :

Name            : Bright Data
Bio             : The World's #1 Web Data Platform
Location        : We're everywhere!
Website         : brdta.com/2VQYSWC
Joined on       : Joined February 2016
Following count : 980
Followers count : 3,769
Tweet text      : Happy BOO-RIM! Our offices transformed into a spooky "Bright Fright" wonderland today. The treats were to die for and the atmosphere was frightfully fun...
Check out these bone-chilling sights:
Tweet text      : Our Bright Champions are honored each month, and today we are happy to present February's! Thank you for your exceptional work. 
Sagi Tsaeiri  (Junior BI Developer)
Or Dinoor (Compliance Manager)
Sergey Popov (R&D DevOps)
Tweet text      : Omri Orgad, Chief Customer Officer at 
@bright_data
, explores the benefits of outsourcing public web data collections for businesses using AI tools.
#WebData #ArtificialIntelligence

Click the link below to find out more
.
.
.
<output truncated>

Voici le code complet du script de web scraping :  

# import the required packages and libraries
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

# set up a new Selenium driver
driver= webdriver.Chrome(service=Service(ChromeDriverManager().install()))

# define the username of the profile to scrape and generate its URL
username = "bright_data"
URL = "https://twitter.com/" + username + "?lang=en"

# load the URL in the Selenium driver
driver.get(URL)

# wait for the webpage to be loaded
# PS: this considers a profile page to be loaded when at least one tweet has been loaded
#     it might not work well for restricted profiles or public profiles with zero tweets
try:
    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, '[data-testid="tweet"]')))
except WebDriverException:
    print("Tweets did not appear! Proceeding after timeout")

# extract the information using either CSS selectors (and data-testid) or XPath
name = driver.find_element(By.CSS_SELECTOR,'div[data-testid="UserName"]').text.split('\n')[0]
bio = driver.find_element(By.CSS_SELECTOR,'div[data-testid="UserDescription"]').text
location = driver.find_element(By.CSS_SELECTOR,'span[data-testid="UserLocation"]').text
website = driver.find_element(By.CSS_SELECTOR,'a[data-testid="UserUrl"]').text
join_date = driver.find_element(By.CSS_SELECTOR,'span[data-testid="UserJoinDate"]').text
following_count = driver.find_element(By.XPATH, "//span[contains(text(), 'Following')]/ancestor::a/span").text
followers_count = driver.find_element(By.XPATH, "//span[contains(text(), 'Followers')]/ancestor::a/span").text
tweets = driver.find_elements(By.CSS_SELECTOR, '[data-testid="tweet"]')

# print the collected information
print("Name\t\t: " + name)
print("Bio\t\t: " + bio)
print("Location\t: " + location)
print("Website\t\t: " + website)
print("Joined on\t: " + join_date)
print("Following count\t: " + following_count)
print("Followers count\t: " + followers_count)

# print each collected tweet's text
for tweet in tweets:
    tweet_text = tweet.find_element(By.CSS_SELECTOR,'div[data-testid="tweetText"]').text
    print("Tweet text\t: " + tweet_text)

Le web scraping sur Twitter avec Bright Data

Le web scraping vous donne certes beaucoup de flexibilité et de contrôle sur la façon dont vous collectez des données sur des pages web, mais il peut parfois être difficile à configurer. Dans les cas où l’application web cible charge la plupart des données de ses pages via des appels XHR après le chargement de la page statique, et où il existe très peu d’identificateurs statiques dans HTML pour localiser des éléments (comme dans le cas de Twitter), il peut être difficile de trouver la bonne configuration.

Dans de tels cas, Bright Data peut vous aider. Bright Data est une plateforme de données web qui peut vous aider à extraire d’Internet de gros volumes de données non structurées. Bright Data propose un produit pour collecter des données sur Twitter ; ce produit peut vous aider à obtenir une collection détaillée de presque tous les points de données possibles figurant sur les pages web de Twitter.  

Par exemple, les instructions suivantes vous indiquent comment extraire les données du même profil utilisateur Twitter avec Bright Data.

Commencez par naviguer jusqu’au panneau de configuration Bright Data. Cliquez sur le bouton View data products pour afficher les solutions de web scraping proposées par Bright Data :  

BroghtData CP

Cliquez ensuite sur Get started sur la carte Web Scraper IDE :  

Jeux de données et données web

Bright Data vous fournit un environnement de développement intégré de web scraper, le Web Scraper IDE, que vous pouvez utiliser pour créer votre propre web scraper à partir de zéro, ou à partir d’un modèle de base. Bright Data vous offre également une infrastructure qui se développe automatiquement et des outils de débogage intégrés pour vous aider à démarrer rapidement.  

Vous serez invité à créer un web scraper à partir de zéro ou à utiliser un modèle existant. Si vous voulez démarrer rapidement, consultez le modèle de recherche par hashtag de Twitter (que vous utiliserez ici pour obtenir la configuration initiale de votre IDE). Cliquez sur l’option de recherche par hashtag de Twitter :  

Vous devriez être en mesure de voir l’IDE complet à l’écran avec du code déjà ajouté à l’éditeur, ce qui vous permettra de commencer. Pour utiliser cet EDI pour extraire les données de pages de profil Twitter, supprimez le code existant dans l’éditeur et collez-y le code suivant :

const start_time = new Date().getTime();

block(['*.png*', '*.jpg*', '*.mp4*', '*.mp3*', '*.svg*', '*.webp*', '*.woff*']);
// Set US ip address
country('us');
// Save the response data from a browser request
tag_response('profile', /\/UserTweets/)
// Store the website's URL here
let url = new URL('https://twitter.com/' + input["Username"]);

// function initialization
async function navigate_with_wait() {
  navigate(url, { wait_until: 'domcontentloaded' });
  try {
    wait_network_idle({ ignore: [/accounts.google.com/, /twitter.com\/sw.js/, /twitter.com\/i\/jot/] })
  } catch (e) { }
}

// calling navigate_with_wait function
navigate_with_wait()

// sometimes page does not load. If the "Try again" button exists in such case, try to click it and wait for results
let try_count = 0
while (el_exists('[value="Try again"]') && try_count++ <= 5) {
  //   wait_page_idle(4000)
  if (el_exists('[value="Try again"]')) {
    try { click('[value="Try again"]', { timeout: 1e3 }) } catch (e) { }
  } else {
    if (location.href.includes(url)) break
    else navigate_2()
  }
  if (el_exists('[data-testid="empty_state_header_text"]')) navigate_2()
}

const gatherProfileInformation = (profile) => {

  // Extract tweet-related information
  let tweets = profile.data.user.result.timeline_v2.timeline.instructions[1].entries.flatMap(entry => {
    if (!entry.content.itemContent)
      return [];

    let tweet = entry.content.itemContent.tweet_results.result

    return {
      "text": tweet.legacy.full_text,
      "time": tweet.legacy.created_at,
      "id": tweet.legacy.id_str,
      "replies": tweet.legacy.reply_count,
      "retweets": tweet.legacy.retweet_count,
      "likes": tweet.legacy.favorite_count,
      "hashtags": tweet.legacy.entities?.hashtags.toString(),
      "tagged_users": tweet.legacy.entities?.user_mentions.toString(),
      "isRetweeted": tweet.legacy.retweeted,
      "views": tweet.views.count
    }
  })

  // Extract profile information from first tweet
  let profileDetails = profile.data.user.result.timeline_v2.timeline.instructions[1].entries[0].content.itemContent.tweet_results.result.core.user_results.result;

  // Prepare the final object to be collected
  let profileData = {
    "profile_name": profileDetails.legacy.name,
    "isVerified": profileDetails.legacy.verified, // Might need to swap with profileDetails.isBlueVerified
    "bio": profileDetails.legacy.description,
    "location": profileDetails.legacy.location,
    "following": profileDetails.legacy.friends_count,
    "followers": profileDetails.legacy.followers_count,
    "website_url": profileDetails.legacy.entities?.url.urls[0].display_url || "",
    "posts": profileDetails.legacy.statuses_count,
    "media_count": profileDetails.legacy.media_count,
    "profile_background_image_url": profileDetails.legacy.profile_image_url_https,
    "handle": profileDetails.legacy.screen_name,
    "collected_number_of_posts": tweets.length,
    "posts_info": tweets
  }

  // Collect the data in the IDE
  collect(profileData)

  return null;
}

try {
  if (el_is_visible('[data-testid="app-bar-close"]')) {
    click('[data-testid="app-bar-close"]');
    wait_hidden('[data-testid="app-bar-close"]');
  }
  // Scroll to the bottom of the page for all tweets to load
  scroll_to('bottom');

  // Parse the webpage data
  const { profile } = parse();

  // Collect profile information from the page
  gatherProfileInformation(profile)

} catch (e) {
  console.error(`Interaction warning (1 stage): ${e.message}`);
}

Il y a, dans le code précédent, des commentaires inline qui vous aideront à comprendre ce qui se passe. La structure de base est la suivante :  

  1. Accédez à la page de profil
  2. Attendez que la page se charge
  3. Interceptez la réponse de l’API /UserTweets/
  4. Analysez la réponse et extrayez les informations

Vous devrez supprimer les paramètres d’entrée existants et ajouter un paramètre d’entrée unique « Username » dans la section d’entrée située en bas de la page. Ensuite, vous devrez lui fournir une valeur d’entrée, par exemple « bright_data ». Exécutez ensuite le code en cliquant sur le bouton d’aperçu :  

Le résultat devrait ressembler à ceci :

Voici la réponse JSON détaillée à titre de référence :  

{
  "profile_name": "Bright Data",
  "isVerified": false,
  "bio": "The World's #1 Web Data Platform",
  "location": "We're everywhere!",
  "following": 981,
  "followers": 3970,
  "website_url": "brdta.com/2VQYSWC",
  "posts": 1749,
  "media_count": 848,
  "profile_background_image_url": "https://pbs.twimg.com/profile_images/1372153221146411008/U_ua34Q5_normal.jpg",
  "handle": "bright_data",
  "collected_number_of_posts": 40,
  "posts_info": [
    {
      "text": "This week we will sponsor and attend @neudatalab's London Data Summit 2023. @omri_orgad, our CCO, will also participate in a panel discussion on the impact of artificial intelligence on the financial services industry. \nWe look forward to seeing you there! \n#ai #financialservices https://t.co/YtVOK4NuKY",
      "time": "Mon Mar 27 14:31:22 +0000 2023",
      "id": "1640360870143315969",
      "replies": 0,
      "retweets": 1,
      "likes": 2,
      "hashtags": "[object Object],[object Object]",
      "tagged_users": "[object Object],[object Object]",
      "isRetweeted": false,
      "views": "386"
    },
    {
      "text": "Is our Web Unlocker capable of bypassing multiple anti-bot solutions? That's the question that @webscrapingclub sought to answer! \nIn their latest blog post, they share their hands-on, step-by-step challenge and their conclusions.\nRead here: https://t.co/VwxcxGMLWm",
      "time": "Thu Mar 23 11:35:32 +0000 2023",
      "id": "1638867069587566593",
      "replies": 0,
      "retweets": 2,
      "likes": 3,
      "hashtags": "",
      "tagged_users": "[object Object]",
      "isRetweeted": false,
      "views": "404"
    },
  ]
}

En plus des fonctionnalités de web scraping, Bright Data propose des jeux de données de réseaux sociaux, qui contiennent des informations très enrichies basées sur des données recueillies sur des sites de réseaux sociaux comme Twitter. Vous pouvez les utiliser pour en savoir plus sur votre public cible, découvrir des tendances, identifier des influenceurs émergents, etc.  

Conclusion

 

Dans cet article, vous avez appris à collecter des informations sur Twitter en utilisant Selenium. Bien qu’il soit possible de collecter des données de cette façon, ce n’est pas idéal car cela peut s’avérer compliqué et chronophage. Pour cette raison, vous avez également appris à utiliser Bright Data, qui est une solution plus simple pour collecter des données sur Twitter.

More from Bright Data

Datasets Icon
Get immediately structured data
Access reliable public web data for any use case. The datasets can be downloaded or delivered in a variety of formats. Subscribe to get fresh records of your preferred dataset based on a pre-defined schedule.
Web scraper IDE Icon
Build reliable web scrapers. Fast.
Build scrapers in a cloud environment with code templates and functions that speed up the development. This solution is based on Bright Data’s Web Unlocker and proxy infrastructure making it easy to scale and never get blocked.
Web Unlocker Icon
Implement an automated unlocking solution
Boost the unblocking process with fingerprint management, CAPTCHA-solving, and IP rotation. Any scraper, written in any language, can integrate it via a regular proxy interface.

Ready to get started?