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é :
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>
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 :
Cliquez ensuite sur Get started sur la carte Web Scraper IDE :
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 :
- Accédez à la page de profil
- Attendez que la page se charge
- Interceptez la réponse de l’API
/UserTweets/
- 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.