SW-2b-Python-RDFBasics

Navigation : Index | << SW-2 C# | SW-3 C# >>

RDF en Python avec rdflib - Fondamentaux

Ce notebook est un sidetrack optionnel qui presente l’equivalent Python des concepts du notebook SW-2 (dotNetRDF). Vous y decouvrirez rdflib, la bibliotheque de reference pour manipuler des graphes RDF en Python.

Objectifs d’apprentissage

A la fin de ce notebook, vous saurez : 1. Créer et manipuler des graphes RDF avec rdflib 2. Utiliser les différents types de noeuds (URI, blank nodes, litteraux) 3. Serialiser des graphes dans différents formats (Turtle, N-Triples, JSON-LD) 4. Faire la correspondance entre dotNetRDF et rdflib

Prerequis

  • SW-2-CSharp-RDFBasics recommande (pour la comprehension conceptuelle)
  • Python 3.10+

Duree estimee : 30 minutes

Note : Ce notebook se concentre sur les fondamentaux RDF. Pour SPARQL en Python, voir le sidetrack SW-4b-Python-SPARQL.


1. Installation et Imports

rdflib est le coeur de l’ecosysteme Python pour le Web Sémantique. C’est l’equivalent direct de dotNetRDF pour .NET.

# Dependances pre-provisionnees (rdflib) : voir SemanticWeb/requirements.txt ; imports dans les cellules suivantes.

Interpretation

L’option -q (quiet) reduit la verbosite de pip. Une fois installe, rdflib offre une API pythonique pour toutes les opérations RDF.

try:
    from rdflib import Graph, URIRef, Literal, BNode, Namespace
    from rdflib.namespace import RDF, RDFS, XSD, FOAF, DC

    RDFLIB_AVAILABLE = True
    print("Imports rdflib prets.")
    print(f"rdflib version : {Graph.__module__}")
    print()
    print("Namespaces standards W3C :")
    print(f"  RDF  : {RDF}")
    print(f"  RDFS : {RDFS}")
    print(f"  XSD  : {XSD}")
    print(f"  FOAF : {FOAF}")
except ImportError:
    RDFLIB_AVAILABLE = False
    print("rdflib non disponible. Installez avec : pip install rdflib")
Imports rdflib prets.
rdflib version : rdflib.graph

Namespaces standards W3C :
  RDF  : http://www.w3.org/1999/02/22-rdf-syntax-ns#
  RDFS : http://www.w3.org/2000/01/rdf-schema#
  XSD  : http://www.w3.org/2001/XMLSchema#
  FOAF : http://xmlns.com/foaf/0.1/

Interpretation

rdflib fournit des namespaces preconfigures pour les vocabulaires standards du W3C : - RDF : types et proprietes de base (rdf:type, rdf:Property) - RDFS : hiérarchie de classes/proprietes (rdfs:subClassOf, rdfs:label) - XSD : types de données XML Schema (xsd:integer, xsd:dateTime) - FOAF : description de personnes (foaf:name, foaf:knows)

Classe rdflib Equivalent dotNetRDF
Graph Graph
URIRef IUriNode
BNode IBlankNode
Literal ILiteralNode
Namespace UriFactory / PrefixMapper

2. Créer un Graphe et Ajouter des Triples

Un graphe RDF est un ensemble de triples (sujet, predicat, objet). En rdflib, on utilise la méthode add() avec un tuple Python.

if RDFLIB_AVAILABLE:
    # Create a new empty graph
    g = Graph()

    # Define a custom namespace
    EX = Namespace("http://example.org/")
    g.bind("ex", EX)
    g.bind("foaf", FOAF)

    # Add triples about a person
    g.add((EX.Alice, RDF.type, FOAF.Person))
    g.add((EX.Alice, FOAF.name, Literal("Alice Dupont")))
    g.add((EX.Alice, FOAF.age, Literal(30, datatype=XSD.integer)))
    g.add((EX.Alice, FOAF.knows, EX.Bob))

    g.add((EX.Bob, RDF.type, FOAF.Person))
    g.add((EX.Bob, FOAF.name, Literal("Bob Martin")))
    g.add((EX.Bob, FOAF.age, Literal(25, datatype=XSD.integer)))

    print(f"Nombre de triples dans le graphe : {len(g)}")
    print()

    # Iterate over all triples
    print("Triples dans le graphe :")
    for s, p, o in g:
        print(
            f"  {s.n3(g.namespace_manager)} -- {p.n3(g.namespace_manager)} --> {o.n3(g.namespace_manager)}"
        )
else:
    print("rdflib non disponible : creation de graphe ignoree.")
Nombre de triples dans le graphe : 7

Triples dans le graphe :
  ex:Bob -- foaf:name --> "Bob Martin"
  ex:Bob -- rdf:type --> foaf:Person
  ex:Alice -- foaf:name --> "Alice Dupont"
  ex:Alice -- rdf:type --> foaf:Person
  ex:Alice -- foaf:age --> "30"^^xsd:integer
  ex:Alice -- foaf:knows --> ex:Bob
  ex:Bob -- foaf:age --> "25"^^xsd:integer

Interpretation : Creation de graphe

