Nettoyage du Spam dans Google Analytics avec Python

comparatif des années

Précédemment nous avions vu comment nettoyer le spam avec les segments directement dans Google Analytics et comment nettoyer ce spam avec R.

Cette fois nous allons voir comment le faire avec Python. Pour cela nous reprendrons la méthode d’importation des données de l’API de Google Analytics avec Python, que nous avions décrite précédemment. Démarrons tout de suite le code source.

Dans l’article précédent nous avions notamment importé les données de trafic de Networking Morbihan sur 7 ans 1/2 . Les données brutes présentaient des anomalies que nous allons nettoyer ici.

Page vues brutes depuis 2011

Code Source

N’hésitez pas à copier/coller les codes sources suivants. Comme nous vous l’indiquions précédemment nous faisons tourner le programme dans l’environnement de développement Spyder.

Attention le code source démarre ici à la récupération des données utiles. Pour voir comment se connecter à Google Analytics API reportez vous à notre article précédent sur l’importation.

Récupération des données pour nettoyage :

Afin de pouvoir filtrer le spam il sera nécessaire de récupérer des dimensions : hostname, browser, fullReferrer, sourceMedium, language, landingPagePath, pagePath dans Google Analytics.

##########################################################################
# RECUPERATION DES DONNEES POUR FILTRAGE 
##########################################################################
#Pour mémoire Dimensions & Metrics Explorer 
#https://developers.google.com/analytics/devguides/reporting/core/dimsmets
#Attention le nombre de dimensions est limité à 9 et de Metrics à 10.
def get_gaPVAllYears(analytics):
  # Use the Analytics Service Object to query the Analytics Reporting API V4.
  return analytics.reports().batchGet(
      body={
        'reportRequests': [
        {
          'viewId': VIEW_ID,
           'pageSize': 100000,  #pour dépasser la limite de 1000
          'dateRanges': [{'startDate': "2011-07-01", 'endDate': "2018-12-31"}],
          'metrics': [{'expression': 'ga:pageviews'}],
          'dimensions': [{'name': 'ga:date'},
                         {'name': 'ga:hostname'},
                         {'name': 'ga:browser'},
                         {'name': 'ga:fullReferrer'},
                         {'name': 'ga:sourceMedium'},
                         {'name': 'ga:language'},
                         {'name': 'ga:landingPagePath'},
                         {'name': 'ga:pagePath'},
                         {'name': 'ga:keyword'}],
        }]
      }
  ).execute()
response = get_gaPVAllYears(analytics)
gaPVAllYears = dataframe_response(response)
gaPVAllYears.dtypes
gaPVAllYears.count()  #51717 enregistrements

Préparation des données :

Dans cette partie nous allons préparer les données pour être exploitables par la suite. Nous allons notamment créer une observation par page vue..

###############################################################################
#Etape 1 préparation des données
#changement des noms de variables pour manipuler les colonnes.
gaPVAllYears = gaPVAllYears.rename(columns={'ga:browser': 'browser',
                                              'ga:date': 'date',
                                              'ga:fullReferrer': 'fullReferrer',
                                              'ga:hostname': 'hostname',
                                              'ga:keyword': 'keyword',
                                              'ga:landingPagePath': 'landingPagePath',
                                              'ga:language': 'language',
                                              'ga:pagePath': 'pagePath',
                                              'ga:pageviews': 'pageviews',
                                              'ga:sourceMedium': 'sourceMedium'})

#creation de la variable Année à partir de ga:date
gaPVAllYears['Année'] = gaPVAllYears['date'].astype(str).str[:4]

#separation de la variable sourceMedium en source et medium
gaPVAllYears['source'] = gaPVAllYears['sourceMedium'].str.split("/",1, expand = True)[0]
gaPVAllYears['medium'] = gaPVAllYears['sourceMedium'].str.split("/",1, expand = True)[1]

