Web-Scraping Python avec BeautifulSoup

Sep 16 2020

Je suis relativement nouveau en python et pour une mission, j'ai dû écrire un programme qui récupère une page Web avec BeautifulSoup, en extrait tous les paragraphes et en extraire tous les mots se terminant par "ing", et finalement l'enregistrer dans un fichier avec le format «Word» + tab + «wordcount» + «newline».

C'est mon code pour l'instant. Y a-t-il une manière plus pythonique de gérer cela? Ou généralement des moyens d'améliorer le code?

from bs4 import BeautifulSoup
import requests
import re


def main():
    site = "https://en.wikipedia.org/wiki/Data_science"
    r = requests.get(site).content
    soup = BeautifulSoup(r)
    ps = soup.findAll('p')
    fulltext = ''
    for p in ps:
        fulltext += p.get_text()
    words = match_words(fulltext)
    formated_words = sort_and_format(words)
    with open(r"Q1_Part1.txt","w") as file:
        file.write(formated_words)


def match_words(string):
    pattern = re.compile(r'\b(\w*ing)\b')
    words = re.findall(pattern, string.lower())
    matching_words = {}
    for word in words:
        if word in matching_words:
            matching_words[word] += 1
        else:
            matching_words[word] = 1
    return matching_words

def sort_and_format(dict):
    ordered_keys = sorted(dict, key=dict.get, reverse=True)
    output_string = ''
    for r in ordered_keys:
        output_string += f"{r}\t{dict[r]}\n"
    return output_string
    
main()

Réponses

3 Carcigenicate Sep 16 2020 at 20:57
if word in matching_words:
    matching_words[word] += 1
else:
    matching_words[word] = 1

Si vous vérifiez si un dictionnaire a une clé avant de l'ajouter, un defaultdictpeut être une meilleure option:

from collections import defaultdict

matching_words = defaultdict(int)
matching_words[word] += 1

intrenvoie a 0lorsqu'il est appelé sans arguments, et qui 0est utilisé comme valeur par défaut pour le dictionnaire lorsque la clé n'existe pas.


fulltext = ''
for p in ps:
    fulltext += p.get_text()

Ce n'est pas très efficace. Les performances de +=on strings se sont améliorées dans les versions ultérieures de Python, mais elles restent généralement plus lentes. L'alternative typique utilise join:

pieces = [p.get_text() for p in ps]
fulltext = "".join(pieces)

# Or just

fulltext = "".join([p.get_text() for p in ps])

Puis de même dans sort_and_format:

output_string = "".join([f"{r}\t{dict[r]}\n"] for r in ordered_keys])

Dans sort_and_format, vous avez nommé le paramètre dict. Ceci est sous-optimal pour plusieurs raisons:

  • dict est un nom générique qui ne décrit pas correctement les données.
  • dict est le nom d'une classe intégrée, et l'observation rend votre code plus confus et vous empêche d'utiliser le.

Indiquer le type peut être utile cependant, je pourrais donc introduire des conseils de type ici

from typing import Dict

def sort_and_format(words: Dict[str, int]) -> str:
    . . .

Cela dit que les fonctions accepte une Dictcartographie ionary strIngs à ints, et retourne un strING


Aussi pour sort_and_format, j'ai trouvé que lorsque vous commencez à vous en tenir andaux noms, cela peut suggérer que la fonction en fait trop. Vous constaterez peut-être que le code aura plus de sens si le tri et le formatage se produisent séparément. Ces fonctions peuvent gérer uniquement le formatage et peuvent recevoir une séquence avec laquelle travailler à la place. Si cette séquence est triée, très bien, sinon, aussi bien. L'ordre de tri n'a pas d'importance pour le formatage.