Sortie obtenue : 7 triples decrivant deux personnes (Alice et Bob) et leur relation.

Opération Syntaxe rdflib Syntaxe dotNetRDF
Namespace personnalise EX = Namespace("http://example.org/") UriFactory.Create("http://example.org/")
Binding de prefixe g.bind("ex", EX) g.NamespaceMap.AddNamespace("ex", uri)
Ajouter un triple g.add((s, p, o)) g.Assert(new Triple(s, p, o))
Compter les triples len(g) g.Triples.Count
Iterer les triples for s, p, o in g: foreach (var t in g.Triples)

Points cles : 1. g.add() prend un tuple Python (s, p, o) - contrairement a dotNetRDF qui utilise un objet Triple 2. EX.Alice est un raccourci pour URIRef("http://example.org/Alice") 3. g.bind() associe un prefixe pour la serialisation (ex: ex:Alice au lieu de l’URI complete) 4. La méthode .n3(g.namespace_manager) formate un noeud avec les prefixes (plus lisible)


3. Types de Noeuds

RDF définit trois types de noeuds. Voici comment rdflib les represente.

if RDFLIB_AVAILABLE:
    # 1. URIRef - identifies a resource by its URI
    uri_node = URIRef("http://example.org/Alice")
    print(f"URIRef       : {uri_node}")
    print(f"  type       : {type(uri_node).__name__}")
    print()

    # 2. BNode - anonymous node (no URI)
    blank = BNode()
    print(f"BNode        : {blank}")
    print(f"  type       : {type(blank).__name__}")
    print()

    # 3. Literal - data values
    lit_simple = Literal("Hello World")
    lit_lang = Literal("Bonjour le monde", lang="fr")
    lit_int = Literal(42, datatype=XSD.integer)
    lit_float = Literal(3.14, datatype=XSD.double)
    lit_bool = Literal(True, datatype=XSD.boolean)
    lit_date = Literal("2025-01-15", datatype=XSD.date)

    print("Literal examples:")
    for name, lit in [
        ("simple", lit_simple),
        ("lang=fr", lit_lang),
        ("integer", lit_int),
        ("double", lit_float),
        ("boolean", lit_bool),
        ("date", lit_date),
    ]:
        dt = lit.datatype if lit.datatype else "(none)"
        lg = lit.language if lit.language else "(none)"
        print(f"  {name:10s} : value={lit.toPython()!r:30s}  datatype={dt}  lang={lg}")
else:
    print("rdflib non disponible : types de noeuds ignores.")
URIRef       : http://example.org/Alice
  type       : URIRef

BNode        : N48c9a5bad7364f9ebf027748a2532b32
  type       : BNode

Literal examples:
  simple     : value='Hello World'                   datatype=(none)  lang=(none)
  lang=fr    : value='Bonjour le monde'              datatype=(none)  lang=fr
  integer    : value=42                              datatype=http://www.w3.org/2001/XMLSchema#integer  lang=(none)
  double     : value=3.14                            datatype=http://www.w3.org/2001/XMLSchema#double  lang=(none)
  boolean    : value=True                            datatype=http://www.w3.org/2001/XMLSchema#boolean  lang=(none)
  date       : value=datetime.date(2025, 1, 15)      datatype=http://www.w3.org/2001/XMLSchema#date  lang=(none)

Interpretation : Types de noeuds

Type Classe rdflib Notation Turtle Equivalent dotNetRDF
URI URIRef <http://...> ou ex:Alice g.CreateUriNode(uri)
Blank Node BNode _:b0 g.CreateBlankNode()
Litteral simple Literal("texte") "texte" g.CreateLiteralNode("texte")
Litteral type Literal(42, datatype=XSD.integer) "42"^^xsd:integer g.CreateLiteralNode("42", XSD.Integer)
Litteral avec langue Literal("Bonjour", lang="fr") "Bonjour"@fr g.CreateLiteralNode("Bonjour", "fr")

Points cles : 1. La méthode .toPython() convertit un litteral rdflib en type Python natif 2. Un litteral ne peut pas avoir a la fois un datatype et un lang (contrainte RDF) 3. Les BNode recoivent un identifiant unique auto-genere


4. Serialisation - Formats de Sortie

Un graphe RDF peut etre serialise dans plusieurs formats standards.

if RDFLIB_AVAILABLE:
    # Serialize to Turtle (compact, human-readable)
    turtle_output = g.serialize(format="turtle")
    print("=== Format Turtle ===")
    print(turtle_output)
else:
    print("rdflib non disponible : serialisation Turtle ignoree.")
=== Format Turtle ===
@prefix ex: <http://example.org/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:Alice a foaf:Person ;
    foaf:age 30 ;
    foaf:knows ex:Bob ;
    foaf:name "Alice Dupont" .

ex:Bob a foaf:Person ;
    foaf:age 25 ;
    foaf:name "Bob Martin" .

Interpretation : Format Turtle

Le format Turtle est le plus lisible pour les humains : - Utilise des prefixes (ex:, foaf:) - Regroupe les proprietes d’un même sujet avec ; - Simplifie rdf:type en mot-cle a

Equivalent dotNetRDF : CompressingTurtleWriter

