-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.Rmd
2345 lines (1551 loc) · 91.5 KB
/
index.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
title: "Premiers pas en *web scraping*"
subtitle: "Introduction à la collecte automatique de données du *web*"
date: "`r Sys.Date()`"
author:
- name: Léa Christophe
affiliation: Université Paris 1 Panthéon-Sorbonne, UMR Géographie-cités
- name: Hugues Pecout
affiliation: CNRS, UMR Géographie-cités
- name: Robin Cura
affiliation: Université Paris 1 Panthéon-Sorbonne, UMR PRODIG
- name: Alexandre Cebeillac
affiliation: Université de Rouen Normandie, UMR IDEES
- name: Sébastien Rey-Coyrehourcq
affiliation: Université de Rouen Normandie, UMR IDEES
image: "featured.png"
logo: "figures/rzine.png"
output:
rzine::readrzine: # remotes::install_gitlab("rzine/package", host = "https://gitlab.huma-num.fr/", force = TRUE)
highlight: kate
number_sections: true
csl: Rzine_citation.csl
bibliography: biblio.bib
nocite: |
@*
link-citations: true
# github: "author/repository"
gitlab: "gitlab.huma-num.fr/webscraping/rzine_webscraping"
doi: "xx.xxx/xxxx.xxxxxxx"
licence: "by-sa"
params:
force_scraping: FALSE
# 5 possible choices : "by-nd", "by", "by-nc-sa", "by-nc","by-sa"
editor_options:
chunk_output_type: console
---
```{r setup, include=FALSE}
## Global options
knitr::opts_chunk$set(echo=TRUE,
cache=FALSE,
prompt=FALSE,
comment=NA,
message=FALSE,
warning=FALSE,
class.source="bg-info",
class.output="bg-warning")
```
> Cet article peut être utilisé comme support pour une initiation aux bases de la collecte automatique de données du *web* avec R. De nombreux aspects et méthodes de *web scraping* qui ne sont pas abordés dans cet article sont présentés sur ce [**site web associé**](https://webscraping.gitpages.huma-num.fr/website/), plus complet et approfondi sur le *web scraping* avec R et Python.
# Introduction {-}
Le *web scraping*, ou extraction de données sur le *web*, est un ensemble de techniques qui consistent à extraire de manière automatisée des informations à partir de sites *web*. En d'autres termes, c'est comme si vous utilisiez un robot pour récupérer des données à partir de pages *web*, plutôt que de le faire manuellement.
En sciences humaines et sociales, le *web scraping* peut être utilisé pour collecter des données pertinentes sur des sites *web*, forums, blogs, des réseaux sociaux, ou autres sources en ligne. Cette méthode peut être particulièrement utile pour analyser des tendances, des opinions, des dynamiques, ou tout simplement pour constituer une base de données à partir de plusieurs sources internet. L'utilisation de R pour le *web scraping* permet d'automatiser ce processus et d'analyser les données extraites immédiatement de manière efficace.
Le *web scraping* permet d'extraire des informations spécifiques d'une page *web* en analysant sa structure HTML et en extrayant uniquement les éléments pertinents. Cette pratique demande ainsi quelques connaissances techniques en matière de *web*, de langage de balisage HTML et de langage CSS, qui permettent respectivement de structurer une page *web* et de définir l'apparence et la mise en forme des différents éléments du document.
Au-delà d'initier au *web scraping* avec R, cet article aborde dans un premier temps les connaissances indispensables à la mise en place d'une collecte de données automatisée.
Le *web scraping* regroupe différentes techniques plus ou moins complexes. Cet article, qui est une initiation à la pratique de la collecte automatique de données, présente le cas d'utilisation le plus simple (c'est-à-dire le *scraping* d'une page web statique et peu interactive). Pour aller plus loin et se former à des techniques plus avancées, vous pouvez consulter ce support de formation plus détaillé :
<center>
[**https://webscraping.gitpages.huma-num.fr/website/**](https://webscraping.gitpages.huma-num.fr/website/){target="_blank"}
</center>
\
# Objectifs et prérequis
## Le site web [***ScrapImmob***](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}
Pour cette introduction à la collecte automatisée de données du *web* avec R, nous proposons une mise en pratique sur un site internet de démonstration créé pour l'occasion : [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}. **Ce site compile 1000 annonces fictives de ventes immobilières de maisons et d'appartements.**
<center>
![Source : [https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}](figures/ScrapImmob_Visuel.png)
</center>
\
<div class="alert alert-danger"> <b>L'intégralité des biens présentés, ainsi que les images associées ont été générés par différents modèles d'intelligence artificielle. Toutes les informations affichées sont donc complètement fictives. Toute ressemblance avec une annonce existante ou ayant existé serait purement fortuite et ne pourrait être que le fruit d'une pure coïncidence.</b> </div>
Les annonces de ventes de maisons ou d'appartements affichées sur le site [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"} sont localisées dans dix départements français des régions Normandie et Île-de-France : l'Eure, la Seine-Maritime, l'Essonne, les Hauts-de-Seine, Paris, la Seine-Saint-Denis, la Seine-et-Marne, le Val d'Oise, le Val de Marne et les Yvelines.
Une exploration rapide du site nous permet d'observer que chaque annonce contient les informations suivantes :
- des **photographies**,
- un **titre**,
- une **description**,
- des **caractéristiques techniques** (surfaces, prix, nombre de pièces, etc.),
- Une **localisation** (commune et carte interactive).
<center>
![Source : [https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/annonces/IDF_0078.html](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/annonces/IDF_0078.html){target="_blank"}](figures/ScrapImmob_annonce.png)
</center>
\
## Les objectifs de collecte
Avant de se lancer dans une collecte automatisée, **il est primordial de bien définir ses objectifs et de déterminer précisément quelles informations on souhaite récupérer. L'exploration du code source du site** (cf. [partie 2.3](#explorer-le-code-source)) **permet ensuite d'estimer le niveau de difficulté et de choisir une méthode de collecte optimale.**
<div class="alert alert-info">
Pour cette démonstration, **notre objectif est de récupérer la liste des biens mis en vente (maisons et appartements) en Seine-Maritime sur le site** [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}, **incluant l'ensemble des caractéristiques de description. La récupération des coordonnées géographiques affichées dans la carte interactive est également souhaitée**.
</div>
**Voici l'ensemble des caractéristiques que nous souhaitons récupérer :**
:::{.bold}
- **Titre**
- **Description**
- **Type de bien**
- **Nombre de chambres**
- **Nombre de salles de bain**
- **Surface habitable**
- **Surface de jardin**
- **Prix**
- **Commune**
- **Coordonnées géographiques**
- **Photographies** (URL et téléchargement)
:::
Pour répondre à cet objectif, nous allons alterner exploration, collecte et traitement de données :
1. Exploration du système de filtrage des biens (fonctionnement du système d'URL) ([Partie 3.2.1](#le-système-durl))
2. Récupération de la liste de toutes les pages (URLs) que l'on souhaite *scraper* ([Partie 3.2.2](#localiser-les-urls-des-pages-dannonces))
3. Exploration du code source du site afin de localiser les informations à récupérer ([Partie 3.3.1](#collecte-principale))
4. Collecte de l'ensemble des caractéristiques de chaque bien à partir de la liste des URsL récupérées ([Partie 3.3.2](#construction-dun-tableau-de-collecte))
5. Nettoyage et traitement des données collectées ([Partie 3.4](#nettoyage-des-données))
6. Exploration du code source pour récupérer les coordonnées géographiques (carte intéractive) ([Partie 3.5](#collecte-de-coordonnées))
7. Collecte et traitement de coordonnées géographiques de chaque bien ([Partie 3.5](#collecte-de-coordonnées))
8. Téléchargement de l'ensemble des photos associées aux biens précédemment collectés ([Partie 3.8](#téléchargement-des-photos))
\
## Comprendre le code source
**Le scraping d'un site web démarre toujours par l'exploration du code source du site ciblé**. En effet, pour automatiser la collecte d'informations, il est nécessaire d'inspecter le code source afin de déterminer la localisation précise des différentes informations ciblées.
**Extrait du code source de la page d'accueil du site web** [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"} :
<center>
![Source : [view-source:https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html](view-source:https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}](figures/code_source_extarct.png)
</center>
\
Pour comprendre la structure d'une page web, plusieurs prérequis sont nécessaires. **Il est indispensable d'avoir certaines connaissances spécifiques liées à la structure d'une page *web* et une compréhension des principes techniques sous-jacents. Vous devez ainsi acquérir une connaissance minimum du langage HTML (balises) et une familiarité avec le CSS (sélecteurs) pour identifier, cibler et extraire les éléments d'une page** ***web***. Pour cette raison, la partie suivante introduit les connaissances techniques indispensables à la mise en place de la collecte automatisée.
\
# Bases techniques
## Le langage HTML
Le langage HTML (***H****yper***T***ext* ***M****arkup* ***L****anguage*) est un dérivé du XML (*e****X****tensible* ***M****arkup* ***L****anguage*). Le XML est un langage de balisage flexible qui permet de définir des règles spécifiques pour structurer et stocker des données. Il est utilisé dans une variété de domaines, notamment pour l'échange de données entre applications. Par exemple, voici un document XML simple représentant des informations sur un livre :
```
<Book>
<Title>Le Seigneur des Anneaux</Title>
<Author>J.R.R. Tolkien</Author>
<Genre>Fantasy</Genre>
<Year>1954</Year>
</Book>
```
Le langage HTML, sous-langage du XML, partage de nombreuses caractéristiques avec ce langage, mais il est spécifiquement conçu pour structurer et organiser le contenu des pages *web*. Le HTML utilise une série de balises (des *tags*) pour définir la structure et le contenu d'une page *web*, ce qui permet aux navigateurs *web* de l'afficher correctement pour les utilisateurs.
\
### Les balises
Il n'y a pas de nombre fixe de balises HTML, car de nouvelles balises peuvent être introduites avec les versions futures du langage HTML. Cependant, il existe un ensemble de balises HTML standard définies dans les spécifications du W3C (*World Wide Web Consortium*^[« Le ***World Wide Web Consortium***, abrégé par le sigle ***W3C***, est un organisme de standardisation à but non lucratif, fondé en octobre 1994 chargé de promouvoir la compatibilité des technologies du *World Wide Web* telles que HTML5, HTML, XHTML, XML, RDF, SPARQL, CSS, XSL, PNG, SVG, MathML et SOAP. » [Wikipedia, 10/09/2024](https://fr.wikipedia.org/wiki/World_Wide_Web_Consortium)]). Pour obtenir la liste la plus à jour des balises HTML, vous pouvez consulter la [documentation officielle du W3C](https://www.w3.org/){target="_blank"}.
Chaque balise est entourée des symboles **`<`** et **`>`** et peut contenir des attributs qui spécifient des propriétés supplémentaires pour l'élément. Si aucune balise n'est obligatoire dans une page HTML, il est de convention d'avoir la structure de base suivante :
```{html, eval = FALSE, class.source='bg-info'}
<!DOCTYPE html>
<html>
<head>
<!-- Métadonnées de la page -->
<title>Titre de la page</title>
</head>
<body>
<!-- Contenu de la page -->
</body>
</html>
```
- **`<!DOCTYPE html>`** : En début du document, indique au navigateur qu'il s'agit d'une page HTML5^[« **HTML5** (*HyperText Markup Language 5*) est la dernière révision majeure du HTML (format de données conçu pour représenter les pages *web*). Cette version a été finalisée le 28 octobre 2014. HTML5 spécifie deux syntaxes d'un modèle abstrait défini en termes de DOM : HTML5 et XHTML5. », [Wikipedia, 10/09/2024](https://fr.wikipedia.org/wiki/HTML5)].
- **`<html>`** : Englobe tout le contenu de la page HTML. Définit le début et la fin du document HTML.
- **`<head>`** : Contient les métadonnées de la page, des liens vers des fichiers annexes (CSS, javascript…), etc.
- **`<body>`** : Contient tout le contenu visible de la page, tel que le texte, les images, les liens, les tableaux, etc.
- **`<title>`** : Définit le titre de la page qui apparaîtra dans la barre de titre du navigateur.
Dans la majorité des cas, on utilise une balise de fermeture pour indiquer la fin de l'élément. Une balise de fermeture présente le symbole **`/`** avant le nom de la balise :
```{html, eval = FALSE, class.source='bg-info'}
<body>
<!-- Contenu de la page -->
</body>
```
Le `body` peut englober toute une variété de balises prédéfinies pour structurer et ajouter différents types de contenu dans la page *web*. Voici quelques balises indispensables à connaître :
- **`<h1>`**, **`<h2>`**, …, **`<h6>`** : **titres** de différents niveaux hiérarchiques,
- **`<p>`** : **paragraphe**,
- **`<a>`** : **lien hypertexte**,
- **`<img>`** : **image**,
- **`<ul>`**, **`<ol>`**, **`<li>`** : **listes** non ordonnées et ordonnées,
- **`<table>`**, **`<tr>`**, **`<td>`** : **tableau** avec des lignes et des cellules,
- **`<div>`** : **section** (type bloc),
- **`<span>`** : **section** (type "*inline*").
Exemple d'utilisation de la balise **`p`** (paragraphe) :
```{html, eval = FALSE, class.source='bg-info'}
<body>
<p>Ceci est un paragraphe en langage HTML</p>
</body>
```
Dans cet exemple, les balises **`<p>`** et **`</p>`** permettent de créer un paragraphe dont le contenu textuel est :
"*Ceci est un paragraphe en langage HTML*".
Les différentes balises permettent ainsi de structurer, hiérarchiser et organiser le contenu d'une page *web*. Elles peuvent s'emboîter indéfiniment :
```{html, eval = FALSE, class.source='bg-info'}
<body>
<div>
<p>Un paragraphe en <a href="https://fr.wikipedia.org/wiki/HTML5">langage HTML</a></p>
</div>
</body>
```
Voici le rendu graphique du code HTML ci-dessus dans un navigateur *web* :
![](figures/para_html.png)
\
### Les attributs
Ces balises hiérarchisées et potentiellement regroupées peuvent être renseignées d'attributs qui permettent de spécifier des informations supplémentaires et jouer sur leur mise en forme. Exemples d'attributs :
- **`id`** : identifiant unique de la balise,
- **`class`** : classe de la balise,
- **`href`** : lien,
- etc.
**Ces attributs sont à spécifier dans les balises ouvrantes** :
```{html, eval = FALSE, class.source='bg-info'}
<body>
<div id="debut_doc">
<h1 class="categorie">Tous les articles</h1>
</div>
<div id="Liste_ref">
<h2 class="article">Titre article</h2>
<p class="summary">Résumé article...</p>
<a href="https://www.article.org">Intégralité article</a>
<h2 class="article">Titre article bis</h2>
<p class="summary">Résumé article bis...</p>
<a href="https://www.article_bis.org">Intégralité article bis</a>
</div>
</body>
```
L'organisation segmentée du contenu via les balises **`div`** ou **`span`** ainsi que les différents attributs spécifiés permettent la mise en forme et le paramétrage des éléments. **Les attributs des balises peuvent être associés à une mise en forme (couleur, taille, position, etc.) à l'aide du langage CSS** (cf. [partie suivante](#le-langage-css)). **Mais il s'agit également d'éléments cruciaux pour la collecte automatisée de données sur le** ***web***, **car ils permettent de cibler précisément des éléments**.
Dans l'exemple de code source ci-dessus, il est ainsi possible de récupérer toutes les URLs (***U****niform* ***R****esource* ***L****ocator*) des articles, en ciblant le contenu de tous les attributs **`href`** des balises **`<a>`** situés dans la **`<div>`** ayant pour identifiant "*Liste_ref*".
\
## Le langage CSS
Le CSS, ou *Cascading Style Sheets* (feuilles de style en cascade), est un langage de programmation utilisé pour décrire l'apparence et la mise en forme des documents HTML et XML. Il permet de contrôler l'apparence visuelle des différents éléments de pages *web* (taille, position, couleur, police, marges, etc.) via les sélecteurs CSS.
Le CSS peut être incorporé de différentes manières :
**1. Directement dans les balises** à l'aide de l'attribut **`style`** :
```{html, eval = FALSE, class.source='bg-info'}
<h1 style ="font-size:40px;color:#f03b35;text-align:center;">Titre principal</h1>
```
**2. Dans le document HTML**, inclut dans une balise **`<style>`** adaptée à cet effet :
```{html, eval = FALSE, class.source='bg-info'}
<html>
<style>
h1 {
color: #f03b35;
font-size: 40px;
text-align: center;
}
</style>
<body>
<h1>Titre principal</h1>
</body>
</html>
```
**3. Dans une feuille de style externe** (fichier texte avec l'extension **`.css`**). Cette dernière méthode est à privilégier car la séparation du contenu et la mise en forme facilite la mise à jour des styles. Le lien vers la feuille de style CSS est à indiquer dans une balise `<link>` inclue dans la balise `<head>` qui contient les métadonnées du document :
:::: {style="display: flex;"}
::: {.column width="59%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Titre principal</h1>
<p>Un premier paragraphe introductif</p>
</body>
</html>
```
:::
::: {.column width="2%"}
:::
::: {.column width="39%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
body {
background-color: lightblue;
}
h1 {
color: #f03b35;
font-size: 40px;
text-align:center;
}
p {
font-style: italic;
font-size: 20px;
}
```
:::
::::
**Dans les trois cas présentés ci dessus, le rendu graphique dans un navigateur** ***web*** **sera le suivant :**
![](figures/h1.png){fig-align="center"}
\
### Les sélecteurs CSS simples
<div class="alert alert-danger">**Bien qu'une connaissance approfondie du CSS ne soit pas nécessaire pour le *web scraping*, il est important de connaître les sélecteurs CSS.** **Leur utilisation est précieuse pour cibler des données de manière détaillée et ainsi optimiser la collecte.**</div>
Comme une page HTML est un ensemble **structuré** de balises, ce qui revient à dire qu'une page HTML est un ensemble organisé de données, il est possible d'interroger la structure de cette page pour en cibler des éléments. En HTML, on utilise un langage d'interrogation propre, basé sur des **sélecteurs**, c'est-à-dire des expressions qui permettent de définir précisément un sous-ensemble des balises HTML (et CSS) d'une page web.
Si on peut utiliser ces sélecteurs pour extraire de l'information, leur usage le plus courant consiste à mettre en forme des éléments HTML, c'est-à-dire à appliquer un style spécifique à un sous-ensemble de balises.
**1. Les sélecteurs d'éléments** permettent de cibler tous les éléments d'un même type. Exemple pour les balises `h2`:
:::: {style="display: flex;"}
::: {.column width="49%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<h2>Titre de niveau 2</h2>
```
:::
::: {.column width="2%"}
:::
::: {.column width="49%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
h2 { font-size: 20px; }
```
:::
::::
\
**2. Les sélecteurs d'identifiants** permettent de cibler un élément par son identifiant (**`id`**), préfixé du symbole `#`.
:::: {style="display: flex;"}
::: {.column width="49%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<div id="example"> </div>
```
:::
::: {.column width="2%"}
:::
::: {.column width="49%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
#example { background-color: #f2f2f2; }
```
:::
::::
\
**3. Les sélecteurs de classes** permettent de cibler tous les éléments d'une même classe. On utilise le préfixe `.` pour indiquer qu'il s'agit d'un nom de `class`.
:::: {style="display: flex;"}
::: {.column width="49%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<p class="summary">Ceci est un résumé</p>
```
:::
::: {.column width="2%"}
:::
::: {.column width="49%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
.summary { font-family: Arial, sans-serif; }
```
:::
::::
\
**4. Les sélecteurs d'attributs** permettent de cibler des éléments ayant un attribut commun. Exemple : l'attribut `href` de la balise `<a>`, qui renseigne l'URL d'un lien cliquable.
:::: {style="display: flex;"}
::: {.column width="49%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<a href="https://www.example.org">Lien</a>
```
:::
::: {.column width="2%"}
:::
::: {.column width="49%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
a[href] { color: purple; }
```
:::
::::
Il est possible de préciser sa cible en indiquant une valeur spécifique à l'attribut.
```{css, eval = FALSE, class.source='bg-warning'}
a[href="https://example.org"] { color: purple;}
```
\
### Les sélecteurs complexes et combinateurs
Le CSS met également à disposition des sélecteurs complexes et combinateurs que l'on peut utiliser pour cibler des contenus de manière très précise. Quelques exemples :
**A. Les sélecteurs descendants** ciblent les éléments, descendants d'un autre élément.
:::: {style="display: flex;"}
::: {.column width="49%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<div id="liste_ref">
<p class="summary">Ceci est un résumé</p>
</div>
```
:::
::: {.column width="2%"}
:::
::: {.column width="49%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
#liste_ref p .summary { font-size: 12px; }
```
<div class="alert alert-danger">Cible tous les paragraphes de la classe `summary`, positionnés dans l'élément ayant pour identifiant `liste_ref`.</div>
:::
::::
\
**B. Les sélecteurs de voisin direct** ciblent les nœuds qui suivent immédiatement un élément.
:::: {style="display: flex;"}
::: {.column width="49%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<div id="liste_ref">
<h2>Titre article</h2>
<p class="summary">Ceci est un résumé</p>
<h2>Titre article</h2>
<p class="summary">Ceci est un résumé</p>
</div>
```
:::
::: {.column width="2%"}
:::
::: {.column width="49%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
#liste_ref + h2 { font-size: 14px; }
```
\
<div class="alert alert-danger">L'utilisation du symbole `+` permet de cibler uniquement la première balise `h2` de la balise ayant pour identifiant `liste_ref`.</div>
:::
::::
\
### Exercice
Pour mieux comprendre l'utilisation et le fonctionnement du HTML et du CSS, **vous pouvez vous exercer à la pratique de ces deux langages à partir de cet exemple simple mis à disposition en téléchargement** :
\
<center>
[<img src="figures/zip.png" width="30"/> **exo_HTML.zip**](https://gitlab.huma-num.fr/webscraping/rzine_webscraping/-/raw/master/exo_HTML.zip?ref_type=heads&inline=false)
</center>
<br>
Une fois téléchargé, décompressez le répertoire qui contient deux fichiers : page.html et style.CSS.
1. Un fichier HTML peut être interprété (mis en page) par n'importe quel navigateur (Mozilla Firefox, Google Chrome, Microsoft Edge, ect.). Ouvrez le fichier page.html avec un navigateur. Aucune connexion internet n'est nécessaire.
![](figures/exo_html.png)
2. Ouvrez les deux fichiers (page.html et style.CSS) avec un éditeur de fichier texte (ex : bloc-note, RStudio, Notepad++, ect.).
:::: {style="display: flex;"}
::: {.column width="69%"}
<center><b>page.html</b></center>
```{html, eval = FALSE, class.source='bg-info'}
<!DOCTYPE html>
<html>
<head>
<title>Ma Page HTML</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Un titre de niveau 1</h1>
<p>Un premier petit paragraphe.</p>
<h2>Un titre de niveau 2</h2>
<p>Un autre paragraphe contenant un lien pour aller sur le site
<a href="https://elementr.netlify.app/">ElementR</a>.
</p>
</body>
</html>
```
:::
::: {.column width="2%"}
:::
::: {.column width="29%"}
<center><b>style.css</b></center>
```{css, eval = FALSE, class.source='bg-warning'}
h1 {
color: #f03b35;
font-size: 40px;
text-align:center;
}
```
:::
:::
3. Ajoutez et/ou modifiez du contenu (balise) dans le code source du fichier page.html. Enregistrez le fichier et ré-affichez (ou rafraîchissez avec la touche `F5`) la page HTML dans le navigateur pour découvrir le rendu.
4. Ajoutez et/ou modifiez du contenu dans le code source du fichier style.css. Enregistrez le fichier et ré-affichez la page HTML (ou rafraîchissez-la avec la touche `F5`) dans le navigateur pour apprécier la nouvelle mise en page.
\
## Explorer le code source
Maintenant que vous avez les bases des langages HTML et CSS, vous avez les capacités de consulter le code source du site [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}, comprendre sa structure et localiser les informations que vous souhaitez extraire.
### Affichage brut
Il est très simple d'afficher le code source d'une page *web*. Réalisez un clic-droit sur la page ciblée, puis sélectionnez le menu "*(Affichez le) code source de la page*". Le code source que le navigateur reçoit et met en forme est alors affiché dans un nouvel onglet. Cette fonctionnalité est également accessible avec le raccourci `Ctrl + U`.
::::: {style="display: flex;"}
:::: {.column width="49%"}
::: {style="text-align: center;"}
Avec *Google Chrome* :
![](figures/view_source_chrome.png){fig-align="center"}
:::
::::
:::: {.column width="2%"}
::::
:::: {.column width="49%"}
::: {style="text-align: center;"}
Avec *Mozilla Firefox* :
![](figures/view_source_firefox.png)
:::
::::
:::::
\
### L'inspecteur de code
**L'inspecteur de code**, un outil proposé par l'ensemble des navigateurs, simplifie l'exploration du code source d'une page. En s'appuyant sur différentes fonctionnalités (survol, filtrage, recherche, etc.), **l'inspecteur de code facilite l'identification des balises, des classes, des identifiants, des styles associés aux éléments** et permet ainsi de procéder à une extraction ciblée de données. En utilisant l'inspecteur de code source, vous accédez à toutes les informations nécessaires pour concevoir un script de collecte automatisée.
Pour y accéder, cliquez-droit n'importe où sur la page *web* ouverte dans un navigateur, puis cliquez sur “Inspecter” :
:::: {style="display: flex;"}
::: {.column width="49%"}
::: {style="text-align: center;"}
Avec *Google Chrome* :
![](figures/inspector_html_chrome.png){fig-align="center"}
:::
:::
::: {.column width="2%"}
:::
::: {.column width="49%"}
::: {style="text-align: center;"}
Avec *Mozilla Firefox* :
![](figures/inspector_html.png)
:::
:::
::::
\
Une fois l'inspecteur ouvert, l'outil de sélection (entouré en <font style="color:#FF0000;">**rouge**</font>) donne la possibilité de naviguer dans le code source en survolant les différents éléments de la page, et vice versa.
![](figures/inspecteur_capture2.png)
\
Vous pouvez déplier le code source pour l'explorer en profondeur.
Sur la page d'accueil de [ScrapImmob](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}, **les liens vers les pages d'annonces listées sur la page d'accueil semblent stockés dans l'attribut `href` des balises `<a>` de la classe `stretched-link`**.
![](figures/inspecteur_capture.png)
\
En réalisant un clic-droit sur un élément, vous pouvez accédez à la fonctionnalité "copier" qui vous permet de récupérer plusieurs choses :
![](figures/copier_select.png)
\
Il est ainsi possible de récupérer aisément :
- **l'intérieur du HTML** : contenu de la balise sélectionnée. Par exemple, l'intérieur du HTML de `<div id="abc" class="foo"><strong>TEXTE</strong></div>` est `<strong>TEXTE</strong>`.
- **l'exterieur du HTML** : ensemble de la balise sélectionnée. Par exemple, l'extérieur du HTML de `<div id="abc" class="foo"><strong>TEXTE</strong></div>` est `<div id="abc" class="foo"><strong>TEXTE</strong></div>`.
- **Le sélecteur CSS**. Exemple pour le titre du site "ScrapImmob" : `header.col-12 > h1:nth-child(1)`
- **Le chemin CSS**. Exemple pour le titre du site "ScrapImmob" : `html body div.container-fluid div.row header.col-12.text-center.p-3 h1`
- **Le Xpath**. Exemple pour le titre du site "ScrapImmob" : `/html/body/div/div[1]/header/h1`
<div class="alert alert-info">
XPath est un acronyme qui signifie "*XML Path Language*". Il s'agit d'un langage de requête utilisé pour naviguer et interroger des documents XML ou HTML. XPath est ainsi utilisé pour sélectionner des éléments spécifiques d'une page *web*.</div>
Ces trois derniers éléments pourront vous être précieux pour automatiser votre collecte.
Par exemple, le sélecteur CSS de la première balise `<a>` de la classe `stretched-link` est `div.col:nth-child(1) > div:nth-child(1) > div:nth-child(3) > a:nth-child(1)`. Ce sélecteur permet de cibler la première balise `<a>` de cette classe.
<div class="alert alert-info">
L'inspecteur de code des navigateurs génère des sélecteurs qui sont toujours exacts, mais ne sont pas nécessairement les plus parcimonieux ou compréhensibles. Il existe en effet de très nombreux ensembles de sélecteurs différents qui peuvent cibler le(s) même(s) élément(s) d'une page web. En pratique, si le navigateur indique bien que le sélecteur correspondant à cette dernière requête est `div.col:nth-child(1) > div:nth-child(1) > div:nth-child(3) > a:nth-child(1)`, on notera que la récupération du premier élément de `a.stretched-link` (par exemple en faisant `$$('a.stretched-link')[0]` dans la console, cf. *infra*) est plus lisible.
</div>
\
### La console de l'inspecteur de code
Pour tester un sélecteur CSS, c'est-à-dire vérifier qu'il isole bien un sous-ensemble des éléments structurés d'une page web, on peut l'exécuter directement dans la `Console` de l'inspecteur de code.
::: {style="text-align: center;"}
Avec *Mozilla Firefox* :
![](figures/browser_console_firefox.png){fig-align="center"}
:::
Dans la console, on peut tester un sélecteur CSS en utilisant la syntaxe spécifique suivante :
`$$('sélecteur')`
Par exemple, pour sélectionner tous les liens (balises `<a>...</a>`) de cette page web, on peut exécuter la requête `$$('a')` dans la console de l'inspecteur. Exemple avec *Firefox* :
![](figures/browser_console_firefox_selectA.png){fig-align="center"}
Cela renvoie une liste d'objets HTML que l'on peut explorer interactivement (code dépliable), et permet ainsi de tester le bon fonctionnement d'une requête via sélecteurs CSS, avant de l'automatiser dans un script de *scraping*.
Chacun des éléments isolés peut être localisé dans l'inspecteur (clic sur l'icône ![](figures/browser_console_firefox_iconLocate.png) avec *Firefox*, et clic sur le nom de l'élément avec le navigateur *Chrome*).
\
### Exercice
1. Combien y-a-t-il de liens (balises `<a>...</a>`) sur [cette page web](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"} ?
2. Combien y-a-t-il de liens (balises `<a>...</a>`) de la classe `.stretched-link` ? Vous pouvez utiliser la syntaxe suivante : `$$('element.class')`
3. Combien y-a-t-il d'éléments en gras (balise `<strong>...</strong>`) sur [une page d'annonce](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/annonces/IDF_0049.html){target="_blank"} ?
4. Combien y-a-t-il de balises `<script>...</script>` qui permettent d'intégrer ou de faire référence à un script javascript exécutable ? Dans quelle balise `<script>` sont stockées les coordonnées géographiques du marqueur affiché sur la carte interactive des [pages d'annonces](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/annonces/IDF_0049.html){target="_blank"} ?
<!--
## Les appels réseaux d'une page
Quand on souhaite réaliser une collecte d'éléments redondants (structurés de la même manière) sur une page *web* (ex : sites d'annonce, catalogues de médias, etc.), il peut être utile de chercher à comprendre le fonctionnement interne de la page.
Pour cela, il est possible d'observer ce qui se passe lorsqu'une page *web* est chargée, de façon à repérer des éléments structurés (données *json* par exemple) qui seraient chargés ou requêtés lors de l'interrogation de la page. Il s'agit donc d'observer les appels qu'une page émet lors de son chargement, pour récupérer des images, des polices, des bibliothèques de mise en page (CSS, javascript), etc.
On peut réaliser cette opération dans l'inspecteur de code, dans l'onglet "*Réseaux*" (ou *Network* dans Chrome) après avoir actualisé la page :
<video width="100%" controls>
<source src="figures/Screencast_Inspecteur_Network_crop.webm" type="video/webm">
</video>
\
Dans L'exemple ci-dessus, on constate que l'ouverture de la page de cet article déclenche :
1. L'ouverture de la page à proprement parler (format `html` dans la colonne `Type`).
2. Le chargement de la bibliothèque de fonctions MathJax.js (`javascript`) qui permet de mettre en forme des équations dans des pages HTML.
3. La tentative de chargement de la "favicon" du site, c'est-à-dire de l'icône qui devrait s'afficher dans le titre de l'onglet. Comme cette icône n'existe pas, la page essaie de la charger depuis d'autres emplacements (3 derniers appels), sans succès.
<div class="alert alert-info">
<b>La première étape du *web scraping* consiste ainsi à analyser le code source de la page et du site *web* ciblé, afin de trouver le chemin ou l'emplacement exact des informations que l'on souhaite récupérer. Plus le code HTML d'un site est structuré et optimisé, plus la collecte automatisée sera facilitée.</b></div>
-->
\
# Mise en pratique
## Les packages de scraping
Comme pour toutes les opérations et manipulations de données, plusieurs solutions existent pour le *web scraping* avec R. Si plusieurs fonctions R-base^[L’appellation R-base utilisée tout au long de ce document désigne l'ensemble des fonctions natives de R, c'est-à-dire installées d'office avec le logiciel, sans ajout de *packages* supplémentaires. On oppose fréquemment les fonctions R-base et les fonctions du *tidyverse*, qui utilisent une approche syntaxique assez différente.] peuvent être utilisées, il existe surtout deux packages de référence en matière de collecte automatisée :
- [**`rvest`**](https://rvest.tidyverse.org/){target="_blank"} : développé par Hadley Wickham (Posit), ce package facilite le scraping en utilisant les sélecteurs CSS et XPath pour extraire les données formatées en HTML ou en XML. Ce package s'inspire de bibliothèques Python déjà existantes : [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/){target="_blank"} et [RoboBrowser](https://robobrowser.readthedocs.io/en/latest/readme.html){target="_blank"}. **`rvest`** fait partie de l'écosystème **`tidyverse`**, les fonctions peuvent s'utiliser avec le(s) *pipe(s)*. Ce package est simple d'utilisation mais également limité en matière de fonctionnalité. Il ne permet que depuis peu, et de manière réduite, de simuler les actions d'un utilisateur (clic, scroll, etc.). On l'utilisera essentiellement pour l'extraction de données de sites *web* statiques.
- [**`Rselenium`**](https://cran.r-project.org/web/packages/RSelenium/index.html){target="_blank"} : ce package est ce que l'on appelle un *binding* de langage ("liaison") qui permet d'utiliser, depuis R, les fonctionnalités de l'outil **`Selenium 2.0 WebDriver`**. Ce *framework* *web* permet d'automatiser les tests d'applications *web* en simulant des utilisateurs. **`Selenium 2.0 WebDriver`** peut aussi être utilisé pour la collecte automatisée de données sur des sites *web* dynamiques, nécessitant une interaction utilisateur (exécution de code javascript généré par clic, survol, scroll, etc.). Il s'agit concrètement d'un outil qui simule la navigation sur un site web plutôt que d'un outil comme `rvest` qui en extrait simplement le contenu.
**Le site *web* [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"} est un site *web* statique. Il n'existe pas de base de données, toutes les informations affichées par le navigateur sont stockées dans le code source de la page. Ainsi, il n'est pas nécessaire de simuler le comportement d'un utilisateur pour accéder aux données ciblées. Dans ce cas, il est plus simple d'utiliser le package [**`rvest`**](https://rvest.tidyverse.org/){target="_blank"}**.
\
### Installation de `rvest`
Le package [**`rvest`**](https://rvest.tidyverse.org/){target="_blank"} permet de naviguer à travers la structure HTML des pages *web*, d'extraire du texte, des tableaux, des images et d'autres éléments, et de les convertir en données structurées directement exploitables dans R.
Commencez par installer le package, puis chargez la librairie.
```{r, message=FALSE, warning=FALSE, eval=FALSE}
install.packages("rvest")
library(rvest)
```
```{r, message=FALSE, warning=FALSE, echo = FALSE, eval=TRUE, cache=FALSE}
library(rvest)
```
\
### Fonctionnement de **`rvest`**
Commençons par explorer les principales fonctions du package [**`rvest`**](https://rvest.tidyverse.org/){target="_blank"} à partir d'un extrait du code source du site *web* [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}.
Pour cela, nous pouvons utiliser la fonction **`minimal_html()`** qui permet de créer un contenu HTML (XML) requêtable à partir de code HTML fourni dans une chaîne de caractères.
```{r, message=FALSE, warning=FALSE}
# Extrait de code source HTML du site ScrapImmob
extrait_html <- "<div class='card-body'>
<a href='annonces/IDF_0078.html' class='stretched-link'></a>
<p class='card-text'>Maison charmante de 96 m² avec grand jardin à Châtenay-Malabry</p>
</div>"
# Interprétation du "texte" comme du code source HTML
basic_html <- minimal_html(extrait_html)
class(basic_html)
```
- Les fonctions **`html_element()`** ou **`html_elements()`** permettent respectivement la récupération du 1er ou de tous les éléments d'un document HTML, en précisant un sélecteur CSS ou un Xpath. Par exemple, `html_elements("p")` appliqué au code source d'une page permet de récupérer tous les paragraphes :
```{r, echo = TRUE, message=FALSE, warning=FALSE}
# Récupérer toutes les balises <p> du document :
basic_html |> html_elements("p")
```
- La fonction **`html_attr()`** permet l'extraction des valeurs d'attributs.
```{r, echo=TRUE, message=FALSE, warning=FALSE}
# Récupérer la ou les URL(s) indiquée(s) dans l'attribut href
basic_html |> html_elements("a") |> html_attr("href")
```
- Les fonctions **`html_text()`** ou **`html_text2()`** permettent d'extraire uniquement le texte d'un élément.
```{r, echo=TRUE, message=FALSE, warning=FALSE}
# Récupérer le contenu textuel des balises <p>
basic_html |> html_elements("p") |> html_text2()
```
<div class="alert alert-danger">**Les fonctions du package `rvest` sont conçues pour être utilisées en *pipe*. Elles fonctionnent aussi bien avec le *pipe* natif du R-base (`|>`), qu'avec celui du package `magrittr` (`%>%`), utilisé par le `tidyverse`**</div>
\
## Le système d'URL
### Filtrage des biens & ciblage d'URL
:::: {style="display: flex;"}
::: {.column width="28%"}
::: {style="text-align: left;"}
![](figures/menu_filtre.png)
:::
:::
::: {.column width="2%"}
:::
::: {.column width="70%"}
Sur la page d'accueil du site [**ScrapImmob**](https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/index.html){target="_blank"}, un ensemble de boutons situé à gauche permet de filtrer les biens par département et par type de biens. **Lorsque l'on clique sur l'un de ces boutons, les biens affichés sur la page sont filtrés en fonction du choix réalisé. L'URL de la page est alors modifiée** de la manière suivante :
**URL principale** :
[https://analytics.huma-num.fr/Robin.Cura/**ScrapImmob/index.html**]{style="font-size: 90%;"}
**URL du filtre "Essonne (91) - Appartement"** :
[https://analytics.huma-num.fr/Robin.Cura/**ScrapImmob/91_appartement/index.html**]{style="font-size: 90%;"}
**URL du filtre "Val-d'Oise (95) - Maison"** :
[https://analytics.huma-num.fr/Robin.Cura/**ScrapImmob/95_maison/index.html**]{style="font-size: 90%;"}
:::
::::
Notre objectif étant de collecter uniquement des données sur les biens (maisons et appartements) de la Seine-Maritime, nous allons orienter notre collecte sur les deux URLs suivantes :
- https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/**76_maison/index.html**
- https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/**76_appartement/index.html**
Nous commençons par récupérer le code source de ces deux pages avec la fonction `read_html()`. Cette fonction effectue une requête HTTP, puis en analyse le code HTML récupéré à l'aide du package `xml2`.
```{r, eval=params$force_scraping, message=FALSE, warning=FALSE, cache=FALSE, echo = FALSE}
url_appart <- "https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/76_appartement/"
url_maison <- "https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/76_maison/"
code_appart <- read_html(url_appart)
code_maison <- read_html(url_maison)
xml2::write_xml(code_appart, "offline_data/code_appart.html")
xml2::write_xml(code_appart, "offline_data/code_maison.html")
```
```{r, eval=TRUE, message=FALSE, warning=FALSE, cache=FALSE, echo = FALSE}
url_appart <- "https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/76_appartement/"
url_maison <- "https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/76_maison/"
code_appart <- read_html("offline_data/code_appart.html")
code_maison <- read_html("offline_data/code_maison.html")
```
```{r, eval=FALSE, message=FALSE, warning=FALSE, cache=FALSE}
url_appart <- "https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/76_appartement/"
url_maison <- "https://analytics.huma-num.fr/Robin.Cura/ScrapImmob/76_maison/"
code_appart <- read_html(url_appart)
code_maison <- read_html(url_maison)
```
```{r, eval=TRUE, message=FALSE, warning=FALSE, cache=FALSE}
# Affichage de l'objet code_appart
code_appart
```
\
### Localiser les URLs des pages d'annonces
À partir de ces deux pages et de leur code source, nous allons récupérer la liste des URLs de toutes les pages des annonces affichées. Cela nous permettra dans un second temps, de collecter l'ensemble des informations de description de chaque bien en vente.
En explorant le code source, on remarque que les URLs des pages d'annonces complètes sont stockées dans des balises `<a>` de la classe `stretched-link` :
![](figures/detect_URL.png)
\
Pour récupérer l'ensemble de ces URLs, nous devons collecter les valeurs (URL en chaîne de caractères) de l'attribut `href` dans toutes les balises `<a>` de la classe `stretched-link` :
```{r, echo=TRUE, message=FALSE, warning=FALSE}
code_appart |> html_elements("a.stretched-link") |> html_attr("href")