forked from hadley/r4ds
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwebscraping.qmd
559 lines (424 loc) · 49.5 KB
/
webscraping.qmd
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
# Ιστοσυγκομιδή {#sec-scraping}
```{r}
#| echo: false
source("_common.R")
```
## Εισαγωγή
Αυτό το κεφάλαιο σας παρουσιάζει τα βασικά της ιστοσυγκομιδής (web scraping) με το πακέτο [rvest](https://rvest.tidyverse.org).
Η ιστοσυγκομιδή είναι ένα πολύ χρήσιμο εργαλείο για την εξαγωγή δεδομένων από ιστοσελίδες.
Ορισμένοι ιστότοποι θα προσφέρουν την επιλογή ενός API, ένα σύνολο δομημένων αιτημάτων HTTP που επιστρέφουν δεδομένα ως JSON, τα οποία χειρίζεστε χρησιμοποιώντας τις τεχνικές από το @sec-rectangling.
Όπου είναι εφικτό, θα πρέπει να χρησιμοποιείτε το API[^webscraping-1], γιατί συνήθως θα σας δώσει πιο αξιόπιστα δεδομένα.
Δυστυχώς, όμως, ο προγραμματισμός με API είναι εκτός ύλης για αυτό το βιβλίο.
Αντίθετα, διδάσκουμε την συγκομιδή (scraping), μία τεχνική που λειτουργεί είτε ένας ιστότοπος προσφέρει API είτε όχι.
[^webscraping-1]: Πολλά δημοφιλή API έχουν ήδη αντίστοιχα πακέτα στο CRAN που τα ενθυλακώνουν, οπότε ξεκινήστε πρώτα με λίγη έρευνα!
Σε αυτό το κεφάλαιο, θα συζητήσουμε πρώτα το ηθικό κομμάτι και τη νομιμότητα της συγκομιδής προτού πάμε στα βασικά της HTML.
Στη συνέχεια, θα μάθετε τα βασικά των επιλογέων CSS για τον εντοπισμό συγκεκριμένων στοιχείων στη σελίδα και τον τρόπο χρήσης των συναρτήσεων του rvest για τη λήψη δεδομένων από κείμενο και γνωρίσματα HTML στην R.
Στη συνέχεια, θα συζητήσουμε ορισμένες τεχνικές για να καταλάβουμε ποιος επιλογέας CSS χρειάζεται για τη σελίδα που συγκομίζετε, προτού ολοκληρώσουμε με μερικές μελέτες περίπτωσης και μία σύντομη συζήτηση περί δυναμικών ιστοτόπων.
### Προαπαιτούμενα
Σε αυτό το κεφάλαιο, θα επικεντρωθούμε στα εργαλεία που παρέχει το πακέτο rvest.
Το rvest είναι μέλος του tidyverse, αλλά δεν είναι βασικό της μέλος, επομένως θα χρειαστεί να την φορτώσετε ξεχωριστά.
Θα φορτώσουμε επίσης ολόκληρο το tidyverse, καθώς θα δούμε ότι είναι γενικά χρήσιμο όταν δουλεύουμε με τα δεδομένα που έχουμε συλλέξει.
```{r}
#| label: setup
#| message: false
library(tidyverse)
library(rvest)
```
## Περί ηθικής και νομιμότητας της ιστοσυγκομιδής
Πριν ξεκινήσουμε να συζητάμε για τον κώδικα που θα χρειαστείτε για την ιστοσυγκομιδή, πρέπει να μιλήσουμε για το εάν είναι νόμιμο και ηθικό.
Γενικά, η κατάσταση είναι περίπλοκη και για τα δύο.
Η νομιμότητα εξαρτάται αρκετά από το πού ζεις.
Ωστόσο, ως γενική αρχή, εάν τα δεδομένα είναι δημοσίως διαθέσιμα, μη προσωπικά και πραγματολογικά, είναι πιθανό να είστε εντάξει[^webscraping-2].
Αυτοί οι τρεις παράγοντες είναι σημαντικοί επειδή συνδέονται με τους όρους και τις προϋποθέσεις, τις προσωπικές πληροφορίες και τα πνευματικά δικαιώματα του ιστότοπου, όπως θα συζητήσουμε παρακάτω.
[^webscraping-2]: Προφανώς δεν είμαστε δικηγόροι και αυτό δεν είναι νομική συμβουλή.
Αυτή όμως είναι η καλύτερη περίληψη που μπορούμε να δώσουμε έχοντας διαβάσει αρκετά για αυτό το θέμα.
Εάν τα δεδομένα δεν είναι δημόσια, μη προσωπικά ή τεκμηριωμένα ή εάν τα συλλέγετε ειδικά για κέρδος, θα πρέπει να μιλήσετε με έναν δικηγόρο.
Σε κάθε περίπτωση, θα πρέπει να σέβεστε τους πόρους του διακομιστή που φιλοξενεί τις σελίδες που επιλέγετε για ιστοσυγκομιδή.
Κυρίως, αυτό σημαίνει ότι εάν συγκομίζετε πολλαπλές σελίδες, θα πρέπει να φροντίσετε να περιμένετε για λίγο μεταξύ κάθε αιτήματος.
Ένας εύκολος τρόπος για να το κάνετε αυτό είναι να χρησιμοποιήσετε το πακέτο [**polite**](https://dmi3kno.github.io/polite/) του Dmytro Perepolkin.
Θα κάνει μία παύση αυτόματα μεταξύ των αιτημάτων και θα αποθηκεύσει προσωρινά τα αποτελέσματα, ώστε να μην ζητήσετε ποτέ την ίδια σελίδα δύο φορές.
### Όροι χρήσης
Αν κοιτάξετε προσεκτικά, θα διαπιστώσετε ότι πολλοί ιστότοποι περιλαμβάνουν έναν σύνδεσμο "όροι και προϋποθέσεις" ή "όροι παροχής υπηρεσιών" κάπου στη σελίδα και αν τον διαβάσετε προσεκτικά, συχνά θα ανακαλύψετε ότι ο ιστότοπος απαγορεύει ρητά την ιστοσυγκομιδή.
Αυτές οι σελίδες τείνουν να περιέχουν αρκετά ευρείς νομικούς ισχυρισμούς έτσι ώστε οι εταιρίες να μπορούν να είναι καλυμμένες σε μεγαλύτερο εύρος.
Είναι ευγενικό να σέβεστε αυτούς τους όρους παροχής υπηρεσιών όπου είναι δυνατόν, αλλά μην τους θεωρήσετε δεδομένους.
Τα δικαστήρια των ΗΠΑ έχουν γενικά διαπιστώσει ότι τοποθετώντας απλά τους όρους παροχής υπηρεσιών στο υποσέλιδο του ιστότοπου δεν αρκεί για να δεσμεύεστε από αυτούς, π.χ., [HiQ Labs v. LinkedIn](https://en.wikipedia.org/%20wiki/HiQ_Labs_v._LinkedIn).
Γενικά, για να δεσμευτείτε από τους όρους παροχής υπηρεσιών, πρέπει να έχετε κάνει κάποια ξεκάθαρη ενέργεια, όπως είναι η δημιουργία λογαριασμού ή η αποδοχή ενός πλαισίου με όρους.
Αυτός είναι ο λόγος για τον οποίο έχει σημασία το εάν τα δεδομένα είναι **δημόσια** ή όχι.
Εάν δεν χρειάζεστε λογαριασμό για να αποκτήσετε πρόσβαση σε αυτά, είναι απίθανο να δεσμεύεστε από τους όρους παροχής υπηρεσιών.
Σημειώστε, ωστόσο, ότι η κατάσταση είναι μάλλον διαφορετική στην Ευρώπη, όπου τα δικαστήρια έχουν διαπιστώσει ότι οι όροι της υπηρεσίας ισχύουν ακόμη και αν δεν συμφωνείτε ρητά με αυτούς.
### Δεδομένα προσωπικού χαρακτήρα
Ακόμα κι αν τα δεδομένα είναι δημόσια, θα πρέπει να είστε εξαιρετικά προσεκτικοί σχετικά με την συγκομιδή δεδομένων προσωπικού χαρακτήρα, όπως ονόματα, διευθύνσεις ηλεκτρονικού ταχυδρομείου, αριθμούς τηλεφώνου, ημερομηνίες γέννησης κ.λπ.
Η Ευρώπη έχει ιδιαίτερα αυστηρούς νόμους σχετικά με τη συλλογή ή αποθήκευση τέτοιων δεδομένων ([GDPR](https://gdpr-info.eu/)) και ανεξάρτητα από το πού ζείτε, είναι πιθανό να αντιμετωπίσετε ένα ηθικό αδιέξοδο.
Για παράδειγμα, το 2016, μία ομάδα ερευνητών συγκόμισε πληροφορίες δημόσιων προφίλ χρήστη (π.χ. ονόματα χρήστη, ηλικία, φύλο, τοποθεσία κ.λπ.) περίπου 70.000 ατόμων από τον ιστότοπο γνωριμιών OkCupid και τα δημοσιοποίησαν χωρίς καμία προσπάθεια ανωνυμοποίησης.
Ενώ οι ερευνητές θεώρησαν ότι δεν υπήρχε τίποτα λάθος με αυτό, καθώς τα δεδομένα ήταν ήδη δημόσια, αυτή η πράξη καταδικάστηκε ευρέως, λόγω ηθικών ανησυχιών σχετικά με την ταυτοποίηση των χρηστών των οποίων οι πληροφορίες δημοσιεύτηκαν στο σύνολο δεδομένων.
Εάν η εργασία σας περιλαμβάνει συγκομιδή προσωπικών δεδομένων, συνιστούμε ανεπιφύλακτα να διαβάσετε για τη μελέτη της OkCupid[^webscraping-3] καθώς και παρόμοιες μελέτες με αμφισβητήσιμη ερευνητική δεοντολογία, που αφορούν την απόκτηση και την δημοσιοποίηση δεδομένων προσωπικού χαρακτήρα.
[^webscraping-3]: Ένα παράδειγμα άρθρου σχετικά με τη μελέτη της OkCupid δημοσιεύτηκε από το Wired, <https://www.wired.com/2016/05/okcupid-study-reveals-perils-big-data-science> .
### Πνευματικά δικαιώματα
Τέλος, πρέπει επίσης να ανησυχείτε και για τη νομοθεσία περί πνευματικών δικαιωμάτων.
Η νομοθεσία περί πνευματικών δικαιωμάτων είναι περίπλοκη, αλλά αξίζει να ρίξετε μία ματιά στον [νόμο των ΗΠΑ](https://www.law.cornell.edu/uscode/text/17/102) που περιγράφει ακριβώς τι προστατεύεται: "\[...\] πρωτότυπα έργα παγιωμένα σε οποιοδήποτε απτό μέσο έκφρασης, \[...\]".
Στη συνέχεια, περιγράφει συγκεκριμένες κατηγορίες που εφαρμόζεται, όπως λογοτεχνικά έργα, μουσικά έργα, κινηματογραφικές ταινίες και άλλα.
Ιδιαίτερα, από την προστασία των πνευματικών δικαιωμάτων, απουσιάζουν τα δεδομένα.
Αυτό σημαίνει ότι εφόσον περιορίζετε την συγκομιδή σας στα γεγονότα, δεν ισχύει η προστασία πνευματικών δικαιωμάτων.
(Σημειώστε όμως ότι η Ευρώπη έχει ένα ξεχωριστό δικαίωμα "[sui generis](https://en.wikipedia.org/wiki/Database_right)" που προστατεύει τις βάσεις δεδομένων.)
Για παράδειγμα, στις ΗΠΑ, οι συνταγές μαγειρικής δεν υπόκεινται σε πνευματικά δικαιώματα, επομένως τα πνευματικά δικαιώματα δεν μπορούν να χρησιμοποιηθούν για την προστασία τους.
Αλλά αν αυτή η λίστα συνταγών συνοδεύεται από κάποιο σημαντικό καινοτόμο λογοτεχνικό περιεχόμενο, αυτό υπόκειται σε πνευματικά δικαιώματα.
Αυτός είναι ο λόγος για τον οποίο όταν ψάχνετε για μία συνταγή στο διαδίκτυο, υπάρχει εκ των προτέρων πάντα τόσο πολύ περιεχόμενο.
Εάν χρειάζεται να συγκομίσετε πρωτογενές περιεχόμενο (όπως κείμενο ή εικόνες), ενδέχεται να προστατεύεστε σύμφωνα με το [δόγμα της ορθής χρήσης](https://en.wikipedia.org/wiki/Fair_use).
Ο κανόνας της δίκαιης χρήσης δεν είναι αυστηρός, αλλά σταθμίζει μία σειρά αρκετών παραγόντων.
Είναι πιο πιθανό να ισχύει εάν συλλέγετε δεδομένα για ερευνητικούς ή μη εμπορικούς σκοπούς και εάν περιορίσετε την χρήση αυτού που συλλέγετε σε αυτό που χρειάζεστε.
## Τα βασικά της HTML
Για να συγκομίσετε ιστοσελίδες, πρέπει πρώτα να κατανοήσετε λίγα πράγματα για την **HTML**, τη γλώσσα που περιγράφει τις ιστοσελίδες.
Τα αρχικά της HTML σημαίνουν **H**yper**T**ext **M**arkup **L**anguage και η σύνταξη μοιάζει κάπως έτσι:
``` html
<html>
<head>
<title>Page title</title>
</head>
<body>
<h1 id='first'>A heading</h1>
<p>Some text & <b>some bold text.</b></p>
<img src='myimg.png' width='100' height='100'>
</body>
```
Η HTML έχει μία ιεραρχική δομή που σχηματίζεται από **στοιχεία** τα οποία αποτελούνται από μία ετικέτα έναρξης (π.χ. `<tag>`), προαιρετικά **χαρακτηριστικά** (`id='first'`), μία ετικέτα λήξης[^webscraping-4] (όπως `</tag>`), και **περιεχόμενα** (οτιδήποτε μεταξύ της ετικέτας έναρξης και λήξης).
[^webscraping-4]: Αυτή η κλάση προέρχεται από το πακέτο [xml2](https://xml2.r-lib.org).
Το xml2 είναι ένα πακέτο χαμηλού επιπέδου στο οποίο το πακέτο rvest στηρίζεται.
Εφόσον τα `<` και `>` χρησιμοποιούνται για τις ετικέτες έναρξης και λήξης, δεν μπορείτε να τις γράψετε απευθείας.
Πρέπει να χρησιμοποιήσετε τις **ακολουθίες διαφυγής** της HTML `>` (μεγαλύτερο από) και `<` (λιγότερο από).
Και δεδομένου ότι αυτές οι ακολουθίες διαφυγής χρησιμοποιούν το `&`, εάν θέλετε ένα κυριολεκτικό συμπλεκτικό σύμβολο θα πρέπει να το διαφύγετε ως `&`.
Υπάρχει ένα ευρύ φάσμα πιθανών ακολουθιών διαφυγής στην HTML, αλλά δεν χρειάζεται να ανησυχείτε για αυτές καθώς το πακέτο rvest τις χειρίζεται αυτόματα για εσάς.
Η ιστοσυγκομιδή είναι εφικτή επειδή η δομή της πλειοψηφίας των σελίδων με δεδομένα που θέλετε να συγκομίσετε είναι συνεπής.
### Στοιχεία
Υπάρχουν πάνω από 100 στοιχεία στην HTML.
Μερικά από τα πιο σημαντικά είναι:
- Κάθε σελίδα HTML πρέπει να περιέχει ένα στοιχείο `<html>` και αυτό πρέπει να έχει δύο παιδιά: το `<head>`, που περιέχει μεταδεδομένα, όπως ο τίτλος της σελίδας, και το `<body>`, με το περιεχόμενο που βλέπετε στον browser.
- Ετικέτες στίξης όπως η `<h1>` (επικεφαλίδα 1), η `<section>` (ενότητα), η `<p>` (παράγραφος) και η `<ol>` (διατεταγμένη λίστα) αποτελούν τη συνολική δομή της σελίδας.
- Ετικέτες μορφοποίησης όπως η `<b>` (έντονη γραφή), η `<i>` (πλάγια γράμματα) και η `<a>` (σύνδεσμος) μορφοποιούν το κείμενο που υπάρχει μέσα σε ετικέτες στίξης.
Αν συναντήσετε μία ετικέτα που δεν έχετε ξαναδεί, μπορείτε να μάθετε τι κάνει με λίγο γκουγκλάρισμα.
Ένα άλλο καλό μέρος για να ξεκινήσετε είναι το [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML), το οποίο περιγράφει σχεδόν κάθε πτυχή του προγραμματισμού ιστοσελίδων.
Τα περισσότερα στοιχεία μπορούν να έχουν περιεχόμενο μεταξύ των ετικετών έναρξης και τέλους.
Αυτό το περιεχόμενο μπορεί να είναι κείμενο ή περισσότερα στοιχεία.
Για παράδειγμα, το ακόλουθο κομμάτι HTML περιέχει μία παράγραφο κειμένου, με μία λέξη με έντονη γραφή.
```
<p>
Hi! My <b>name</b> is Hadley.
</p>
```
Τα **παιδιά** είναι τα στοιχεία που το στοιχείο `<html>` περιέχει, επομένως το στοιχείο `<p>` παραπάνω έχει ένα παιδί, το στοιχείο `<b>`.
Το στοιχείο `<b>` δεν έχει παιδιά, αλλά έχει περιεχόμενα (το κείμενο "name").
### Χαρακτηριστικά
Οι ετικέτες μπορεί να έχουν ονοματισμένα **χαρακτηριστικά** που μοιάζουν ως `name1='value1' name2='value2'`.
Δύο από τα πιο σημαντικά χαρακτηριστικά είναι το `id` και το `class`, που χρησιμοποιούνται σε συνδυασμό με την CSS (Cascading Style Sheets) για τον έλεγχο της οπτικής της σελίδας.
Αυτά συνήθως είναι χρήσιμα κατά την συγκομιδή δεδομένων από μία σελίδα.
Τα χαρακτηριστικά χρησιμοποιούνται επίσης για την καταγραφή του προορισμού των συνδέσμων (το χαρακτηριστικό `href` των στοιχείων `<a>`) και της πηγής των εικόνων (το χαρακτηριστικό `src` του στοιχείου `<img>`).
## Εξάγοντας δεδομένα
Για να ξεκινήσετε την συγκομιδή, θα χρειαστείτε τη διεύθυνση URL της σελίδας που θέλετε, την οποία μπορείτε συνήθως να αντιγράψετε από τον browser.
Στη συνέχεια, θα χρειαστεί να διαβάσετε τον HTML κώδικα για αυτήν τη σελίδα στην R με την `read_html()`.
Αυτό επιστρέφει ένα αντικείμενο `xml_document`[^webscraping-5] το οποίο στη συνέχεια θα χειριστείτε χρησιμοποιώντας συναρτήσεις του rvest:
[^webscraping-5]: Αυτή η κλάση προέρχεται από το πακέτο [xml2](https://xml2.r-lib.org).
Το xml2 είναι ένα πακέτο χαμηλού επιπέδου στο οποίο το πακέτο rvest στηρίζεται.
```{r}
html <- read_html("http://rvest.tidyverse.org/")
html
```
Το rvest περιλαμβάνει επίσης μία συνάρτηση που σας επιτρέπει να γράφετε HTML.
Σε αυτό το κεφάλαιο, Θα χρησιμοποιήσουμε τη συνάρτηση αυτή όσο δείχνουμε πώς λειτουργούν οι διάφορες συναρτήσεις του rvest με απλά παραδείγματα.
```{r}
html <- minimal_html("
<p>This is a paragraph</p>
<ul>
<li>This is a bulleted list</li>
</ul>
")
html
```
Τώρα που έχετε το HTML στην R, ήρθε η ώρα να εξάγετε τα δεδομένα που σας ενδιαφέρουν.
Πρώτα θα μάθετε για τους επιλογείς CSS που σας επιτρέπουν να προσδιορίσετε τα στοιχεία που σας ενδιαφέρουν και τις συναρτήσεις του rvest που μπορείτε να χρησιμοποιήσετε για να εξάγετε δεδομένα από αυτά.
Στη συνέχεια, θα καλύψουμε εν συντομία τους πίνακες στην HTML, οι οποίοι έχουν ορισμένα ειδικά εργαλεία.
### Εντοπίζοντας στοιχεία
Το όνομα CSS είναι συντομογραφία για το cascading style sheets, και είναι ένα εργαλείο για τον ορισμό του οπτικού στυλ των εγγράφων HTML.
Η CSS περιλαμβάνει μία μικρογραφία γλώσσας (συντακτικού) για την επιλογή στοιχείων από μία σελίδα και ονομάζεται **επιλογείς CSS**.
Οι επιλογείς CSS ορίζουν μοτίβα για τον εντοπισμό στοιχείων HTML και είναι χρήσιμοι για την ιστοσυγκομιδή, καθώς παρέχουν έναν συνοπτικό τρόπο περιγραφής των στοιχείων που θέλετε να εξάγετε.
Θα επανέλθουμε στους επιλογείς CSS με περισσότερες λεπτομέρειες στην @sec-css-selectors, αλλά ευτυχώς μπορείτε να καλύψετε αρκετό έδαφος μόνο με τρεις από αυτούς:
- Ο επιλογέας `p` επιλέγει όλα τα στοιχεία `<p>`.
- Ο `.title` επιλέγει όλα τα στοιχεία με `class` "title".
- O `#title` επιλέγει το στοιχείο με το χαρακτηριστικό `id` που ισούται με "title".
Τα χαρακτηριστικά id πρέπει να είναι μοναδικά μέσα σε ένα έγγραφο, επομένως μόνο ένα στοιχείο θα επιλέγεται.
Ας δοκιμάσουμε αυτούς τους επιλογείς με ένα απλό παράδειγμα:
```{r}
html <- minimal_html("
<h1>This is a heading</h1>
<p id='first'>This is a paragraph</p>
<p class='important'>This is an important paragraph</p>
")
```
Χρησιμοποιήστε την `html_elements()` για να βρείτε όλα τα στοιχεία που ταιριάζουν με τον επιλογέα:
```{r}
html |> html_elements("p")
html |> html_elements(".important")
html |> html_elements("#first")
```
Μία άλλη σημαντική συνάρτηση είναι η `html_element()`, η οποία επιστρέφει πάντα τον ίδιο αριθμό εξόδων με τις εισόδους.
Εάν την εφαρμόσετε σε ολόκληρο το έγγραφο, θα σας δώσει την πρώτη, σε σειρά, αντιστοίχιση:
```{r}
html |> html_element("p")
```
Υπάρχει μία σημαντική διαφορά μεταξύ της `html_element()` και της `html_elements()` όταν χρησιμοποιείτε έναν επιλογέα που δεν ταιριάζει με κανένα στοιχείο.
Η `html_elements()` επιστρέφει ένα διάνυσμα μήκους 0, ενώ η `html_element()` επιστρέφει μία κενή τιμή.
Αυτό θα γίνει σύντομα σημαντικό.
```{r}
html |> html_elements("b")
html |> html_element("b")
```
### Ένθετες επιλογές
Στις περισσότερες περιπτώσεις, θα χρησιμοποιήσετε τις `html_elements()` και `html_element()` μαζί, χρησιμοποιώντας αρχικά την `html_elements()`, για να προσδιορίσετε στοιχεία που θα γίνουν παρατηρήσεις και στη συνέχεια την `html_element()`, για να βρείτε στοιχεία που θα γίνουν μεταβλητές.
Ας το δούμε στην πράξη χρησιμοποιώντας ένα απλό παράδειγμα.
Εδώ έχουμε μία μη ταξινομημένη λίστα (`<ul>`) όπου κάθε στοιχείο της λίστας (`<li>`) περιέχει πληροφορίες σχετικά με τέσσερις χαρακτήρες του StarWars:
```{r}
html <- minimal_html("
<ul>
<li><b>C-3PO</b> is a <i>droid</i> that weighs <span class='weight'>167 kg</span></li>
<li><b>R4-P17</b> is a <i>droid</i></li>
<li><b>R2-D2</b> is a <i>droid</i> that weighs <span class='weight'>96 kg</span></li>
<li><b>Yoda</b> weighs <span class='weight'>66 kg</span></li>
</ul>
")
```
Μπορούμε να χρησιμοποιήσουμε την `html_elements()` για να δημιουργήσουμε ένα διάνυσμα, όπου κάθε στοιχείο αντιστοιχεί σε διαφορετικό χαρακτήρα:
```{r}
characters <- html |> html_elements("li")
characters
```
Για να εξαγάγουμε το όνομα κάθε χαρακτήρα, χρησιμοποιούμε την `html_element()`, γιατί όταν εφαρμοστεί στην έξοδο της `html_elements()` είναι σίγουρο ότι θα επιστρέψει ένα αποτέλεσμα ανά στοιχείο:
```{r}
characters |> html_element("b")
```
Η διάκριση μεταξύ `html_element()` και `html_elements()` δεν είναι σημαντική για το όνομα, αλλά είναι σημαντική για το βάρος.
Θέλουμε να πάρουμε ένα βάρος για κάθε χαρακτήρα, ακόμα κι αν δεν υπάρχει `<span>` βάρος (weight).
Αυτό κάνει η `html_element()`:
```{r}
characters |> html_element(".weight")
```
Η `html_elements()` βρίσκει όλα τα `<span>` βάρους (weight) που είναι παιδιά του `characters`.
Υπάρχουν μόνο τρία από αυτά, οπότε χάνουμε τη σύνδεση μεταξύ ονομάτων και βαρών:
```{r}
characters |> html_elements(".weight")
```
Τώρα που έχετε επιλέξει τα στοιχεία που σας ενδιαφέρουν, θα πρέπει να εξάγετε τα δεδομένα, είτε από τα περιεχόμενα του κειμένου είτε από ορισμένα χαρακτηριστικά.
### Κείμενο και χαρακτηριστικά
Η `html_text2()`[^webscraping-6] εξάγει τα περιεχόμενα απλού κειμένου ενός στοιχείου HTML:
[^webscraping-6]: Η rvest παρέχει επίσης την `html_text()`, αλλά θα πρέπει σχεδόν πάντα να χρησιμοποιείτε την `html_text2()`, καθώς κάνει καλύτερη δουλειά στη μετατροπή του ένθετου HTML κώδικα σε κείμενο.
```{r}
characters |>
html_element("b") |>
html_text2()
characters |>
html_element(".weight") |>
html_text2()
```
Σημειώστε ότι τυχόν ακολουθίες διαφυγής θα αντιμετωπιστούν αυτόματα.
θα δείτε ακολουθίες διαφυγής HTML μόνο στην πηγαίο κώδικα HTML, κι όχι στα δεδομένα που επιστρέφονται από το rvest.
Η `html_attr()` εξάγει δεδομένα από χαρακτηριστικά:
```{r}
html <- minimal_html("
<p><a href='https://en.wikipedia.org/wiki/Cat'>cats</a></p>
<p><a href='https://en.wikipedia.org/wiki/Dog'>dogs</a></p>
")
html |>
html_elements("p") |>
html_element("a") |>
html_attr("href")
```
Η `html_attr()` επιστρέφει πάντα μία συμβολοσειρά, επομένως εάν εξάγετε αριθμούς ή ημερομηνίες, θα πρέπει να εφαρμόσετε κάποια μετεπεξεργασία.
### Πίνακες
Εάν είστε τυχεροί, τα δεδομένα σας θα είναι ήδη αποθηκευμένα σε έναν πίνακα HTML και θα πρέπει απλώς να τα διαβάσετε από αυτόν τον πίνακα.
Συνήθως, είναι εύκολο να αναγνωρίσετε έναν πίνακα στον browser σας: θα έχει μία ορθογώνια δομή με γραμμές και στήλες και μπορείτε να τον αντιγράψετε και να τον επικολλήσετε σε ένα εργαλείο όπως το Excel.
Οι πίνακες HTML δημιουργούνται από τέσσερα κύρια στοιχεία: `<table>`, `<tr>` (γραμμή πίνακα), `<th>` (επικεφαλίδα πίνακα) και `<td>` (δεδομένα πίνακα).
Ακολουθεί ένας απλός πίνακας HTML με δύο στήλες και τρεις γραμμές:
```{r}
html <- minimal_html("
<table class='mytable'>
<tr><th>x</th> <th>y</th></tr>
<tr><td>1.5</td> <td>2.7</td></tr>
<tr><td>4.9</td> <td>1.3</td></tr>
<tr><td>7.2</td> <td>8.1</td></tr>
</table>
")
```
Το πακέτο rvest παρέχει μία συνάρτηση που ξέρει πώς να διαβάζει αυτού του είδους δεδομένων: την `html_table()`.
Αυτή επιστρέφει μία λίστα που περιέχει ένα tibble για κάθε πίνακα που βρίσκεται στη σελίδα.
Χρησιμοποιήστε την `html_element()` για να προσδιορίσετε τον πίνακα που θέλετε να εξάγετε:
```{r}
html |>
html_element(".mytable") |>
html_table()
```
Σημειώστε ότι τα `x` και `y` έχουν μετατραπεί αυτόματα σε αριθμούς.
Αυτή η αυτόματη μετατροπή δεν λειτουργεί πάντα, επομένως σε πιο σύνθετα σενάρια μπορεί να θέλετε να την απενεργοποιήσετε με το `convert = FALSE` και στη συνέχεια να κάνετε τη δική σας μετατροπή.
## Εύρεση των κατάλληλων επιλογέων {#sec-css-selectors}
Το να βρείτε τον επιλογέα που χρειάζεστε για τα δεδομένα σας είναι συνήθως το πιο δύσκολο μέρος του προβλήματος.
Συχνά θα χρειαστεί να κάνετε κάποιους πειραματισμούς για να βρείτε έναν επιλογέα που είναι τόσο συγκεκριμένος (δηλαδή δεν επιλέγει πράγματα που δεν σας ενδιαφέρουν) όσο και ευαίσθητος (δηλαδή επιλέγει όλα όσα σας ενδιαφέρουν).
Οι πολλαπλές δοκιμές είναι ένα φυσιολογικό κομμάτι της διαδικασίας!
Υπάρχουν δύο βασικά εργαλεία που είναι διαθέσιμα για να σας βοηθήσουν με αυτήν τη διαδικασία: το SelectorGadget και τα εργαλεία προγραμματιστή του browser σας.
Το [SelectorGadget](https://rvest.tidyverse.org/articles/selectorgadget.html) είναι ένας σελιδοδείκτης javascript που δημιουργεί αυτόματα επιλογείς CSS με βάση τα θετικά και αρνητικά παραδείγματα που παρέχετε.
Δεν λειτουργεί πάντα, αλλά όταν λειτουργεί, είναι μαγικό!
Μπορείτε να μάθετε πώς να το εγκαταστήσετε και να το χρησιμοποιήσετε, είτε διαβάζοντας το <https://rvest.tidyverse.org/articles/selectorgadget.html> είτε παρακολουθώντας το βίντεο της Mine στη διεύθυνση <https://www.youtube.com/watch?v=PetWV5g1Xsc>.
Κάθε σύγχρονος browser συνοδεύεται από κάποια εργαλειοθήκη για προγραμματιστές, συνιστούμε όμως το Chrome, ακόμα κι αν δεν είναι το κανονικό σας πρόγραμμα περιήγησης: τα εργαλεία προγραμματιστή του είναι μερικά από τα καλύτερα και είναι άμεσα διαθέσιμα.
Κάντε δεξί κλικ σε ένα στοιχείο στη σελίδα και κάντε κλικ στην επιλογή «Επιθεώρηση».
Αυτό θα ανοίξει μία επεκτάσιμη προβολή της πλήρους σελίδας HTML, με κέντρο το στοιχείο στο οποίο μόλις κάνατε κλικ.
Μπορείτε να το χρησιμοποιήσετε για να εξερευνήσετε τη σελίδα και να πάρετε μία ιδέα του ποιοι επιλογείς μπορεί να δουλέψουν.
Δώστε ιδιαίτερη προσοχή στα χαρακτηριστικά κλάσης (class) και αναγνωριστικού (id), καθώς αυτά χρησιμοποιούνται συχνά για να σχηματίσουν την οπτική δομή της σελίδας, και ως εκ τούτου αποτελούν καλά εργαλεία για την εξαγωγή των δεδομένων που αναζητάτε.
Μέσα στην καρτέλα Στοιχεία, μπορείτε επίσης να κάνετε δεξί κλικ σε ένα στοιχείο και να επιλέξετε `Αντιγραφή ως Eπιλογέα` για να δημιουργήσετε έναν επιλογέα που θα προσδιορίζει μοναδικά το στοιχείο που σας ενδιαφέρει.
Εάν είτε το SelectorGadget είτε το Chrome DevTools έχουν δημιουργήσει έναν επιλογέα CSS που δεν καταλαβαίνετε, δοκιμάστε να δείτε το [Selectors Explained](https://kittygiraudel.github.io/selectors-explained/){.uri} που μεταφράζει τους επιλογείς CSS σε απλά αγγλικά.
Εάν παρατηρείτε ότι το κάνετε αυτό συχνά, ίσως να θέλετε να μάθετε περισσότερα σχετικά με τους επιλογείς CSS γενικά.
Συνιστούμε να ξεκινήσετε με το διασκεδαστικό εκπαιδευτικό υλικό [CSS dinner](https://flukeout.github.io/), και στη συνέχεια να ανατρέξετε στο [MDN web docs](https://developer.mozilla.org/en-US/docs/%20Web/CSS/CSS_Selectors).
## Βάζοντάς τα όλα μαζί
Ας τα συγκεντρώσουμε όλα αυτά μαζί για να συγκομίσουμε μερικούς ιστοτόπους.
Υπάρχει ο κίνδυνος ότι αυτά τα παραδείγματα δεν θα λειτουργούν πλέον όταν τα εκτελέσετε --- αυτή είναι η κύρια πρόκληση της ιστοσυγκομιδής.
Εάν αλλάξει η δομή του ιστότοπου, τότε θα πρέπει να αλλάξετε και τον κώδικα.
### StarWars
Το rvest περιλαμβάνει ένα πολύ απλό παράδειγμα στο `vignette("starwars")`.
Αυτή είναι μία απλή σελίδα με ελάχιστη HTML, επομένως είναι ένα καλό μέρος για να ξεκινήσετε.
Θα σας συνιστούσα να πλοηγηθείτε σε αυτήν τη σελίδα τώρα και να χρησιμοποιήσετε την "Επιθεώρηση στοιχείου" για να επιθεωρήσετε έναν από τους τίτλους που αναφέρεται σε μία ταινία Star Wars.
Χρησιμοποιήστε το πληκτρολόγιο ή το ποντίκι για να εξερευνήσετε την ιεραρχία της HTML και να δείτε εάν μπορείτε να αποκτήσετε μία αίσθηση της κοινής δομής που χρησιμοποιείται από κάθε ταινία.
Θα πρέπει να μπορείτε να δείτε ότι κάθε ταινία έχει μία κοινή δομή που μοιάζει με αυτή:
``` html
<section>
<h2 data-id="1">The Phantom Menace</h2>
<p>Released: 1999-05-19</p>
<p>Director: <span class="director">George Lucas</span></p>
<div class="crawl">
<p>...</p>
<p>...</p>
<p>...</p>
</div>
</section>
```
Στόχος μας είναι να μετατρέψουμε αυτά τα δεδομένα σε ένα πλαίσιο δεδομένων 7 γραμμών με μεταβλητές `title`, `year`, `director`, και `intro`.
Θα ξεκινήσουμε διαβάζοντας το HTML αρχείο και εξάγοντας όλα τα στοιχεία `<section>`:
```{r}
url <- "https://rvest.tidyverse.org/articles/starwars.html"
html <- read_html(url)
section <- html |> html_elements("section")
section
```
Αυτό ανακτά επτά στοιχεία που ταιριάζουν με τις επτά ταινίες που βρέθηκαν σε αυτήν τη σελίδα, υποδηλώνοντας ότι η χρήση του `section` ως επιλογέα είναι καλή.
Η εξαγωγή των επιμέρους στοιχείων είναι απλή, καθώς τα δεδομένα βρίσκονται πάντα στο κείμενο.
Το μόνο που χρειάζεται είναι να βρούμε τον κατάλληλο επιλογέα:
```{r}
section |> html_element("h2") |> html_text2()
section |> html_element(".director") |> html_text2()
```
Αφού το κάνουμε αυτό για κάθε στοιχείο, μπορούμε να βάλουμε όλα τα αποτελέσματα σε ένα tibble:
```{r}
tibble(
title = section |>
html_element("h2") |>
html_text2(),
released = section |>
html_element("p") |>
html_text2() |>
str_remove("Released: ") |>
parse_date(),
director = section |>
html_element(".director") |>
html_text2(),
intro = section |>
html_element(".crawl") |>
html_text2()
)
```
Επεξεργαστήκαμε λίγο περισσότερο την `released` για να λάβουμε μία μεταβλητή που θα είναι εύκολη στη χρήση αργότερα για την ανάλυσή μας.
### Κορυφαίες ταινίες στο IMDB
Για την επόμενη εργασία μας θα αντιμετωπίσουμε κάτι λίγο πιο δύσκολο, θα εξάγουμε τις κορυφαίες 250 ταινίες από το IMDb (Internet Movie Database).
Την περίοδο που γράφτηκε η δεύτερη αγγλική έκδοση αυτού του κεφαλαίου, η σελίδα έμοιαζε έτσι @fig-scraping-imdb.
```{r}
#| label: fig-scraping-imdb
#| echo: false
#| fig-cap: |
#| Στιγμιότυπο των κορυφαίων ταινιών του IMDb στις 05/12/2022.
#| fig-alt: |
#| Το στιγμιότυπο οθόνης δείχνει έναν πίνακα με τις στήλες "Rank and Title",
#| "IMDb Rating" και "Your Rating". Προβάλλονται 9 ταινίες από τις
#| 250 κορυφαίες. Οι 5 καλύτερες είναι το Shawshank Redemption,
#| The Godfather, The Dark Knight, The Godfather: Part II και το 12 Angry Men.
knitr::include_graphics("screenshots/scraping-imdb.png", dpi = 300)
```
Τα δεδομένα αυτά έχουν μία σαφή δομή πίνακα, επομένως αξίζει να ξεκινήσετε με την `html_table()`:
```{r}
url <- "https://web.archive.org/web/20220201012049/https://www.imdb.com/chart/top/"
html <- read_html(url)
table <- html |>
html_element("table") |>
html_table()
table
```
Το παραπάνω περιλαμβάνει μερικές κενές στήλες, αλλά γενικά κάνει καλή δουλειά στην καταγραφή της πληροφορίας από τον πίνακα.
Ωστόσο, πρέπει να το επεξεργαστούμε λίγο περισσότερο για να το κάνουμε πιο εύκολο στη χρήση.
Αρχικά, θα μετονομάσουμε τις στήλες και θα αφαιρέσουμε το περιττό κενό διάστημα στην κατάταξη και τον τίτλο.
Αυτό Θα το κάνουμε με την `select()` (αντί για την `rename()`), για να κάνουμε τη μετονομασία και την επιλογή μόνο αυτών των δύο στηλών σε ένα βήμα.
Στη συνέχεια, θα αφαιρέσουμε τις νέες γραμμές και τα επιπλέον κενά και, στη συνέχεια, θα εφαρμόσουμε την `separate_wider_regex()` (από την @sec-extract-variables) για να μεταφέρουμε τον τίτλο, το έτος και την κατάταξη στις δικές τους, ξεχωριστές μεταβλητές.
```{r}
ratings <- table |>
select(
rank_title_year = `Rank & Title`,
rating = `IMDb Rating`
) |>
mutate(
rank_title_year = str_replace_all(rank_title_year, "\n +", " ")
) |>
separate_wider_regex(
rank_title_year,
patterns = c(
rank = "\\d+", "\\. ",
title = ".+", " +\\(",
year = "\\d+", "\\)"
)
)
ratings
```
Ακόμη και σε αυτήν την περίπτωση, όπου τα περισσότερα δεδομένα προέρχονται από κελιά πίνακα, αξίζει να κοιτάξετε τον ακατέργαστο κώδικα HTML.
Εάν το κάνετε, θα ανακαλύψετε ότι μπορούμε να προσθέσουμε λίγα επιπλέον δεδομένα χρησιμοποιώντας ένα από τα χαρακτηριστικά.
Αυτός είναι ένας από τους λόγους για τους οποίους αξίζει να αφιερώσετε λίγο χρόνο για να εξετάσετε την πηγαίο κώδικα της σελίδας.
Μπορεί να βρείτε επιπλέον δεδομένα ή μία διαφορετική οδό για να πάρετε τα δεδομένα, η οποία να είναι ελαφρώς πιο εύκολη.
```{r}
html |>
html_elements("td strong") |>
head() |>
html_attr("title")
```
Αυτό, μπορούμε να το συνδυάσουμε με τα δεδομένα του πίνακα και να εφαρμόσουμε ξανά την `separate_wider_regex()` για να εξαγάγουμε το μέρος της πληροφορίας που μας ενδιαφέρει:
```{r}
ratings |>
mutate(
rating_n = html |> html_elements("td strong") |> html_attr("title")
) |>
separate_wider_regex(
rating_n,
patterns = c(
"[0-9.]+ based on ",
number = "[0-9,]+",
" user ratings"
)
) |>
mutate(
number = parse_number(number)
)
```
## Δυναμικές σελίδες
Μέχρι στιγμής έχουμε επικεντρωθεί σε ιστοτόπους όπου η `html_elements()` επιστρέφει ό,τι βλέπετε στον browser και συζητήσαμε πώς να τα αναλύσετε και πώς να τα οργανώσετε σε τακτοποιημένα πλαίσια δεδομένων.
Κατά καιρούς, ωστόσο, θα βρείτε έναν ιστότοπο όπου η `html_elements()` και παρόμοιες συναρτήσεις δεν επιστρέφουν τίποτα σαν αυτό που βλέπετε στο πρόγραμμα περιήγησης.
Σε πολλές περιπτώσεις, αυτό συμβαίνει επειδή προσπαθείτε να συγκομίσετε έναν ιστότοπο που δημιουργεί δυναμικά το περιεχόμενο της σελίδας με javascript.
Αυτό δεν λειτουργεί προς το παρόν με το rvest, επειδή το πακέτο rvest κατεβάζει τον ακατέργαστο κώδικα HTML και δεν εκτελεί καθόλου javascript.
Είναι ακόμα εφικτό να συγκομίσετε αυτούς τους τύπους σελίδων, αλλά το πακέτο rvest πρέπει να χρησιμοποιήσει μία πιο δαπανηρή διαδικασία: την πλήρη προσομοίωση του browser, συμπεριλαμβανομένης της εκτέλεσης όλων των στοιχείων javascript.
Η λειτουργία αυτή δεν ήταν διαθέσιμη τη στιγμή της σύνταξης αυτού του κειμένου, αλλά είναι κάτι πάνω στο οποίο εργαζόμαστε ενεργά και μπορεί να είναι διαθέσιμη κατά την περίοδο που θα διαβάζετε αυτό το βιβλίο.
Χρησιμοποιεί το [πακέτο chromote](https://rstudio.github.io/chromote/index.html) το οποίο εκτελεί στην πραγματικότητα τον browser Chrome στο παρασκήνιο και σας παρέχει πρόσθετα εργαλεία για να αλληλεπιδράσετε με τον ιστότοπο, όπως η πληκτρολόγηση κειμένου και το κλικ σε κουμπιά.
Ρίξτε μία ματιά στον [ιστότοπο του rvest](http://rvest.tidyverse.org/) για περισσότερες λεπτομέρειες.
## Σύνοψη
Σε αυτό το κεφάλαιο, μάθατε το γιατί, το γιατί όχι, και το πώς της συγκομιδής δεδομένων από ιστοσελίδες.
Πρώτα, μάθατε για τα βασικά της HTML και τη χρήση επιλογέων CSS για αναφορά σε συγκεκριμένα στοιχεία και μετά μάθατε να χρησιμοποιείτε το πακέτο rvest για να μεταφέρετε δεδομένα από την HTML στην R.
Στη συνέχεια, παρουσιάσαμε την ιστοσυγκομιδή με δύο μελέτες περίπτωσης: ένα πιο απλό σενάριο για την συγκομιδή δεδομένων σχετικών με τις ταινίες StarWars από τον ιστότοπο του πακέτου rvest, και ένα πιο περίπλοκο σενάριο για την συγκομιδή των κορυφαίων 250 ταινιών από την IMDB.
Οι τεχνικές λεπτομέρειες της συγκομιδής δεδομένων από τον ιστό μπορεί να είναι περίπλοκες, ιδιαίτερα όταν ασχολούμαστε με ιστοτόπους, ωστόσο οι νομικές και ηθικές οπτικές μπορεί να είναι ακόμη πιο περίπλοκες.
Είναι σημαντικό για εσάς να ενημερωθείτε και για τα δύο αυτά προτού ξεκινήσετε τη συγκομιδή δεδομένων.
Αυτό μας φέρνει στο τέλος της ενότητας του βιβλίου σχετικά με την εισαγωγή δεδομένων όπου έχετε μάθει τεχνικές για να λαμβάνετε δεδομένα από τον τόπο αποθήκευσής τους (υπολογιστικά φύλλα, βάσεις δεδομένων, αρχεία JSON και ιστοτόπους) σε μία τακτοποιημένη μορφή στην R.
Τώρα ήρθε η ώρα να στρέψουμε το βλέμμα μας σε ένα νέο θέμα: πως να αξιοποιήσουμε στο έπακρο την R ως γλώσσα προγραμματισμού.