Web-Scraping Python avec BeautifulSoup
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
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:
dictest un nom générique qui ne décrit pas correctement les données.dictest 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.