#transformation date string en datetime 
gaPVAllYears.date = pd.to_datetime(gaPVAllYears.date,  format="%Y%m%d")

#replication des lignes en fonction de la valeur de pageviews (dans R : uncount) 
#i.e. : une ligne par page vue
gaPVAllYears = gaPVAllYears.reindex(gaPVAllYears.index.repeat(gaPVAllYears.pageviews))
gaPVAllYears = gaPVAllYears.reset_index(drop=True) #reindexation
gaPVAllYears.pageviews = 1 #tous les pageviews à 1 maintenant

#### Verifs
gaPVAllYears[['date', 'pageviews']]
gaPVAllYears.dtypes
gaPVAllYears.count()  #82559 enregistrements !!!! au  31/12/2018 comme dans R


###############################################################################
# Sauvegarde en csv pour éviter de faire des appels à GA
gaPVAllYears.to_csv("gaPVAllYears.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

Nettoyage des langues suspectes :

Remarque préalable : pour ceux qui n’ont pas réussi à récupérer leurs données avec l’API nous vous proposons de récupérer nos données brutes en .zip ici : https://github.com/Anakeyn/CleanSpamGAwPython/blob/master/gaPVAllYears.zip, afin de tester le reste du programme.

Dans cette partie nous allons vérifier que les langues sont bien au format ISO « langue-pays » : xxx-xxx, par exemple : fr-FR, fr-BE, es ..

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYears = pd.read_csv("gaPVAllYears.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYears.dtypes
gaPVAllYears.count()  #82559 enregistrements 
gaPVAllYears.head(20)
##############################################################################

##########################################################################
#Nettoyage des langues suspectes.
##########################################################################
pattern = "^[a-zA-Z]{2,3}([-/][a-zA-Z]{2,3})?$"
indexGoodlang = gaPVAllYears[(gaPVAllYears.language.str.contains(pat=pattern,regex=True)==True)].index
gaPVAllYearsCleanLanguage=gaPVAllYears.iloc[indexGoodlang]
gaPVAllYearsCleanLanguage.reset_index(inplace=True, drop=True)  #reindexation.
gaPVAllYearsCleanLanguage.dtypes
gaPVAllYearsCleanLanguage.count() #76733
gaPVAllYearsCleanLanguage[['date', 'pageviews']]

#creation de la dataframe daily_data par jour
dfDatePV = gaPVAllYearsCleanLanguage[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index et pageviews a pris le décompte
daily_data['date'] = daily_data.index #
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]


#Graphique pages vues par jour
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'anomalie de fin 2016 a disparu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour',
       title='suite au nettoyage des langues.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données nettoyées variable langue", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Lang.png", bbox_inches="tight", dpi=600)


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'anomalie de fin 2016 a disparu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile',
       title='suite au nettoyage des langues.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données nettoyées variable langue", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Lang-mm30.png", bbox_inches="tight", dpi=600)

###############################################################################
# Sauvegarde en csv 
gaPVAllYearsCleanLanguage.to_csv("gaPVAllYearsCL.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

Graphique Pages VUES après nettoyage des langues suspectes

PV langues nettoyées
PV langues nettoyées

Nettoyage des Ghostnames :

Les Ghostnames sont des noms de domaine qui hébergent votre code de suivi de Google Analytics (sans votre consentement en général :-(). Ici nous n’allons conserver que les sites qui sont légitimes comme monsite.com ou encore des sites de cache comme celui de Google, par exemple :
webcache.googleusercontent.com.

On enlève aussi les sous-domaines que l’on ne veut pas garder, ici pour nous loc.networking-morbihan.com .

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYearsCleanLanguage = pd.read_csv("gaPVAllYearsCL.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYearsCleanLanguage.dtypes
gaPVAllYearsCleanLanguage.count()  #76733 enregistrements 
gaPVAllYearsCleanLanguage.head(20)
##############################################################################


##########################################################################
#nettoyage des hostnames.
##########################################################################
#Pour faciliter la lecture on va créer une liste de patterns 
#on garde ceux qui nous intéressent
patternGoodHostname = ["networking-morbihan\.com", "translate\.googleusercontent\.com", 
                         "webcache\.googleusercontent\.com", 
                         "networking-morbihan\.com\.googleweblight\.com", 
                         "web\.archive\.org"]

#on regroupe en une seule pattern
pattern = '|'.join(patternGoodHostname)
indexGoodHostname =  gaPVAllYearsCleanLanguage[(gaPVAllYearsCleanLanguage.hostname.str.contains(pat=pattern,regex=True)==True)].index

gaPVAllYearsCleanHost1 = gaPVAllYearsCleanLanguage.iloc[indexGoodHostname]
gaPVAllYearsCleanHost1.reset_index(inplace=True, drop=True)  #reindexation.
gaPVAllYearsCleanHost1.dtypes
gaPVAllYearsCleanHost1.count() #76170 

#on vire loc.networking-morbihan.com qui restait
patternBadHostname = "loc\.networking-morbihan\.com"
#on garde ceux qui ne correspondent pas à la pattern attention ici ==False
indexGoodHostname = gaPVAllYearsCleanHost1[(gaPVAllYearsCleanHost1.hostname.str.contains(pat=patternBadHostname,regex=True)==False)].index
gaPVAllYearsCleanHost = gaPVAllYearsCleanHost1.iloc[indexGoodHostname]
gaPVAllYearsCleanHost.reset_index(inplace=True, drop=True)  #reindexation.
gaPVAllYearsCleanHost.dtypes
gaPVAllYearsCleanHost.count() #76159

#creation de la dataframe daily_data par jour
dfDatePV = gaPVAllYearsCleanHost[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index
daily_data['date'] = daily_data.index #
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]


#Graphique pages vues par jour
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour',
       title='suite au nettoyage des Hostnames par rapport au nettoyage des langues.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données nettoyées variable hostname", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Host.png", bbox_inches="tight", dpi=600)


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile',
       title='suite au nettoyage des Hostnames par rapport au nettoyage des langues.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données nettoyées variable hostname", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Host-mm30.png", bbox_inches="tight", dpi=600)

###############################################################################
# Sauvegarde en csv 
gaPVAllYearsCleanHost.to_csv("gaPVAllYearsCH.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

Graphique Pages VUES après nettoyage des GHOSTNAMES

Ghostnames Nettoyés
Ghostnames Nettoyés

Nettoyage des browsers suspects

Avant de virer les browsers suspects il est nécessaire de vérifier à la main ce que vous avez dans la variable browser. Cela peut changer par rapport à nous .

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYearsCleanHost = pd.read_csv("gaPVAllYearsCH.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYearsCleanHost.dtypes
gaPVAllYearsCleanHost.count()  #76159 enregistrements 
gaPVAllYearsCleanHost.head(20)
##############################################################################


##########################################################################
#nettoyage des browser suspects - peut contenir des robots crawlers
##########################################################################
#voyons ce qu'il y a dedans 
gaPVAllYearsCleanHost['browser'].value_counts()
#on vire les "curiosités" et les bots
patternBadBrowser = ["not set","Google\\.com", "en-us", 
                         "GOOG", "PagePeeker\\.com", 
                         "bot"]

#on regroupe en une seule pattern
pattern = '|'.join(patternBadBrowser)
#on garde ceux qui ne correspondent pas à la pattern attention ici ==False
indexGoodBrowser = gaPVAllYearsCleanHost[(gaPVAllYearsCleanHost.browser.str.contains(pat=pattern,regex=True)==False)].index
gaPVAllYearsCleanBrowser = gaPVAllYearsCleanHost.iloc[indexGoodBrowser]
gaPVAllYearsCleanBrowser.reset_index(inplace=True, drop=True)  #reindexation.
gaPVAllYearsCleanBrowser.dtypes
gaPVAllYearsCleanBrowser.count() #76126


#creation de la dataframe daily_data par jour
dfDatePV = gaPVAllYearsCleanBrowser[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index
daily_data['date'] = daily_data.index #
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]


#Graphique pages vues par jour
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour',
       title='suite au nettoyage des browsers suspects par rapport aux nettoyages précédents.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable browser", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Browser.png", bbox_inches="tight", dpi=600)


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile',
       title='suite au nettoyage des browsers suspects par rapport aux nettoyages précédents.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable browser", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Host-mm30.png", bbox_inches="tight", dpi=600)


###############################################################################
# Sauvegarde en csv 
gaPVAllYearsCleanBrowser.to_csv("gaPVAllYearsCB.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

Graphique Pages VUES après nettoyage de la variable Browser

Variable browser nettoyée
Variable browser nettoyée

Nettoyage des Crawlers Spammers et sources de trafic non désirées dans la variable source.

Afin de réaliser cette opération nous aurons besoin de récupérer un fichier de sites blacklistés. Nous en avons créé un que vous pouvez récupérer dans cette archive sur notre Github. Toutefois il est possible que vous soyez obligé de le compléter au vu de ce que vous avez dans vos données.

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYearsCleanBrowser = pd.read_csv("gaPVAllYearsCB.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYearsCleanBrowser.dtypes
gaPVAllYearsCleanBrowser.count()  #76126 enregistrements 
gaPVAllYearsCleanBrowser.head(20)
##############################################################################


##########################################################################
#nettoyage des Crawlers Spammers et autres sources de trafic non désirées 
#dans source
##########################################################################
gaPVAllYearsCleanBrowser['source'].value_counts()
gaPVAllYearsCleanSource = gaPVAllYearsCleanBrowser.copy() #=on fait une copie ici 
#la liste des sites et mots clés non désirés est dans un fichier que 
#nous avons créé.
dfBlacklistSites = pd.read_csv("blacklist-source-sites.csv", sep=";")
patternBadSource = dfBlacklistSites["blacksites"].tolist()

#ça plante si on le fait en une fois, on va devoir diviser en paquets
len(patternBadSource)
step = 500
steps = list(range(0, len(patternBadSource), step))
j=0
for i in steps:
    if (i+step < len(patternBadSource) ) :
        imax=i+step
    else :
        imax = len(patternBadSource)
    print("i=",i)
    print("imax=",imax)
    patternBadSourcePack = '|'.join(patternBadSource[i:imax])
    indexGoodSource = gaPVAllYearsCleanSource[(gaPVAllYearsCleanSource.source.str.contains(pat=patternBadSourcePack,regex=True)==False)].index
    print("indexGoodSource size =", indexGoodSource.size)
    gaPVAllYearsCleanSource = gaPVAllYearsCleanSource.iloc[indexGoodSource]
    gaPVAllYearsCleanSource.reset_index(inplace=True, drop=True)  #on reindexe 


gaPVAllYearsCleanSource.reset_index(inplace=True, drop=True)  #reindexation. pas sur que cela serve beaucoup ici 
###################!!!!!!!!!!!!!!!!!!!!!!!!!
gaPVAllYearsCleanSource.dtypes
gaPVAllYearsCleanSource.count() #74275 #même chose qu'avec R !!!!!! 74275 

#pour vérifier ce que l'on a dans la variable
gaPVAllYearsCleanSource['source'].value_counts()
# Sauvegarde en csv 
gaPVAllYearsCleanSource['source'].value_counts().to_csv("gaPVAllYearsCSSC.csv", sep=";")  #séparateur ; 
#########

#creation de la dataframe daily_data par jour
dfDatePV = gaPVAllYearsCleanSource[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index
daily_data['date'] = daily_data.index #
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]


#Graphique pages vues par jour
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour',
       title='suite au nettoyage des referrers suspects par rapport aux nettoyages précédents.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable source", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Source.png", bbox_inches="tight", dpi=600)


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile',
       title='suite au nettoyage des referrers suspects par rapport aux nettoyages précédents.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable source", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-Source-mm30.png", bbox_inches="tight", dpi=600)

###############################################################################
# Sauvegarde en csv 
gaPVAllYearsCleanSource.to_csv("gaPVAllYearsCS.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

Graphique Pages VUES après nettoyage de la variable SOURCE

Variable source nettoyée
Variable source nettoyée

Nettoyage des fausses pages référentes dans la variable fullReferrer

Il s’agit ici de fausses pages référentes mais sur des sites légitimes. Nous avons aussi créé un fichier blacklist-fullReferrer-Page.csv que vous pouvez récupérer sur notre Github.

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYearsCleanSource = pd.read_csv("gaPVAllYearsCS.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYearsCleanSource.dtypes
gaPVAllYearsCleanSource.count()  #74275 enregistrements 
gaPVAllYearsCleanSource.head(20)
##############################################################################


##########################################################################
#nettoyage des fausses pages référentes dans fullReferrer
##########################################################################
gaPVAllYearsCleanFullReferrer = gaPVAllYearsCleanSource.copy() #=on fait une copie ici 
#la liste des pages  non désirées est dans un fichier que 
#nous avons créé.
dfBlacklistFullReferrers = pd.read_csv("blacklist-fullRefferer-Page.csv", sep=";")
patternBadFullReferrer = dfBlacklistFullReferrers["Blackpages"].tolist()

pattern = '|'.join(patternBadFullReferrer)
indexGoodFullReferrer = gaPVAllYearsCleanFullReferrer[(gaPVAllYearsCleanFullReferrer.fullReferrer.str.contains(pat=pattern,regex=True)==False)].index

gaPVAllYearsCleanFullReferrer = gaPVAllYearsCleanFullReferrer.iloc[indexGoodFullReferrer]
gaPVAllYearsCleanFullReferrer.reset_index(inplace=True, drop=True)  #on reindexe 
gaPVAllYearsCleanFullReferrer.count() #73829 #même chose qu'avec R !!!!!! 

#creation de la dataframe daily_data par jour
dfDatePV = gaPVAllYearsCleanFullReferrer[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index
daily_data['date'] = daily_data.index #
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]


#Graphique pages vues par jour
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour',
       title='suite au nettoyage des pages référentes suspectes par rapport aux nettoyages précédents.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable fullReferrer", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-FullReferrer.png", bbox_inches="tight", dpi=600)


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile',
       title='suite au nettoyage des pages référentes suspectes par rapport aux nettoyages précédents.')
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable fullReferrer", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-FullReferrer-mm30.png", bbox_inches="tight", dpi=600)

###############################################################################
# Sauvegarde en csv 
gaPVAllYearsCleanFullReferrer.to_csv("gaPVAllYearsCFR.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

Graphique Pages VUES après nettoyage de la variable fullReFERRER

Variable fullReferrer nettoyée
Variable fullReferrer nettoyée

Nettoyage des pages d’administration

Il s’agit ici des pages d’administration de votre site Web et qui dépendent de votre CMS. Donc vous serez obligé de l’adapter. Pour nous il s’agit de WordPress.

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYearsFullReferrer = pd.read_csv("gaPVAllYearsCFR.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYearsCleanFullReferrer.dtypes
gaPVAllYearsCleanFullReferrer.count()  #73829 enregistrements 
gaPVAllYearsCleanFullReferrer.head(20)
##############################################################################

##########################################################################
#nettoyage des pages d'administration dans pagePath
##########################################################################
gaPVAllYearsCleanPagePath = gaPVAllYearsCleanFullReferrer.copy() #=on fait une copie ici 
#on vire les accès à l'administration et les pages vues depuis l'administration
patternBadPagePath = ["/wp-login\\.php", "/wp-admin/", "/cron/", "/?p=\\d"]

pattern = '|'.join(patternBadPagePath)
indexGoodPagePath  = gaPVAllYearsCleanPagePath[(gaPVAllYearsCleanPagePath.pagePath.str.contains(pat=pattern,regex=True)==False)].index
gaPVAllYearsCleanPagePath = gaPVAllYearsCleanPagePath.iloc[indexGoodPagePath]
gaPVAllYearsCleanPagePath.reset_index(inplace=True, drop=True)  #on reindexe 
gaPVAllYearsCleanPagePath.count() #73301 #même chose qu'avec R !!!!!! 

#creation de la dataframe daily_data par jour
dfDatePV = gaPVAllYearsCleanPagePath[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index
daily_data['date'] = daily_data.index #
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]


#Graphique pages vues par jour
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour',
       title="suite au nettoyage des pages d'administration par rapport aux nettoyages précédents.")
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable pagePath", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-PagePath.png", bbox_inches="tight", dpi=600)


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile',
       title="suite au nettoyage des pages d'administration par rapport aux nettoyages précédents.")
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable pagePath", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-PagePath-mm30.png", bbox_inches="tight", dpi=600)

###############################################################################
# Sauvegarde en csv 
gaPVAllYearsCleanPagePath.to_csv("gaPVAllYearsCPP.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

PAGE VUES APRES SUPPRESSION DES PAGES D’ADMINISTRATION

Pages Vues après nettoyage des pages d'administration
Pages Vues après nettoyage des pages d’administration

Nettoyage des pages dont l’entrée sur le site s’est faite via l’administration : variable landingPagePath.

Comme précédemment, la liste des pages dépend de votre CMS. pour nous il s’agit de WordPress.

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYearsCleanPagePath = pd.read_csv("gaPVAllYearsCPP.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYearsCleanPagePath.dtypes
gaPVAllYearsCleanPagePath.count()  #73301 enregistrements 
gaPVAllYearsCleanPagePath.head(20)
##############################################################################


##########################################################################
#nettoyage des pages dont l'entrée sur le site s'est faite 
#via l'administration, variable landingPagePath
##########################################################################
gaPVAllYearsCleanLandingPagePath  = gaPVAllYearsCleanPagePath.copy() #=on fait une copie ici 
patternBadLandingPagePath = ["/wp-login\\.php", "/wp-admin/", "/cron/", "/?p=\\d"]

pattern = '|'.join(patternBadLandingPagePath)
indexGoodLandingPagePath   = gaPVAllYearsCleanLandingPagePath[(gaPVAllYearsCleanLandingPagePath.landingPagePath.str.contains(pat=pattern,regex=True)==False)].index
gaPVAllYearsCleanLandingPagePath = gaPVAllYearsCleanLandingPagePath.iloc[indexGoodLandingPagePath]
gaPVAllYearsCleanLandingPagePath.reset_index(inplace=True, drop=True)  #on reindexe 
gaPVAllYearsCleanLandingPagePath.count() #72822  #même chose qu'avec R !!!!!! 

#creation de la dataframe daily_data par jour
dfDatePV = gaPVAllYearsCleanLandingPagePath[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index
daily_data['date'] = daily_data.index #recrée la colonne date.
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]


#Graphique pages vues par jour
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour',
       title="suite au nettoyage des pages d'administration référentes par rapport aux nettoyages précédents.")
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable landingPagePath", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-LandingPagePath.png", bbox_inches="tight", dpi=600)


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold')
ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile',
       title="suite au nettoyage des pages d'administration référentes par rapport aux nettoyages précédents.")
fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable landingPagePath", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-s2011-Clean-LandingPagePath-mm30.png", bbox_inches="tight", dpi=600)

###############################################################################
# Sauvegarde en csv 
gaPVAllYearsCleanLandingPagePath.to_csv("gaPVAllYearsCLPP.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

PAGES VUES APRES NETTOYAGE DE la variable landingPAGEPATH

variable landingPagePath nettoyée
variable landingPagePath nettoyée

Il s’agissait de la dernière étape. Au début nous avions 82559 observations et à la fin nous trouvons 72821 enregistrements soit près de 10000 ou 15% de spam ce qui n’est pas négligeable.

Jeu de données nettoyé

Pour finir, comparons les différentes années sur le jeu de données nettoyé.

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
gaPVAllYearsCleanLandingPagePath = pd.read_csv("gaPVAllYearsCLPP.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
gaPVAllYearsCleanLandingPagePath.dtypes
gaPVAllYearsCleanLandingPagePath.count()  #72821 enregistrements 
gaPVAllYearsCleanLandingPagePath.head(20)
##############################################################################

##########################################################################
# Jeu de données nettoyé
##########################################################################
#nom de sauvegarde plus facile à retenir :
dfPageViews = gaPVAllYearsCleanLandingPagePath.copy() #=on fait une copie ici 
###############################################################################
# Sauvegarde en csv 
dfPageViews.to_csv("dfPageViews.csv", sep=";", index=False)  #séparateur ; 
###############################################################################

#Relecture ############
myDateToParse = ['date']  #pour parser la variable date en datetime sinon object
dfPageViews = pd.read_csv("dfPageViews.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse)
#verifs
dfPageViews.dtypes
dfPageViews.count()  #72822 enregistrements 
dfPageViews.head(20)
##############################################################################
#creation de la dataframe daily_data par jour
dfDatePV = dfPageViews[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues
daily_data = dfDatePV.groupby(dfDatePV['date']).count() #
#dans l'opération précédente la date est partie dans l'index
daily_data['date'] = daily_data.index #recrée la colonne date.
daily_data['cnt_ma30'] =  daily_data['pageviews'].rolling(window=30).mean()
daily_data['Année'] = daily_data['date'].astype(str).str[:4]
daily_data['DayOfYear'] = daily_data['date'].dt.dayofyear #récupère la date du jour


#Graphique Moyenne Mobile 30 jours.
sns.set()  #paramètres esthétiques ressemble à ggplot par défaut.
fig, ax = plt.subplots()  #un seul plot 
sns.lineplot(x='DayOfYear', y='cnt_ma30', hue='Année', data= daily_data,  
                  palette=sns.color_palette("husl",n_colors=8))
fig.suptitle("Les données présentent une saisonnalité : ", fontsize=14, fontweight='bold')
ax.set(xlabel="Numéro de Jour dans l'année", ylabel='Nbre pages vues / jour en moyenne mobile',
       title="Le trafic baisse en général en été.")
fig.text(.9,-.05,"Comparatif Nbre pages vues par jour  par an moy. mob. 30 jours \n Données nettoyées", 
         fontsize=9, ha="right")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
#plt.show()
fig.savefig("PV-Comparatif-mm30.png", bbox_inches="tight", dpi=600)

# Sauvegarde en csv 
daily_data.to_csv("DailyDataCleanPython.csv", sep=";", index=False)  #séparateur ; 

##########################################################################
# MERCI pour votre attention !
##########################################################################
#on reste dans l'IDE
#if __name__ == '__main__':
#  main()

Graphique Comparatif des PAGES VUES EN MOYENNE MOBILE SUR 30 JOURS PAR ANNEES

comparatif des années
comparatif des années

Vous pouvez retrouver le code source en entier et les fichiers nécessaires dans notre Github à l’adresse https://github.com/Anakeyn/CleanSpamGAwPython

N’hésitez pas à laisser vos avis, conseils et remarques en commentaires,

A Bientôt,

Pierre

Leave a Reply

Your email address will not be published. Required fields are marked *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.