if RDFLIB_AVAILABLE:
    # Serialize to N-Triples (one triple per line, no abbreviations)
    nt_output = g.serialize(format="nt")
    print("=== Format N-Triples ===")
    print(nt_output)
    print()

    # Serialize to JSON-LD
    jsonld_output = g.serialize(format="json-ld", indent=2)
    print("=== Format JSON-LD ===")
    print(jsonld_output)
else:
    print("rdflib non disponible : serialisation multi-formats ignoree.")
=== Format N-Triples ===
<http://example.org/Bob> <http://xmlns.com/foaf/0.1/name> "Bob Martin" .
<http://example.org/Bob> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .
<http://example.org/Alice> <http://xmlns.com/foaf/0.1/name> "Alice Dupont" .
<http://example.org/Alice> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .
<http://example.org/Alice> <http://xmlns.com/foaf/0.1/age> "30"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/Alice> <http://xmlns.com/foaf/0.1/knows> <http://example.org/Bob> .
<http://example.org/Bob> <http://xmlns.com/foaf/0.1/age> "25"^^<http://www.w3.org/2001/XMLSchema#integer> .


=== Format JSON-LD ===
[
  {
    "@id": "http://example.org/Alice",
    "@type": [
      "http://xmlns.com/foaf/0.1/Person"
    ],
    "http://xmlns.com/foaf/0.1/age": [
      {
        "@type": "http://www.w3.org/2001/XMLSchema#integer",
        "@value": 30
      }
    ],
    "http://xmlns.com/foaf/0.1/knows": [
      {
        "@id": "http://example.org/Bob"
      }
    ],
    "http://xmlns.com/foaf/0.1/name": [
      {
        "@value": "Alice Dupont"
      }
    ]
  },
  {
    "@id": "http://example.org/Bob",
    "@type": [
      "http://xmlns.com/foaf/0.1/Person"
    ],
    "http://xmlns.com/foaf/0.1/age": [
      {
        "@type": "http://www.w3.org/2001/XMLSchema#integer",
        "@value": 25
      }
    ],
    "http://xmlns.com/foaf/0.1/name": [
      {
        "@value": "Bob Martin"
      }
    ]
  }
]

Interpretation : Comparaison des formats

Format Extension Lisibilite Usage principal Equivalent dotNetRDF
Turtle .ttl Excellente Edition manuelle CompressingTurtleWriter
N-Triples .nt Moyenne Echanges bulk NTriplesWriter
RDF/XML .rdf Faible Compatibilite historique RdfXmlWriter
JSON-LD .jsonld Bonne (dev web) APIs web, Schema.org JsonLdWriter

Conseil : Preferez Turtle pour le travail quotidien et JSON-LD pour l’integration web.


5. Charger un Fichier Existant

rdflib peut charger des graphes depuis des fichiers dans différents formats.

if RDFLIB_AVAILABLE:
    # Load Example.ttl from the data directory
    g_example = Graph()
    g_example.parse("data/Example.ttl", format="turtle")

    print(f"Triples charges depuis Example.ttl : {len(g_example)}")
    print()

    # Display all triples
    print("Contenu de Example.ttl :")
    for s, p, o in g_example:
        print(
            f"  {s.n3(g_example.namespace_manager)} -- {p.n3(g_example.namespace_manager)} --> {o.n3(g_example.namespace_manager)}"
        )
else:
    print("rdflib non disponible : chargement de fichier ignore.")
Triples charges depuis Example.ttl : 4

Contenu de Example.ttl :
  <http://www.w3.org/TR/rdf-syntax-grammar> -- dc:title --> "RDF/XML Syntax Specification (Revised)"
  <http://www.w3.org/TR/rdf-syntax-grammar> -- ex:editor --> _:n7f6357d9f74f4d42be98c11b3f29f15ab1
  _:n7f6357d9f74f4d42be98c11b3f29f15ab1 -- ex:homePage --> <http://purl.org/net/dajobe/>
  _:n7f6357d9f74f4d42be98c11b3f29f15ab1 -- ex:fullname --> "Dave Beckett"

Interpretation : Chargement de fichier

La méthode g.parse() charge un fichier RDF dans le graphe.

Opération rdflib dotNetRDF
Charger un fichier g.parse("file.ttl", format="turtle") FileLoader.Load(g, "file.ttl")
Parser auto-detect g.parse("file.ttl") FileLoader.Load(g, "file.ttl") (auto)

Formats supportes par parse() : turtle, xml, json-ld, nt, n3, trig, nquads


6. Tableau de Correspondance dotNetRDF / rdflib

Ce tableau recapitule les equivalences entre les deux bibliotheques pour les opérations couvertes dans ce notebook.

