Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Ce code montre comment réaliser une analyse de sentiment sur base des reviews Amazon pour un produit donné.
"""
Ce code montre comment réaliser une analyse de sentiment
sur base des reviews Amazon pour un produit donné.
"""
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import requests
from adjustText import adjust_text
from bs4 import BeautifulSoup
from pylab import rcParams
from sklearn.feature_extraction.text import CountVectorizer
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
HEADERS = {
'authority': 'www.amazon.com',
'pragma': 'no-cache',
'cache-control': 'no-cache',
'dnt': '1',
'upgrade-insecure-requests': '1',
'user-agent': ('Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36'),
'accept': ('text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,'
'image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'),
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'sec-fetch-dest': 'document',
'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8',
}
def get_amazon_reviews(product_id, output_file):
"""
Cette fonction télécharge les reviews Amazon
sur base d'un product_id et stocke le résultat
dans un fichier CSV.
"""
# La liste dans laquelle nous stockons les commentaires
reviews = []
# Boucle qui va aller de la page 1 à la page 1000
for i in range(1, 1000):
# Nous téléchargons le contenu de la page de commentaires ici
article_url = f"https://www.amazon.com/product-reviews/{product_id}/?pageNumber={i}"
html = requests.get(article_url, headers=HEADERS).text
# Nous extrayons les commentaires du HTML avec BeautifulSoup
cnt = 0
for span in BeautifulSoup(html).find_all('span'):
classes = span.get('class')
if classes and 'review-text' in classes:
cnt += 1
reviews.append(dict(
page=i,
text=span.text
))
# S'il n'y a plus de commentaires, nous arrêtons la boucle
if cnt == 0:
break
# Sauver le résultant dans un fichier CSV
this_df = pd.DataFrame(reviews)
this_df.to_csv(output_file)
def add_sentiment(input_file='cache/amazon_joby_review.csv'):
"""
Cette fonction ajouter une colonne sentiment à un
data frame sur base d'une colonne de texte nommée "text"
"""
# Nettoyage des données
this_df = pd.read_csv(input_file)
this_df = this_df.drop('Unnamed: 0', axis='columns')
this_df = this_df.loc[not this_df.text.isnull()]
# On créé le modèle d'analyse de sentiment
model = SentimentIntensityAnalyzer()
# Nous appliquons le modèle à la colonne "text" et stockons
# le résultant dans une nouvelle colonne "sentiment".
this_df['sentiment'] = np.vectorize(
lambda x: model.polarity_scores(x).get('compound')
)(this_df.text)
return this_df
def get_term_report(this_df):
"""
Cette fonction créé un rapport de distribution de termes
sur base du sentiment des commentaires.
Le texte doit se trouver dans une colonne "text" et le
sentiment dans une colonne "sentiment".
"""
# Ajouter un flag si un commentaire est positif
this_df['is_positive'] = np.vectorize(lambda x: x > 0)(this_df.sentiment)
# Créer un data frame comptant les mots
vectorizer = CountVectorizer(
min_df=50, max_df=0.5, ngram_range=(1, 2), stop_words='english')
vector = vectorizer.fit_transform(this_df.text)
word_count = pd.DataFrame(
vector.toarray(), columns=vectorizer.get_feature_names())
# Nettoyer
temp = word_count.copy()
temp['is_positive'] = this_df.is_positive
pos = temp.loc[temp.is_positive].drop(
'is_positive', axis='columns')
neg = temp.loc[not temp.is_positive].drop(
'is_positive', axis='columns')
# Concaténer les dataframes
this_report = pd.concat([
pos.sum(),
neg.sum(),
word_count.sum()
], axis='columns')
this_report.columns = ['pos', 'neg', 'total']
# Calculer leur distributions
this_report['this_pos_ratio'] = this_report.pos / this_report.total
this_report['all_pos_ratio'] = this_report.pos.sum() / this_report.total.sum()
this_report['diff_ratio'] = this_report.this_pos_ratio - this_report.all_pos_ratio
return this_report
def plot_scatter_terms(this_report, positive=True):
"""
Cette fonction crée une visualisation simple qui permet
d'identifier quels sont les termes négatifs ou positifs
pour un produit donné.
"""
# Constantes
rcParams['figure.figsize'] = 7, 7
y_col = 'diff_ratio'
n_obs = 15
if positive:
x_col, asc = 'pos', False
else:
x_col, asc = 'neg', True
# Préparer les données
tmp = this_report[[x_col, y_col]]
tmp.columns = ['Volume', 'Intensité']
tmp = tmp.sort_values(by='Intensité', ascending=asc).head(n_obs)
x_col, y_col = tmp['Volume'].tolist(), tmp['Intensité'].tolist()
labels = tmp.index.tolist()
# Construire le scatter
plt.scatter(x_col, y_col)
plt.ylabel('Intensité')
plt.xlabel('Volume')
# Ajouter des labels
annotations = []
for x_i, y_i, label in zip(x_col, y_col, labels):
annotations.append(plt.text(x_i, y_i, label))
adjust_text(annotations, x=x_col, y=y_col)
plt.show()
if __name__ == '__main__':
PRODUCT_ID = 'B074WC9YKL'
CACHE_FILE = 'cache/amazon_joby_review.csv'
# Collecter les reviews
get_amazon_reviews(PRODUCT_ID, CACHE_FILE)
# Analyser les sentiments
data_frame = add_sentiment(CACHE_FILE)
# Visualiser l'histogramme
data_frame.sentiment.plot.hist(bins=12, alpha=0.5)
# Construire un rapport de fréquence des termes
report = get_term_report(data_frame)
# Affichier les dix mots les plus lié aux commentaires négatifs
report.sort_values(by='diff_ratio', ascending=True).head(10)
# Visualiser les termes positifs
plot_scatter_terms(report, positive=True)
# Visualiser les termes négatifs
plot_scatter_terms(report, positive=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment