D2SN/Séminaire Mémoire/Sources de données

Bases de données

modifier

Il ne manque pas de bases de données déjà constituées sur lesquelles on peut travailler directement. Leurs origines sont diverses, des recherches, des archives, des expériences. Certaines ont même été constitués à partir de l'usage d'APIs et du scraping, et on va aussi se retrouver avec une base de données quand on s'utilise d'eux. Donc les soucis au moment de constituer, traiter et utiliser une base de données sont partagées peu importe la méthode. Voici quelques uns :

  • Est-elle bien documenté ?
  • Est-elle complète ?
  • Est-elle structuré comme promis ?
  • Quel interprétation attribuer aux données ?
  • Les coquilles sont conséquentes ? Pour les questions qu'on pose ?
  • Quels biais on trouve dans sa constitution ?
  • Quels biais par rapport à nos questions ?
  • Quel décalage entre les données souhaitées et les données obtenus ?
  • Quelles interactions possibles avec d'autres bases de données ? Quels des autres questions reviennent ?

Sources de bases de données

modifier

Certains dépôts de bases de données fournissent un accès par API ou par téléchargement aux données hébergés, tandis que d'autres fournissent uniquement un accès via le Web.

Les APIs sont des accès documentées et stables, munis d'un vocabulaire avec lequel on peut demander des opérations et des données de façon directe. Quand on utilise une bibliothèques logicielle, l'ensemble de fonctions qu'on utilise est son API. Les Web APIs sont des APIs accessibles sur Internet par des requêtes distantes du type HTTP[S].

Exemple d'usage de deux APIs de Wikimédia / Wikipédia :

#!/usr/bin/python3

"""
    This script was based on :

    get_category_items.py

    MediaWiki API Demos
    Demo of `Categorymembers` module : List twenty items in a category

    MIT License
"""

import sys, requests
import pandas as pd
from numpy import datetime64


def wm_timestamp_to_datetime64(ts, unit):
    """Takes a timestamp in wikimedia format and converts it to a `datetime64` object."""
    return datetime64("{}-{}-{}T{}".format(ts[0:4], ts[4:6], ts[6:8], ts[8:10]), unit)


def datetime64_to_wm_timestamp(dt):
    """Takes a `datetime64` object and converts it to a timestamp in wikimedia format."""
    return datetime64(dt, "h").astype(str).replace("-", "").replace("T", "")


class WikiAPIExemple:
    def __init__(self, language="fr", project="wikipedia.org"):
        """Instantiates an HTTP sesssion and sets the project and language."""
        self.S = requests.Session()
        self.project = project  # wikipedia.org, wiktionary.org
        self.language = language  # fr, en, pt

    def get_categorymembers(self, category_name):
        """Given a category name, gets the pages that are members of that category."""
        url = "https://{}.{}/w/api.php".format(self.language, self.project)
        params = {
            "action": "query",
            "cmtitle": "Category:{}".format(category_name),
            "cmlimit": "20",
            "list": "categorymembers",
            "format": "json",
        }

        data = self.S.get(url=url, params=params).json()
        return data["query"]["categorymembers"]

    def get_pageviews(self, page_name, granularity="monthly"):
        """Given a page name, gets the page views for that page in either monthly or daily intervals."""
        dt_unit = "M" if granularity == "monthly" else "D"
        start = datetime64("2010-10-10", dt_unit)
        end = datetime64("2019-10-31", dt_unit)
        api_ep = (
            "https://wikimedia.org/api/rest_v1"
            + "/metrics/pageviews/per-article"
            + "/{project}/{access}/{agent}/{article}/{granularity}/{start}/{end}"
        )
        url = api_ep.format(
            project=".".join([self.language, self.project]),
            access="all-access",
            agent="user",
            article=page_name.replace(" ", "_"),
            granularity=granularity,
            start=datetime64_to_wm_timestamp(start),
            end=datetime64_to_wm_timestamp(end),
        )
        headers = {"accept": "application/json"}

        s = pd.Series(name=page_name, index=pd.DatetimeIndex([], name=granularity))
        results = self.S.get(url, headers=headers).json()
        for item in results.get("items", []):
            dt = wm_timestamp_to_datetime64(item["timestamp"], unit=dt_unit)
            s.loc[dt] = item["views"]

        return s

    def plot_pageviews(self, pageviews, fname=None):
        """Given a Series or DataFrame, plots it in a decent size and tightened layout."""
        ax = pageviews.plot(figsize=(16, 9))
        ax.figure.tight_layout()
        if fname:
            ax.figure.savefig(fname)


if __name__ == "__main__":
    """Run as a program, this script will get details from the user and make a standard plot"""

    # Input
    input_category = input("Category: ")  # i.e. "Indépendance du Brésil"
    input_granularity = input("Granularity (monthly, daily): ")
    input_fname = input("File name (if empty won't save figure): ")

    # Get pages in category and display them to the user
    wae = WikiAPIExemple("fr", "wikipedia.org")
    cat_members = wae.get_categorymembers(input_category)
    print(cat_members)

    # Build a dataframe with view counts for pages in the wiki's main namespace
    page_views = pd.DataFrame()
    for page in cat_members:
        if page["ns"] == 0:
            page_name = page["title"]
            page_views[page_name] = wae.get_pageviews(page_name, input_granularity)

    # Plot the ten pages with highest average
    sel = page_views.mean().sort_values(ascending=False).iloc[:10].index
    wae.plot_pageviews(page_views.loc[:, sel], input_fname)

Scraping

modifier

Le scraping c'est créer des robots (spiders, crawleurs) pour naviguer automatiquement des pages web et en extraire des contenus d'intérêt, tout en les enregistrant de façon structurée.

L'outil python incontournable pour faire du scraping c'est Scrapy. Voici un bon tutoriel à suivre :