Une situation courante en web scraping est celle où la liste des résultats d'analyse est très longue et contient des informations hétérogènes.
Par exemple, vous avez peut-être remarqué que nos images précédentes peuvent contenir ou non un attribut alt.
Ou imaginez que nous extrayions tous les liens de l'article. Nous savons tous qu'un article Wikipédia contient BEAUCOUP de liens, et nous ne souhaitons peut-être pas en obtenir la liste complète. Le résultat comprendra des liens externes et internes, des références et des citations ; nous devons donc les classer en plusieurs catégories.
Pour résoudre ce problème, nous allons utiliser une fonction lambda. En gros, la fonction lambda prendra comme paramètre chaque élément de la liste de résultats et appliquera la condition que nous définissons, tout comme si nous utilisions un filtre.
À titre d'exemple pratique, supposons que nous devions extraire tous les liens internes, accéder à leur article et en faire un résumé. Étant donné que l'un des cas d'utilisation de Python est l'intelligence artificielle, cet exemple pourrait constituer une excellente application pour obtenir des données d'entraînement.
Tout d'abord, nous devrons installer la bibliothèque NLTK, car le calcul d'un résumé implique le traitement du langage humain.
pip install -U nltk
Et, bien sûr, l'importer dans notre code :
import re
import nltk
import heapq
# need to download only for the first execution
# warning: the size of the dataset is big; hence it will take time
nltk.download()
Remarque : si vous utilisez macOS, vous risquez d’obtenir une erreur « SSL : certificate verify failed ». Cela peut être dû au fait que Python 3.6 utilise une version intégrée d’OpenSSL. Il vous suffit d’ouvrir le répertoire où vous avez installé Python et d’exécuter ce fichier :
/Your/Path/Here/Python 3.6/Install Certificates.command
Comme vous pouvez le voir, nous avons également importé la bibliothèque re, utilisée pour les opérations avec les expressions régulières, et heapq, une implémentation de la file d’attente heap.
Parfait, nous avons tout ce qu'il faut pour commencer à écrire le code. Commençons par extraire les liens internes. Si vous retournez dans le navigateur, vous remarquerez plusieurs choses concernant les éléments qui nous intéressent.
Ces éléments sont les suivants :
- L'attribut href a une valeur ;
- La valeur de l'attribut href commence par « /wiki/ » ;
- Le parent du lien est une balise ;
Ces caractéristiques nous aideront à distinguer les liens dont nous avons besoin de tous les autres.
Maintenant que nous savons comment trouver les liens, voyons comment les extraire.
count = 0
def can_do_summary(tag):
global count
if count > 10: return False
# Reject if parent is not a paragraph
if not tag.parent.name == 'p': return False
href = tag.get('href')
# Reject if href is not set
if href is None: return False
# Reject is href value does not start with /wiki/
if not href.startswith('/wiki/'): return False
compute_summary(href)
return True
def extract_links(soup):
soup.find_all(lambda tag: tag.name == 'a' and can_do_summary(tag))
def main():
URL = 'https://en.wikipedia.org/wiki/Beer'
page = requests.get(URL)
soup = BeautifulSoup(page.content, 'html.parser')
extract_links(soup)
main()
Bon, que s’est-il passé ici ? En examinant la fonction extract_links(), nous pouvons voir qu’au lieu du nom d’une balise, nous avons passé une fonction lambda en paramètre à la méthode .find_all(). Cela signifie que nous ne sélectionnons que celles qui correspondent à notre condition parmi toutes les balises du document HTML.
Comme vous pouvez le voir, la condition d’une balise est d’être un lien et d’être acceptée par la fonction can_do_summary() définie ci-dessus. À cet endroit, nous rejetons tout ce qui ne correspond pas aux caractéristiques observées précédemment. De plus, nous avons utilisé une variable globale pour limiter le nombre de liens extraits à 10. Si vous avez besoin de tous les liens, n’hésitez pas à supprimer la variable count.
Enfin, nous appelons la fonction compute_summary() pour le lien nouvellement trouvé. C'est là que l'article est résumé.
def compute_summary(href):
global count
full_link = 'https://en.wikipedia.org' + href
page = requests.get(full_link)
soup = BeautifulSoup(page.content, 'html.parser')
# Concatenate article paragraphs
paragraphs = soup.find_all('p')
article_text = ""
for p in paragraphs:
article_text += p.text
# Removing Square Bracket, extra spaces, special characters and digits
article_text = re.sub(r'\[[0-9]*\]', ' ', article_text)
article_text = re.sub(r'\s+', ' ', article_text)
formatted_article_text = re.sub('[^a-zA-Z]', ' ', article_text)
formatted_article_text = re.sub(r'\s+', ' ', formatted_article_text)
# Converting text to sentences
sentence_list = nltk.sent_tokenize(article_text)
# Find frequency of occurrence of each word
stopwords = nltk.corpus.stopwords.words('english')
word_frequencies = {}
for word in nltk.word_tokenize(formatted_article_text):
if word not in stopwords:
if word not in word_frequencies.keys():
word_frequencies[word] = 1
else:
word_frequencies[word] += 1
maximum_frequency = max(word_frequencies.values())
for word in word_frequencies.keys():
word_frequencies[word] = (word_frequencies[word] / maximum_frequency)
# Calculate the score of each sentence
sentence_scores = {}
for sent in sentence_list:
for word in nltk.word_tokenize(sent.lower()):
if word in word_frequencies.keys():
if len(sent.split(' ')) < 30:
if sent not in sentence_scores.keys():
sentence_scores[sent] = word_frequencies[word]
else:
sentence_scores[sent] += word_frequencies[word]
# Pick top 7 sentences with highest score
summary_sentences = heapq.nlargest(7, sentence_scores, key=sentence_scores.get)
summary = '\n'.join(summary_sentences)
count += 1
En résumé, nous effectuons une requête HTTP vers la nouvelle URL et convertissons le résultat en un objet BeautifulSoup, comme nous l’avons fait au début de l’article.
Pour calculer un résumé, nous extrayons tous les paragraphes de l'article et les concaténons. Ensuite, nous supprimons tous les caractères spéciaux susceptibles d'interférer avec les calculs.
En termes simples, un résumé est établi en calculant les mots les plus fréquents et en attribuant à chaque phrase un score basé sur la fréquence de ses mots. À la fin, nous sélectionnons les 7 phrases ayant le score le plus élevé.
Ce n'est pas le sujet de notre article, mais vous pouvez en savoir plus ici si vous êtes curieux ou même passionné par le traitement du langage naturel.