Web Scraping 🇬🇧
Note a cura di Antonino Furnari - antonino.furnari@unict.it🔝
Università di Catania, Dipartimento di Matematica e Informatica
Note disponibili qui: http://www.antoninofurnari.github.iohttps://antoninofurnari.github.io/lecture-notes/it/data-science-python/web-scraping/
In questo laboratorio vedremo un esempio di Web Scraping mediante la libreria BeautifulSoup4
. In particolare, vedremo che il web scraping consiste in due fasi (che vengono in genere alternate):
- Esplorare la struttura (DOM) del documento mediante un browser;
- Scrivere il codice per scaricare le pagine da internet e decodificarle.
In questo laboratorio, mostreremo degli esempi di esplorazione DOM con Google Chrome, ma altri browsers (per esempio Mozilla Firefox) possono essere utilizzati in maniera simile.
Per prima cosa installiamo la libreria mediante il seguente comando: pip install bs4
.
1 Analisi del DOM ed Estrazione Automatica
Consideriamo la seguente pagina web:https://www.thomann.de/it/ukulele_soprani.html e apriamola nel browser.
Si tratta di una pagina di un e-commerce che vende strumenti musicali. In particolare, la pagina mostra la lista degli ‘ukulele soprani’ in vendita sul sito. Notiamo che per ogni prodotto sono riportate diverse informazioni, quali ad esempio:
- Il produttore dello strumento (es. ‘Harley Benton’);
- Il modello dello strumento (es. ‘UK-11 DW Brown’);
- L’immagine dello strumento;
- Il prezzo (es. €17,90);
- La disponibilità;
- Il numero di recensioni (es. 422);
- La votazione media ottenuta nelle recensioni (es. 4/5 stelle).
Notiamo inoltre, che la pagina mostra solo una parte dell’elenco (articoli 1-25 di 145) e che in fondo alla pagina sono disponibili dei link per passare alle pagine successive.
In questo laboratorio vedremo come costruire uno script che permette di navigare tra le pagine ed estrarre le informazioni elencate sopra in maniera automatica. Dato che il sito non espone una API, questo processo resta ‘semi-manuale’. Sfrutteremo infatti il contenuto HTML. Va notato che, benché HTML è strutturato, esso non è stato pensato per permettere a delle macchine di comunicare tra loro (non è una API!), ma per permettere al browser di visualizzare delle pagine per l’utente finale. Pertanto le strutture HTML delle pagine sono in genere ambigue e poco standard. Per questo motivo sarà necessario esplorare manualmente il DOM caso per caso.
Iniziamo facendo click col tasto destro sulla parte della pagina che contiene l’item e facendo click su ‘inspect’. Si aprirà un inspector sulla destra (o in basso). Uno degli elementi HTML verrà automaticamente evidenziato. Scorriamo la lista degli elementi HTML nell’inspector e facciamo click su di essi per vedere a quali elementi grafici essi corrispondono. Navigando nel DOM, dovremmo trovare un div
contraddistinto da diverse classi, tra cui extensible-article
.
Se scorriamo la struttura del documento nell’inspector, notiamo che la pagina contiene una lista di elementi div
di classe fx-product-list-entry
. Ciascuno di questi div contiene a sua volta gli elementi relativi a ciascun prodotto. Per verificare la nostra intuizione, iniziamo ad analizzare la pagina html mediante BeautifulSoup. Per poterlo fare, dovremo prima scaricare il contenuto grezzo della pagina HTML mediante urllib
:
|
|
<class 'bytes'>
La variabile page_html
contiene il contenuto ‘grezzo’ della pagina. La pagina può essere molto lunga, quindi è in genere sconsigliato provare a stamparla nella sua interezza con una print (ciò può bloccare il programma). Visualizziamo i primi $1000$ caratteri:
|
|
b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\n "http://www.w3.org/TR/html4/loose.dtd">\n\n<html lang="it" class="no-js">\n<head>\n\n<link rel="preconnect" href="https://fonts.static-thomann.de" crossorigin>\n<link href="https://fonts.static-thomann.de/pics/fonts/open-sans.css?v=1" rel="stylesheet">\n\n<meta name="charset" content="utf-8">\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n<meta http-equiv="X-UA-Compatible" content="IE=edge">\n<title>Ukulele Soprani – Thomann Italia</title>\n\n<meta name="description" content="Più di {numberOfArticlesInCategory} a partire da {minPrice} in stock - Sul nostro sito trovi sempre lo strumento adatto per te. Spese di spedizione gratuite, 30 giorni "soddisfatto o rimborsato" e 3 anni di garanzia. \nThomann - Il negozio di musica più grande d\'Europa">\n\n<meta name="Language" content="it-IT">\n<meta name="Content-Language" content="it-IT">\n<meta name="viewport" content="width=device-wi'
Teoricamente, potremmo fare il parsing della pagina in maniera manuale. In pratica, dato che le pagine HTML hanno una struttura generale simile (sono tutte composte da tag organizzati in maniera gerarchica), esistono diverse librerie per semplificare la loro manipolazione. Importiamo BeautifulSoup e processiamo la pagina:
|
|
<class 'bs4.BeautifulSoup'>
La variabile page_soup
è adesso un oggetto di tipo BeautifulSoup
. Tale oggetto ha a disposizione una serie di metodi che permettono di manipolare facilmente il documento, come vedremo a breve. Proviamo ad esempio a cercare tutti i div
di classe fx-product-list-entry
per vedere se la nostra intuizione è corretta:
|
|
<class 'bs4.element.ResultSet'>
La chiamata a findAll
ha restituito un oggetto di tipo ResultSet
. Vediamo quanti elementi sono contenuti nel set:
|
|
25
Domanda 1 Sarebbe possibile ottenere lo stesso risultato ottenuto con |
Dato che la pagina contiene esattamente $25$ prodotti, ciò conferma la nostra intuizione che le informazioni di ciascun prodotto sono contenute dentro i div di classe fx-product-list-entry
. Per avere una ulteriore verifica, proviamo a visualizzare il contenuto del primo container:
|
|
<div class="fx-product-list-entry">
<div class="product">
<a class="product__image" href="harley_benton_uk_12_black.htm?listPosition=0&type=category">
<picture class="fx-picture">
<source data-srcset="https://thumbs.static-thomann.de/thumb//thumb220x220/pics/prod/257768.webp" srcset="" type="image/webp"> <source data-srcset="https://thumbs.static-thomann.de/thumb//thumb220x220/pics/prod/257768.jpg" srcset=""> <img alt="Harley Benton UK-12 Black" class="fx-image responsive-image product-image lazyload loaded" data-src="" height=" 220" src="https://images.static-thomann.de/pics/images/misc/placeholder.svg" width=" 220"/>
</source></source></picture>
</a>
<a class="product__content" href="harley_benton_uk_12_black.htm?listPosition=0&type=category">
<div class="product__details">
<div class="product__title fx-text">
<span class="title__manufacturer">Harley Benton</span>
<span class="title__name">UK-12 Black</span>
</div>
<div class="product__meta-container">
<div class="product__meta-line">
<div class="product__rating-stars">
<div class="fx-rating-stars">
<div class="fx-rating-stars__stars">
<svg class="fx-icon fx-icon-star-fill fx-rating-stars__icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg>
<div class="fx-rating-stars__filler" style="width: 100%">
<svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> </div>
</div>
<div class="fx-rating-stars__description">461</div>
</div>
</div>
</div>
</div>
<div class="product__description">
<ul class="fx-list">
<li class="product__description-item fx-list__item fx-list__item--circle">Body & neck: Basswood</li>
<li class="product__description-item fx-list__item fx-list__item--circle">Fretboard: roseacer (thermally treated maple wood)</li>
<li class="product__description-item fx-list__item fx-list__item--circle">12 Frets</li>
</ul>
</div>
<div class="product__availability fx-availability fx-availability--in-stock">
disponibile
</div>
</div>
<div class="product__price">
<div class="fx-price-group product__price-group">
<span class="fx-typography-price-primary fx-price-group__primary product__price-primary"> € 19,90
</span>
</div>
</div> </a>
<div class="product__actions">
<div class="action__compare js-action__compare" data-number="257768" data-reference-tracking="">
<svg class="fx-icon fx-icon-compare compare-icon">
<use xlink:href="/static/icons/fxicons.svg?v=9822#compare"></use>
</svg> </div>
<div class="action__wishlist js-action__wishlist" data-caid="" data-is-on-wishlist="" data-number="257768" data-reference-tracking="" data-wishlist-entry-id="" data-wishlist-url="">
<svg class="fx-icon fx-icon-heart wishlist-icon fx-size--md-md-lg">
<use xlink:href="/static/icons/fxicons.svg?v=9822#heart"></use>
</svg> <svg class="fx-icon fx-icon-heart-filled wishlist-icon fx-size--md-md-lg fx-icon--active fx-icon--hidden">
<use xlink:href="/static/icons/fxicons.svg?v=9822#heart-filled"></use>
</svg> </div>
<div class="action__basket js-action__basket" data-is-download="" data-is-voucher="" data-number="257768" data-reference-tracking="">
<form action="" class="basket-form" method="post">
<input name="ar" type="hidden" value="257768"/>
<button class="basket-button" type="submit">
<svg class="fx-icon fx-icon-cart basket-icon">
<use xlink:href="/static/icons/fxicons.svg?v=9822#cart"></use>
</svg> </button>
</form>
</div>
</div>
</div>
</div>
|
|
bs4.element.Tag
Se guardiamo attentamente, vedremo del testo compatibile con gli elementi disponibili per ogni prodotto. Ad esempio, possiamo scorgere un Aggiungi al carrello
, un Harley Benton
e un €17,90
. Possiamo procedere analizzando ricorsivamente il contenuto di ciascun container per estrarre le informazioni che ci servono.
Cerchiamo di estrarre adesso il manufacturer (Harley Benton
, nel caso del primo container). Se ispezioniamo il DOM con il browser (click con il tasto destro sul manufacturer e click su inspect), notiamo che esso è contenuto in uno span di classe title__manufacturer
.
Proviamo ad analizzare il primo container per vedere se contiene uno span di quella classe:
|
|
[<span class="title__manufacturer">Harley Benton</span>]
|
|
'Harley Benton'
Abbiamo trovato il manufacturer! Il comando findAll, ci restituisce però una lista di oggetti (uno solo in questo caso). Possiamo accedere al testo contenuto nell’oggetto come segue:
|
|
Harley Benton
Allo stesso modo, ispezionando il DOM, notiamo che il nome del modello è contenuto in uno span di classe title__name
:
Estraiamo il modello dal container come segue:
|
|
UK-12 Black
Similmente notiamo che le immagini dei prodotti si trovano in un div
di classe product-image
:
|
|
[<a class="product__image" href="harley_benton_uk_12_black.htm?listPosition=0&type=category">
<picture class="fx-picture">
<source data-srcset="https://thumbs.static-thomann.de/thumb//thumb220x220/pics/prod/257768.webp" srcset="" type="image/webp"> <source data-srcset="https://thumbs.static-thomann.de/thumb//thumb220x220/pics/prod/257768.jpg" srcset=""> <img alt="Harley Benton UK-12 Black" class="fx-image responsive-image product-image lazyload loaded" data-src="" height=" 220" src="https://images.static-thomann.de/pics/images/misc/placeholder.svg" width=" 220"/>
</source></source></picture>
</a>]
Notiamo che l’immagine si trova dentro il tag img
all’interno di picture
, source
e nuovamente source
. Possiamo accedere come segue:
|
|
<source data-srcset="https://thumbs.static-thomann.de/thumb//thumb220x220/pics/prod/257768.jpg" srcset=""> <img alt="Harley Benton UK-12 Black" class="fx-image responsive-image product-image lazyload loaded" data-src="" height=" 220" src="https://images.static-thomann.de/pics/images/misc/placeholder.svg" width=" 220"/>
</source>
La URL della immagine, si trova nell’attributo data-srcset
, al quale possiamo accedere come segue:
|
|
https://thumbs.static-thomann.de/thumb//thumb220x220/pics/prod/257768.jpg
Possiamo verificare che l’immagine sia corretta inserendo la URL nel browser, o caricandola mediante IPython:
|
|
In maniera simile (analizzando il DOM manualmente e controllando) possiamo estrarre il prezzo:
|
|
€ 19,90
Convertiamo la stringa in un numero rimuovendo il simbolo dell’euro e sostituendo la virgola con un punto:
|
|
19.9
Domanda 2 Sarebbe possibile ottenere lo stesso risultato senza rimuovere esplicitamente il simbolo dell’euro e utilizzando invece le espressioni regolari? Che espressione regolare servirebbe in questo caso? |
In maniera del tutto analoga otteniamo il numero di revisioni e la disponibilità:
|
|
461 disponibile
Estrarre il rating del prodotto è un po’ meno immediato, in quanto esso non è riportato direttamente in termini numerici, ma solo mostrato mediante le stelline. Per ottenere l’informazione relativa al rating, dobbiamo quandi analizzare il CSS usato per mostrare il numero corretto di stelle. Ispezionando gli elementi grafici relativi alle stelline, scopriamo che il numero corretto di stelle viene mostrato mediante un overlay:
La quantità di stelline da mostrare è specificata mediante la larghezza dell’overlay, espressa in percentuale width: 83.971%
. Per ottenere il rating, dobbiamo dunque isolare questo numero. Iniziamo cercando i div di classe fx-rating-stars__filler
:
|
|
[<div class="fx-rating-stars__filler" style="width: 100%">
<svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> <svg class="fx-icon fx-icon-star-fill fx-rating-stars__filler-icon product__rating-star">
<use xlink:href="/static/icons/fxicons.svg?v=9822#star-fill"></use>
</svg> </div>]
Abbiamo una serie di div
annidati. Siamo interessati allo stile di quello più esterno:
|
|
width: 100%
|
|
'80'
Possiamo isolare il numero mediante una espressione regolare:
|
|
'80'
Domanda 3 E’ possibile ottenere lo stesso risultato senza usare le espressioni regolari? Come? |
Per cui scriviamo:
|
|
80.0
Possiamo automatizzare l’estrazione di queste informazioni in tutta la pagina mediante un ciclo for:
|
|
Adesso facciamo scraping della pagina e inseriamo il risultato in un DataFrame:
|
|
Visualizziamo le prime righe del DataFrame:
|
|
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 25 entries, 0 to 24
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 manufacturer 25 non-null object
1 model 25 non-null object
2 img_url 25 non-null object
3 price 25 non-null float64
4 review_count 25 non-null int64
5 availability 25 non-null object
6 rating 25 non-null float64
dtypes: float64(2), int64(1), object(4)
memory usage: 1.5+ KB
None
manufacturer | model | img_url | price | review_count | availability | rating | |
---|---|---|---|---|---|---|---|
0 | Harley Benton | UK-12 Black | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 461 | disponibile | 80.0 |
1 | Harley Benton | Ukulele UK-11DW Brown | https://thumbs.static-thomann.de/thumb//thumb2... | 18.9 | 745 | disponibile | 84.0 |
2 | Harley Benton | DOTU UKE-S Pirate Skull | https://thumbs.static-thomann.de/thumb//thumb2... | 24.9 | 10 | disponibile | 98.0 |
3 | Harley Benton | Hawaii Koa Soprano Ukulele | https://thumbs.static-thomann.de/thumb//thumb2... | 89.0 | 16 | disponibile | 92.0 |
4 | Harley Benton | UK-12 Soprano Ukulele Blue | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 172 | disponibile | 78.0 |
Il DataFrame contiene $25$ righe relative ai $25$ oggetti presenti nella pagina.
2.1 Download delle Immagini
Abbiamo conservato le URL alle immagini nel DataFrame. Tuttavia, vista la natura dinamica dei siti web, queste potrebbero cambiare URL ed essere in futuro irraggiungibili. E’ quindi in genere una buona idea scaricare anche questo tipo di dato e inserirlo in una cartella apposita. Iniziamo creando una cartella thumann_img
:
|
|
Adesso dobbiamo scaricare ogni immagine e conservarla con un nome di file univoco. Per evitare che immagini diverse abbiano lo stesso nome, derivereremo il nome del file dall’id del DataFrame. Vediamo un esempio di download e salvataggio dell’immagine nel caso in una riga del dataframe:
|
|
manufacturer Harley Benton
model UK-12 Black
img_url https://thumbs.static-thomann.de/thumb//thumb2...
price 19.9
review_count 461
availability disponibile
rating 80.0
Name: 0, dtype: object
Deriveremo il nome dell’immagine dall’id della riga (contenuta in name
) secondo il seguente formato:
|
|
Dove id
indica l’id della riga, mentre ext
è l’estensione. Per ottenere l’estensione corretta, recuperiamo quella contenuta nell URL mediante una espressione regolare:
|
|
'jpg'
Il nome del file di destinazione diventa dunque:
|
|
'img_00000.jpg'
Il percorso completo del file di destinazione possiamo ottenerlo concatenando il path della cartella e il nome del file mediante la funzione join
di os.dir
:
|
|
'thomann_img/img_00000.jpg'
Possiamo dunque scaricare il file mediante urllib
come segue:
|
|
('thomann_img/img_00000.jpg', <http.client.HTTPMessage at 0x7fb0529486a0>)
Proviamo a caricare l’immagine con PIL
per controllare che sia stata correttamente salvata su disco:
|
|
Scriviamo adesso una funzione per automatizzare il download delle immagini. Per poter tenere traccia delle immagini che salviamo sul disco, inseriremo un nuovo campo img_path
al DataFrame.
|
|
Utilizziamo la funzione per scaricare le immagini:
|
|
Visualizziamo le prime righe del nuovo DataFrame:
|
|
manufacturer | model | img_url | price | review_count | availability | rating | img_path | |
---|---|---|---|---|---|---|---|---|
0 | Harley Benton | UK-12 Black | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 461 | disponibile | 80.0 | thomann_img/img_00000.jpg |
1 | Harley Benton | Ukulele UK-11DW Brown | https://thumbs.static-thomann.de/thumb//thumb2... | 18.9 | 745 | disponibile | 84.0 | thomann_img/img_00001.jpg |
2 | Harley Benton | DOTU UKE-S Pirate Skull | https://thumbs.static-thomann.de/thumb//thumb2... | 24.9 | 10 | disponibile | 98.0 | thomann_img/img_00002.jpg |
3 | Harley Benton | Hawaii Koa Soprano Ukulele | https://thumbs.static-thomann.de/thumb//thumb2... | 89.0 | 16 | disponibile | 92.0 | thomann_img/img_00003.jpg |
4 | Harley Benton | UK-12 Soprano Ukulele Blue | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 172 | disponibile | 78.0 | thomann_img/img_00004.jpg |
Controlliamo il numero di immagini in thomann_img
:
|
|
25
2. Navigare Tra le Pagine
Abbiamo visto che la lista dei prodotti si estende su più pagine. Vediamo adesso come navigare tra le pagine per raccogliere le informazioni su tutti i prodotti. Andiamo in fondo alla lista e clicchiamo su uno dei pulsanti per aprire le pagine. Ci accorgiamo che il formato dei link delle pagine è il seguente:
/it/ukulele_soprani.html?ls=25&pg=1
Notiamo inoltre, che andando a una pagina non esistente (es. https://www.thomann.de/it/ukulele_soprani.html?ls=25&pg=1000), viene visualizzata una pagina vuota.
Sfrutteremo queste due caratteristiche per navigare tra le pagine.
Dato un numero di pagina, possiamo trovare il link relativo con il formato:
|
|
'https://www.thomann.de/it/ukulele_soprani.html?pg=1&ls=25'
Per rendere il codice indipendente rispetto a pagina e dominio, estraiamo il nome della pagina “https://www.thomann.de/it/ukulele_soprani.html" dalla url della prima pagina mediante espressione regolare:
|
|
https://www.thomann.de/it/ukulele_soprani.html
Riscriviamo dunque il formato come segue:
|
|
'https://www.thomann.de/it/ukulele_soprani.html?pg=1&ls=25'
Preleviamo dunque gli indici della prima (esclusa la corrente) e ultima pagina dalla lista dei link:
Rivediamo lo script precedente per automatizzare la navigazione tra le pagine e lo scraping:
|
|
Utilizziamo la funzione per effettuare lo scraping:
|
|
Visualizziamo alcune informazioni sul DataFrame:
|
|
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 206 entries, 0 to 205
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 manufacturer 206 non-null object
1 model 206 non-null object
2 img_url 206 non-null object
3 price 206 non-null float64
4 review_count 206 non-null int64
5 availability 206 non-null object
6 rating 206 non-null float64
dtypes: float64(2), int64(1), object(4)
memory usage: 11.4+ KB
None
manufacturer | model | img_url | price | review_count | availability | rating | |
---|---|---|---|---|---|---|---|
0 | Harley Benton | UK-12 Black | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 461 | disponibile | 80.0 |
1 | Harley Benton | Ukulele UK-11DW Brown | https://thumbs.static-thomann.de/thumb//thumb2... | 18.9 | 745 | disponibile | 84.0 |
2 | Harley Benton | DOTU UKE-S Pirate Skull | https://thumbs.static-thomann.de/thumb//thumb2... | 24.9 | 10 | disponibile | 98.0 |
3 | Harley Benton | Hawaii Koa Soprano Ukulele | https://thumbs.static-thomann.de/thumb//thumb2... | 89.0 | 16 | disponibile | 92.0 |
4 | Harley Benton | UK-12 Soprano Ukulele Blue | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 172 | disponibile | 78.0 |
2.1 Navigazione tra le Pagine dei Prodotti
Vediamo adesso come usare la navigazione automatica tra le pagine per fare scraping di altri prodotti, oltre a quelli contenuti nella pagina analizzata finora. Osservando la barra di navigazione in alto, notiamo che la pagina che abbiamo analizzato finora è accedibile da una pagina “ukulele”:
Cliccando su “ukuleles”, accediamo alla pagina https://www.thomann.de/it/ukuleles.html
, che contiene link a diverse altre pagine contenenti le liste di prodotti appartenenti a certe sotto-categorie. Tra queste scorgiamo la pagina “Ukulele Soprani” analizzata in precedenza.
Analizzando il DOM, scropiamo che i vari prodotti sono contenuti all’interno di un elenco puntato dentro un div di classe fx-category-grid
:
Carichiamo la pagina e accediamo a tutti i link che si trovani nel div fx-category-grid
:
|
|
['https://www.thomann.de/it/ukulele_soprani.html',
'https://www.thomann.de/it/ukulele_da_concerto.html',
'https://www.thomann.de/it/ukulele_tenori.html',
'https://www.thomann.de/it/ukulele_baritoni.html',
'https://www.thomann.de/it/ukulele_bassi.html',
'https://www.thomann.de/it/ukulele_design_alternativo.html',
'https://www.thomann.de/it/ukulele_tradizionali1.html',
'https://www.thomann.de/it/custodie_per_ukulele.html',
'https://www.thomann.de/it/supporti_per_ukulele.html',
'https://www.thomann.de/it/tracolle-per-ukulele.html',
'https://www.thomann.de/it/plettri_per_ukulele.html',
'https://www.thomann.de/it/corde-per-ukuleles.html',
'https://www.thomann.de/it/pickup_per_ukulele.html',
'https://www.thomann.de/it/meccaniche_per_ukulele.html',
'https://www.thomann.de/it/meccaniche-ukulele.html',
'https://www.thomann.de/it/corso_di_ukulele1.html',
'https://www.thomann.de/it/libri_canzoni_per_ukulele2.html']
Analogamente, possiamo ottenere il testo di ogni link (ovvero il nome della categoria del prodotto) come segue:
|
|
['Ukulele Soprani',
'Ukulele da Concerto',
'Ukulele Tenori',
'Ukulele Baritoni',
'Ukulele Bassi',
'Ukulele dal design alternativo',
'Ukulele tradizionali',
'Custodie per Ukulele',
'Supporti per Ukulele',
'Tracolle per ukulele',
'Plettri per Ukulele',
'Corde per Ukuleles',
'Pickup per Ukulele',
'Meccaniche per Ukulele',
'Meccaniche Ukulele',
'Corso di Ukulele',
'Spartiti per Ukulele']
Utilizzando le funzioni scritte in precedenza, possiamo dunque automatizzare lo scraping di tutti i prodotti come segue:
|
|
Visualizziamo alcuni dati relativi al DataFrame ottenuto:
|
|
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1210 entries, 0 to 1209
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 manufacturer 1210 non-null object
1 model 1210 non-null object
2 img_url 1210 non-null object
3 price 1210 non-null float64
4 review_count 1210 non-null int64
5 availability 1210 non-null object
6 rating 1210 non-null float64
7 category 1210 non-null object
dtypes: float64(2), int64(1), object(5)
memory usage: 75.8+ KB
None
manufacturer | model | img_url | price | review_count | availability | rating | category | |
---|---|---|---|---|---|---|---|---|
0 | Harley Benton | UK-12 Black | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 461 | disponibile | 80.0 | Ukulele Soprani |
1 | Harley Benton | Ukulele UK-11DW Brown | https://thumbs.static-thomann.de/thumb//thumb2... | 18.9 | 745 | disponibile | 84.0 | Ukulele Soprani |
2 | Harley Benton | DOTU UKE-S Pirate Skull | https://thumbs.static-thomann.de/thumb//thumb2... | 24.9 | 10 | disponibile | 98.0 | Ukulele Soprani |
3 | Harley Benton | Hawaii Koa Soprano Ukulele | https://thumbs.static-thomann.de/thumb//thumb2... | 89.0 | 16 | disponibile | 92.0 | Ukulele Soprani |
4 | Harley Benton | UK-12 Soprano Ukulele Blue | https://thumbs.static-thomann.de/thumb//thumb2... | 19.9 | 172 | disponibile | 78.0 | Ukulele Soprani |
In poco tempo, siamo riusciti ad estrarre record diversi oggetti.
3. Analisi dei Dati Ottenuti
Vediamo adesso di analizzare in breve i dati ottenuti per capire qualcosa di più sui prodotti venduti dallo store. Iniziamo con delle semplici statistiche sulle variabili quantitative, che possono essere ottenute mediante il metodo describe
dei DataFrame:
|
|
price | review_count | rating | |
---|---|---|---|
count | 1210.000000 | 1210.000000 | 1210.000000 |
mean | 117.397620 | 30.933058 | 66.072727 |
std | 158.406797 | 238.981080 | 40.938793 |
min | 0.990000 | 0.000000 | 0.000000 |
25% | 19.900000 | 0.000000 | 0.000000 |
50% | 49.000000 | 3.000000 | 88.000000 |
75% | 149.000000 | 12.000000 | 96.000000 |
max | 999.000000 | 6892.000000 | 100.000000 |
Vediamo adesso quanti prodotti sono contenuti in ciascuna categoria:
|
|
Notiamo che le categorie che contengono più oggetti sono quelle relative agli ukulele da concerto, tenori e soprani. Calcoliamo e visualizziamo adesso il prezzo medio per categoria. Questa volta però utilizziamo un grafico a barre:
|
|
Gli oggetti più costosi in media sono gli “Ukulele Bassi” e gli “Ukulele dal design alternativo”. Cerchiamo di capire adesso quali sono gli oggetti più popolari. Inizieremo visualizzando il numero di review presenti in ogni categoria:
|
|
Domanda 4 Confrontando l’ultimo grafico con il precedente, sembra esserci una correlazione tra il numero di review e il prezzo degli oggetti? Come la si potrebbe spiegare? |
Pare che gli oggetti più recensiti siano i supporti per ukulele e le meccaniche per ukulele. Vediamo adesso qual è il rating medio degli oggetti in ogni categoria:
|
|
GLi oggetti che hanno i rating più bassi sono gli ukulele soprani. Proviamo a vedere perché. Analizziamo solo quella categoria, mostrando il rating medio per marca:
|
|
Domanda 5 Stando a quanto mostrato nell’ultimo grafico, su che tipo di prodotti si dovrebbe puntare per migliorare gli score ottenuti dal sito? |
Esercizi
Esercizio 1 Si scarichino tutte le immagini relative all’ultimo DataFrame creato. |
Esercizio 2 Le pagine relative ai singoli prodotti contengono delle immagini ad alta risoluzione degli oggetti, mentre le immagini da noi scaricate sono a bassa risoluzione. Si modifichi il codice visto in precedenza per navigare nella pagina ed estrarre una delle immagini associate al prodotto. Si scarichino poi tutte le immagini come visto nel laboratorio. |
Esercizio 3 Le pagine relative ai singoli prodotti contengono dei link ai testi delle review. Si scriva un codice che permetta di scaricare tutte le review di tutti i prodotti contenuti nell’ultimo DataFrame. Per ogni review, si conservi anche il relativo rating. Si faccia in modo di mantenere una relazione tra i prodotti e le relative review. |
Esercizio 4 Si scelga un altro sito a piacere e si ripeta su di esso una analisi simile a quella vista in laboratorio. |
Referenze
- Documentazione di Python 3. https://docs.python.org/3/
- Documentazione di Numpy. http://www.numpy.org/
- Documentazione di Pandas: https://pandas.pydata.org/pandas-docs/stable/