Opération dotNetRDF (C#) rdflib (Python)
Créer un graphe var g = new Graph(); g = Graph()
Noeud URI g.CreateUriNode(new Uri("...")) URIRef("...")
Blank node g.CreateBlankNode() BNode()
Litteral simple g.CreateLiteralNode("text") Literal("text")
Litteral type g.CreateLiteralNode("42", XSD.Integer) Literal(42, datatype=XSD.integer)
Litteral avec langue g.CreateLiteralNode("Bonjour", "fr") Literal("Bonjour", lang="fr")
Ajouter un triple g.Assert(new Triple(s, p, o)) g.add((s, p, o))
Retirer un triple g.Retract(new Triple(s, p, o)) g.remove((s, p, o))
Nombre de triples g.Triples.Count len(g)
Serialiser en Turtle new CompressingTurtleWriter().Save(g, file) g.serialize(format="turtle")
Serialiser en N-Triples new NTriplesWriter().Save(g, file) g.serialize(format="nt")
Charger un fichier FileLoader.Load(g, "file.ttl") g.parse("file.ttl")
Namespace g.NamespaceMap.AddNamespace("ex", uri) g.bind("ex", Namespace(uri))

Différences philosophiques

Aspect dotNetRDF rdflib
Style OOP verbeux, typage fort Pythonique, concis
Noeuds Lies au graphe (g.CreateUriNode) Independants (URIRef(...))
Triples Objet Triple(s, p, o) Tuple Python (s, p, o)
Iterer foreach (var t in g.Triples) for s, p, o in g:

A retenir : Les deux bibliotheques implementent les mêmes standards W3C. Le choix depend de votre ecosysteme : .NET pour l’integration C#, Python pour la data science et l’IA.


Exercices

Mettez en pratique les concepts appris avec ces deux exercices.

Exemple guide 1 : Reproduire le graphe “Hello World” de SW-2

Dans le notebook SW-2 (dotNetRDF), nous avons créé un graphe contenant :

<http://www.dotnetrdf.org>  <http://example.org/says>  "Hello World"
<http://www.dotnetrdf.org>  <http://example.org/says>  "Bonjour tout le Monde"@fr

Voici la reproduction en Python avec rdflib.

if RDFLIB_AVAILABLE:
    # Exemple guide 1 : Reproduire le graphe Hello World de SW-2
    g_hello = Graph()

    # Define subject and predicate URIs
    dotnetrdf = URIRef("http://www.dotnetrdf.org")
    says = URIRef("http://example.org/says")

    # Ajouter les deux triples
    g_hello.add((dotnetrdf, says, Literal("Hello World")))
    g_hello.add((dotnetrdf, says, Literal("Bonjour tout le Monde", lang="fr")))

    # Verify
    print(f"Nombre de triples : {len(g_hello)} (attendu: 2)")
    print()
    print("=== Turtle ===")
    print(g_hello.serialize(format="turtle"))
    print("=== N-Triples ===")
    print(g_hello.serialize(format="nt"))
else:
    print("rdflib non disponible : exemple 1 ignore.")
Nombre de triples : 2 (attendu: 2)

=== Turtle ===
@prefix ns1: <http://example.org/> .

<http://www.dotnetrdf.org> ns1:says "Hello World",
        "Bonjour tout le Monde"@fr .


=== N-Triples ===
<http://www.dotnetrdf.org> <http://example.org/says> "Hello World" .
<http://www.dotnetrdf.org> <http://example.org/says> "Bonjour tout le Monde"@fr .

Exemple guide 2 : Graphe de livre avec tous les types de noeuds

Creation d’un graphe RDF decrivant un livre avec URI, litteraux types, langue et blank node.

if RDFLIB_AVAILABLE:
    # Exemple guide 2 : Graphe de livre avec tous les types de noeuds
    g_book = Graph()
    EX = Namespace("http://example.org/")
    g_book.bind("ex", EX)

    g_book.add((EX.Book123, RDF.type, EX.Book))
    g_book.add((EX.Book123, EX.title, Literal("Le Petit Prince")))
    g_book.add((EX.Book123, EX.pages, Literal(96, datatype=XSD.integer)))
    g_book.add((EX.Book123, EX.author, EX.Author123))
    g_book.add(
        (
            EX.Book123,
            EX.description,
            Literal("Un conte philosophique pour enfants et adultes", lang="fr"),
        )
    )
    g_publisher = BNode()
    g_book.add((EX.Book123, EX.publisher, g_publisher))
    g_book.add((g_publisher, EX.publisherName, Literal("Gallimard")))
    g_book.add((g_publisher, EX.publisherURL, URIRef("http://www.gallimard.fr")))

    print("=== Livre en Turtle ===")
    print(g_book.serialize(format="turtle"))
else:
    print("rdflib non disponible : exemple 2 ignore.")
=== Livre en Turtle ===
@prefix ex: <http://example.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:Book123 a ex:Book ;
    ex:author ex:Author123 ;
    ex:description "Un conte philosophique pour enfants et adultes"@fr ;
    ex:pages 96 ;
    ex:publisher [ ex:publisherName "Gallimard" ;
            ex:publisherURL <http://www.gallimard.fr> ] ;
    ex:title "Le Petit Prince" .

Interpretation : Graphe de livre

Le graphe obtenu illustre les trois types de noeuds RDF :

Noeud Type Contenu
ex:Book123 URIRef Ressource identifiee (le livre)
"Le Petit Prince" Literal Valeur textuelle simple
96 Literal (XSD.integer) Valeur numérique typee
"Un conte philosophique..."@fr Literal (lang) Valeur avec langue
_:N... (editeur) BNode Noeud anonyme pour l’editeur

Observations sur la serialisation Turtle : - Le mot-cle a est un raccourci pour rdf:type - Les proprietes d’un même sujet sont separées par ; - Le blank node editeur est representé avec la notation [ ... ] (embarquée) - L’entier 96 est écrit sans guillemets (syntaxe Turtle pour les types XSD courants)


Resume

Ce sidetrack a presente les fondamentaux de rdflib, l’equivalent Python de dotNetRDF.

Concepts cles

Concept Ce que vous avez appris
rdflib Créer des graphes, ajouter des triples, gerer les namespaces
Types de noeuds URIRef, BNode, Literal (avec datatype et lang)
Serialisation Turtle, N-Triples, JSON-LD
Correspondance Equivalences dotNetRDF / rdflib

Prochaines étapes

  • SW-3-CSharp-GraphOperations : Manipulation avancee de graphes en .NET (fusion, requêtes LINQ)
  • SW-4b-Python-SPARQL : Le langage de requête SPARQL en Python
  • SW-5b-Python-LinkedData : Interroger DBpedia et Wikidata avec SPARQLWrapper

Retour au sommaire : Index SemanticWeb

Resume et perspectives

Ce sidetrack Python a permis de decouvrir rdflib, l’equivalent Python de dotNetRDF, et de mettre en parallele les deux ecosystemes. Nous avons explore la creation de graphes RDF avec l’API pythonique de rdflib (g.add((s, p, o))), les trois types de noeuds fondamentaux (URIRef, BNode, Literal avec datatype et langue), et la serialisation dans les formats standards (Turtle, N-Triples, JSON-LD). Le tableau de correspondance detaille entre dotNetRDF et rdflib constitue une reference utile pour passer d’un ecosysteme a l’autre.

L’exemple guide 3 a montre la construction d’un graphe complet representant une équipe de recherche, combinant URI, blank nodes et litteraux types, puis interrogeant le résultat en SPARQL. Cette approche progressive illustre bien la philosophie de rdflib : des tuples Python simples pour manipuler des données RDF, avec un acces direct aux namespaces W3C preconfigures.

Dans le notebook suivant (SW-3-CSharp-GraphOperations), nous retournerons a dotNetRDF pour explorer les opérations avancees sur les graphes (fusion, requêtes LINQ, listes RDF), tandis que le sidetrack SW-4b-Python-SPARQL permettra de mettre en pratique les requêtes SPARQL avec rdflib.

Exercice 1 : Créer un graphe de cours

Créez un graphe RDF decrivant un cours universitaire avec : - Un URI pour le cours (ex:Math101) - Un titre (litteral simple) - Un nombre de credits (litteral type integer) - Un enseignant (URI) - Une description en anglais (litteral avec langue en)

Serialisez le résultat en Turtle.

if RDFLIB_AVAILABLE:
    # TODO etudiant : creer un graphe de cours
    g_course = Graph()
    EX = Namespace("http://example.org/")
    g_course.bind("ex", EX)

    # Indice : inspirez-vous de l'exemple guide 2
    # Etape 1 : ajouter les triples pour le cours
    # g_course.add((EX.Math101, ...))

    # Etape 2 : serialiser en Turtle
    # print(g_course.serialize(format="turtle"))

    print("Exercice a completer : creez un graphe RDF pour un cours")
else:
    print("rdflib non disponible : exercice 1 ignore.")
Exercice a completer : creez un graphe RDF pour un cours

Exercice 2 : Explorer et filtrer un graphe charge

Chargez le fichier data/Example.ttl vu dans la section 5, puis : 1. Affichez tous les triples contenant un blank node 2. Comptez le nombre de litteraux vs URI comme objet 3. Serialisez le graphe en JSON-LD

if RDFLIB_AVAILABLE:
    # TODO etudiant : explorer et filtrer un graphe charge
    g_loaded = Graph()
    g_loaded.parse("data/Example.ttl", format="turtle")

    # Indice : utilisez isinstance(o, BNode), isinstance(o, Literal), isinstance(o, URIRef)
    # Etape 1 : afficher les triples avec blank nodes
    # Etape 2 : compter litteraux vs URI
    # Etape 3 : serialiser en JSON-LD

    print(f"Graphe charge : {len(g_loaded)} triples")
    print("Exercice a completer : explorez et filtrez le graphe")
else:
    print("rdflib non disponible : exercice 2 ignore.")
Graphe charge : 4 triples
Exercice a completer : explorez et filtrez le graphe

7. Interroger un graphe avec SPARQL

Jusqu’ici nous avons construit et sérialisé des graphes RDF. L’autre moitié du travail sémantique consiste à les interroger. SPARQL est le langage de requête standard du web sémantique — l’équivalent de SQL pour les triplets RDF.

Une requête SPARQL fait correspondre un motif de triplet (le bloc WHERE) contre le graphe, puis retourne les variables liées (le SELECT). Le motif ?personne foaf:name ?nom trouve toutes les personnes possédant un nom. rdflib intègre un moteur SPARQL : la méthode g.query("SELECT ...") exécute la requête et renvoie un itérateur sur les lignes résultats.

Exemple guide 3 : Première requête SPARQL SELECT

Construisons un petit graphe social et listons ses membres par nom trié. Notez les PREFIX qui déclarent les raccourcis d’namespace, exactement comme g.bind() côté Python.

if RDFLIB_AVAILABLE:
    # Exemple guide : requete SPARQL SELECT sur un graphe social
    g_demo = Graph()
    EX = Namespace("http://example.org/")
    FOAF_NS = Namespace("http://xmlns.com/foaf/0.1/")
    g_demo.bind("ex", EX)
    g_demo.bind("foaf", FOAF_NS)

    # Trois personnes, une relation foaf:knows
    g_demo.add((EX.Alice, FOAF_NS.name, Literal("Alice")))
    g_demo.add((EX.Bob, FOAF_NS.name, Literal("Bob")))
    g_demo.add((EX.Charlie, FOAF_NS.name, Literal("Charlie")))
    g_demo.add((EX.Alice, FOAF_NS.knows, EX.Bob))

    # Requete SPARQL : toutes les personnes et leur nom, tries par nom
    requete_select = """
    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    SELECT ?personne ?nom WHERE {
        ?personne foaf:name ?nom .
    } ORDER BY ?nom
    """
    resultats = g_demo.query(requete_select)
    print("Personnes dans le graphe (triées par nom) :")
    for ligne in resultats:
        print(f"  {ligne.personne} -> {ligne.nom}")
Personnes dans le graphe (triées par nom) :
  http://example.org/Alice -> Alice
  http://example.org/Bob -> Bob
  http://example.org/Charlie -> Charlie

Exercice 3 : Compter les amis d’Alice avec SPARQL

Reprenez le graphe g_demo de l’exemple guide 3 (Alice connaît Bob). Écrivez une requête SPARQL qui compte combien de personnes ex:Alice connaît via foaf:knows. Utilisez la fonction d’agrégation COUNT.

Indice : la syntaxe d’agrégation est (COUNT(?ami) AS ?nombre). Le SELECT peut retourner un agrégat sans variable de ligne. Le sujet dans le WHERE est l’URI complète <http://example.org/Alice>.

Indice : PREFIX foaf: http://xmlns.com/foaf/0.1/ :> Etape 1 : ecrire le bloc WHERE { http://example.org/Alice foaf:knows ?ami . } :> Etape 2 : ajouter (COUNT(?ami) AS ?nombre) dans le SELECT :> Etape 3 : executer g_demo.query(…) et afficher le nombre :

if RDFLIB_AVAILABLE:
    # TODO etudiant : compter les amis d'Alice avec une requete SPARQL
    # Le graphe g_demo est deja construit dans l'exemple guide 3 ci-dessus.
    # Indice : inspirez-vous de requete_select, avec COUNT dans le SELECT.

    requete_ami = """
    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    SELECT (COUNT(?ami) AS ?nombre) WHERE {
        # TODO : pattern pour les foaf:knows d'Alice
    }
    """

    # Etape : executer la requete et afficher le nombre d'amis
    # resultat_ami = g_demo.query(requete_ami)
    # for ligne in resultat_ami:
    #     print(f"Alice connait {ligne.nombre} personne(s)")

    print("Exercice a completer : ecrivez le pattern SPARQL pour compter les amis d'Alice")
Exercice a completer : ecrivez le pattern SPARQL pour compter les amis d'Alice

Exercice de synthese : Construire un graphe pour un reseau social

Créez un graphe RDF modelisant un mini reseau social avec : - Au moins 3 personnes (URI) avec nom et email - Des relations foaf:knows entre les personnes - Au moins un blank node pour une publication partagee - Serialisation en Turtle + une requête SPARQL listant les amis d’une personne

if RDFLIB_AVAILABLE:
    # TODO etudiant : construire un graphe pour un reseau social
    # Indice : inspirez-vous de l'exemple guide 3 (equipe de recherche)
    # Etape 1 : creer le graphe et les namespaces (foaf + un namespace custom)
    # Etape 2 : ajouter 3+ personnes avec foaf:name, foaf:mbox, foaf:knows
    # Etape 3 : ajouter une publication (BNode) partagee par 2+ personnes
    # Etape 4 : serialiser en Turtle
    # Etape 5 : requete SPARQL pour trouver les amis d'une personne

    print("Exercice a completer : construisez un graphe RDF pour un reseau social")
else:
    print("rdflib non disponible : exercice de synthese ignore.")
Exercice a completer : construisez un graphe RDF pour un reseau social

Exemple guide 3 : Construire un graphe RDF complet

Combinez les concepts pour créer un graphe RDF representant une équipe de recherche avec ses membres, publications et affiliations.

Contraintes

  • Utiliser au moins 3 types de noeuds : URI, BNode, Literal
  • Utiliser au moins 2 namespaces (dont FOAF ou Schema.org)
  • Serialiser le graphe en Turtle et en JSON-LD
  • Afficher les triplets avec for s, p, o in g:

Étapes : 1. Créer le graphe avec les namespaces appropries 2. Ajouter des personnes (URI), une publication (BNode), des litteraux types 3. Serialiser en Turtle et JSON-LD 4. (Bonus) Ecrire une requête SPARQL pour trouver tous les auteurs

if RDFLIB_AVAILABLE:
    # Exemple guide 3 : Construire un graphe RDF complet
    from rdflib import Graph, Namespace, URIRef, BNode, Literal
    from rdflib.namespace import RDF, RDFS, FOAF, XSD

    # Etape 1 : creer le graphe et les namespaces
    g_team = Graph()
    EX = Namespace("http://example.org/research/")
    SCHEMA = Namespace("https://schema.org/")

    g_team.bind("ex", EX)
    g_team.bind("foaf", FOAF)
    g_team.bind("schema", SCHEMA)

    # Etape 2 : ajouter des personnes (URI), une publication (BNode), des litteraux types
    team = EX.ResearchTeam
    alice = EX.AliceMartin
    bob = EX.BobDupont
    publication = BNode()

    g_team.add((team, RDF.type, FOAF.Group))
    g_team.add((team, FOAF.name, Literal("Equipe de recherche IA et Web Semantique")))
    g_team.add((team, RDFS.label, Literal("Semantic Web AI Team")))

    g_team.add((alice, RDF.type, FOAF.Person))
    g_team.add((alice, FOAF.name, Literal("Alice Martin")))
    g_team.add((alice, SCHEMA.affiliation, team))
    g_team.add((alice, FOAF.mbox, URIRef("mailto:alice.martin@example.org")))

    g_team.add((bob, RDF.type, FOAF.Person))
    g_team.add((bob, FOAF.name, Literal("Bob Dupont")))
    g_team.add((bob, SCHEMA.affiliation, team))
    g_team.add((bob, FOAF.mbox, URIRef("mailto:bob.dupont@example.org")))

    g_team.add((team, FOAF.member, alice))
    g_team.add((team, FOAF.member, bob))

    g_team.add((publication, RDF.type, SCHEMA.ScholarlyArticle))
    g_team.add((publication, SCHEMA.name, Literal("Construire un graphe RDF complet")))
    g_team.add((publication, SCHEMA.author, alice))
    g_team.add((publication, SCHEMA.author, bob))
    g_team.add(
        (publication, SCHEMA.datePublished, Literal("2026-05-20", datatype=XSD.date))
    )
    g_team.add((publication, SCHEMA.keyword, Literal("RDF")))
    g_team.add((publication, SCHEMA.keyword, Literal("Semantic Web")))
    g_team.add((team, SCHEMA.subjectOf, publication))

    # Afficher les triplets
    for s, p, o in g_team:
        print(s, p, o)

    # Etape 3 : serialiser en Turtle puis en JSON-LD
    print("=== Turtle ===")
    print(g_team.serialize(format="turtle"))
    print("=== JSON-LD ===")
    jsonld_output = g_team.serialize(format="json-ld", indent=2)
    print(jsonld_output)

    # Etape 4 (bonus) : requete SPARQL pour trouver tous les auteurs
    query = """
    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    PREFIX schema: <https://schema.org/>
    SELECT DISTINCT ?authorName
    WHERE {
        ?paper a schema:ScholarlyArticle ;
               schema:author ?author .
        ?author foaf:name ?authorName .
    }
    ORDER BY ?authorName
    """

    print()
    print("=== Auteurs trouves avec SPARQL ===")
    for row in g_team.query(query):
        print(row.authorName)
else:
    print("rdflib non disponible : exemple guide 3 ignore.")
http://example.org/research/ResearchTeam http://xmlns.com/foaf/0.1/name Equipe de recherche IA et Web Semantique
http://example.org/research/AliceMartin https://schema.org/affiliation http://example.org/research/ResearchTeam
http://example.org/research/AliceMartin http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://xmlns.com/foaf/0.1/Person
Ndcd28b2be0254783bdd23d33f605fb26 https://schema.org/keyword RDF
Ndcd28b2be0254783bdd23d33f605fb26 https://schema.org/author http://example.org/research/AliceMartin
Ndcd28b2be0254783bdd23d33f605fb26 https://schema.org/keyword Semantic Web
Ndcd28b2be0254783bdd23d33f605fb26 https://schema.org/author http://example.org/research/BobDupont
Ndcd28b2be0254783bdd23d33f605fb26 https://schema.org/datePublished 2026-05-20
http://example.org/research/BobDupont http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://xmlns.com/foaf/0.1/Person
http://example.org/research/BobDupont http://xmlns.com/foaf/0.1/mbox mailto:bob.dupont@example.org
http://example.org/research/BobDupont http://xmlns.com/foaf/0.1/name Bob Dupont
http://example.org/research/AliceMartin http://xmlns.com/foaf/0.1/name Alice Martin
http://example.org/research/BobDupont https://schema.org/affiliation http://example.org/research/ResearchTeam
http://example.org/research/ResearchTeam http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://xmlns.com/foaf/0.1/Group
http://example.org/research/ResearchTeam http://xmlns.com/foaf/0.1/member http://example.org/research/BobDupont
http://example.org/research/ResearchTeam http://xmlns.com/foaf/0.1/member http://example.org/research/AliceMartin
http://example.org/research/AliceMartin http://xmlns.com/foaf/0.1/mbox mailto:alice.martin@example.org
http://example.org/research/ResearchTeam http://www.w3.org/2000/01/rdf-schema#label Semantic Web AI Team
Ndcd28b2be0254783bdd23d33f605fb26 http://www.w3.org/1999/02/22-rdf-syntax-ns#type https://schema.org/ScholarlyArticle
Ndcd28b2be0254783bdd23d33f605fb26 https://schema.org/name Construire un graphe RDF complet
http://example.org/research/ResearchTeam https://schema.org/subjectOf Ndcd28b2be0254783bdd23d33f605fb26
=== Turtle ===
@prefix ex: <http://example.org/research/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix schema: <https://schema.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:AliceMartin a foaf:Person ;
    foaf:mbox <mailto:alice.martin@example.org> ;
    foaf:name "Alice Martin" ;
    schema:affiliation ex:ResearchTeam .

ex:BobDupont a foaf:Person ;
    foaf:mbox <mailto:bob.dupont@example.org> ;
    foaf:name "Bob Dupont" ;
    schema:affiliation ex:ResearchTeam .

ex:ResearchTeam a foaf:Group ;
    rdfs:label "Semantic Web AI Team" ;
    foaf:member ex:AliceMartin,
        ex:BobDupont ;
    foaf:name "Equipe de recherche IA et Web Semantique" ;
    schema:subjectOf [ a schema:ScholarlyArticle ;
            schema:author ex:AliceMartin,
                ex:BobDupont ;
            schema:datePublished "2026-05-20"^^xsd:date ;
            schema:keyword "RDF",
                "Semantic Web" ;
            schema:name "Construire un graphe RDF complet" ] .


=== JSON-LD ===
[
  {
    "@id": "http://example.org/research/ResearchTeam",
    "@type": [
      "http://xmlns.com/foaf/0.1/Group"
    ],
    "http://www.w3.org/2000/01/rdf-schema#label": [
      {
        "@value": "Semantic Web AI Team"
      }
    ],
    "http://xmlns.com/foaf/0.1/member": [
      {
        "@id": "http://example.org/research/AliceMartin"
      },
      {
        "@id": "http://example.org/research/BobDupont"
      }
    ],
    "http://xmlns.com/foaf/0.1/name": [
      {
        "@value": "Equipe de recherche IA et Web Semantique"
      }
    ],
    "https://schema.org/subjectOf": [
      {
        "@id": "_:Ndcd28b2be0254783bdd23d33f605fb26"
      }
    ]
  },
  {
    "@id": "_:Ndcd28b2be0254783bdd23d33f605fb26",
    "@type": [
      "https://schema.org/ScholarlyArticle"
    ],
    "https://schema.org/author": [
      {
        "@id": "http://example.org/research/AliceMartin"
      },
      {
        "@id": "http://example.org/research/BobDupont"
      }
    ],
    "https://schema.org/datePublished": [
      {
        "@type": "http://www.w3.org/2001/XMLSchema#date",
        "@value": "2026-05-20"
      }
    ],
    "https://schema.org/keyword": [
      {
        "@value": "RDF"
      },
      {
        "@value": "Semantic Web"
      }
    ],
    "https://schema.org/name": [
      {
        "@value": "Construire un graphe RDF complet"
      }
    ]
  },
  {
    "@id": "http://example.org/research/BobDupont",
    "@type": [
      "http://xmlns.com/foaf/0.1/Person"
    ],
    "http://xmlns.com/foaf/0.1/mbox": [
      {
        "@id": "mailto:bob.dupont@example.org"
      }
    ],
    "http://xmlns.com/foaf/0.1/name": [
      {
        "@value": "Bob Dupont"
      }
    ],
    "https://schema.org/affiliation": [
      {
        "@id": "http://example.org/research/ResearchTeam"
      }
    ]
  },
  {
    "@id": "http://example.org/research/AliceMartin",
    "@type": [
      "http://xmlns.com/foaf/0.1/Person"
    ],
    "http://xmlns.com/foaf/0.1/mbox": [
      {
        "@id": "mailto:alice.martin@example.org"
      }
    ],
    "http://xmlns.com/foaf/0.1/name": [
      {
        "@value": "Alice Martin"
      }
    ],
    "https://schema.org/affiliation": [
      {
        "@id": "http://example.org/research/ResearchTeam"
      }
    ]
  }
]

=== Auteurs trouves avec SPARQL ===
Alice Martin
Bob Dupont

Interpretation : Graphe de recherche complet

L’exemple guide 3 combine tous les concepts du notebook dans un graphe realiste :

Élément Type rdflib Rôle dans le graphe
ex:ResearchTeam URIRef Sujet principal, type foaf:Group
ex:AliceMartin, ex:BobDupont URIRef Membres, type foaf:Person
Publication BNode Ressource anonyme, type schema:ScholarlyArticle
"Alice Martin" Literal Valeur textuelle via foaf:name
"2026-05-20" Literal(XSD.date) Date typee via schema:datePublished

La requête SPARQL en bonus illustre comment interroger un graphe RDF : elle sélectionne les noms des auteurs d’articles scolaires, prouvant que les deux membres sont bien liés à la publication.

Note : La serialization Turtle exploite la notation [ ... ] pour les blank nodes, rendant le graphe compact et lisible. En JSON-LD, le même blank node reçoit un identifiant généré (_:N...).

Retour au sommet