Python Web-Scraping z BeautifulSoup

Sep 16 2020

Jestem stosunkowo nowy w Pythonie i dla zadania musiałem napisać program, który pobiera stronę internetową z BeautifulSoup, wyodrębnia z niej wszystkie akapity i wyodrębnia wszystkie wyrazy kończące się na „ing”, a na koniec zapisuje do pliku z format „Słowo” + tabulator + „liczba słów” + „nowa linia”.

To jest mój kod do tej pory. Czy istnieje bardziej pythonowy sposób na rozwiązanie tego problemu? Albo ogólnie sposoby na ulepszenie kodu?

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()

Odpowiedzi

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

Jeśli sprawdzasz, czy słownik ma klucz przed dodaniem do niego, defaultdictlepszym rozwiązaniem może być:

from collections import defaultdict

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

intzwraca, 0gdy jest wywoływana bez argumentów i 0jest używana jako wartość domyślna dla słownika, gdy klucz nie istnieje.


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

To nie jest zbyt wydajne. Wydajność +=na łańcuchach poprawiła się w późniejszych wersjach Pythona, ale nadal jest ogólnie wolniejsza. Typową alternatywą jest użycie join:

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

# Or just

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

Następnie podobnie w sort_and_format:

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

W programie sort_and_formatnazwałeś parametr dict. Jest to nieoptymalne z kilku powodów:

  • dict to nazwa ogólna, która nie opisuje poprawnie danych.
  • dict to nazwa klasy wbudowanej, a cieniowanie sprawia, że ​​kod jest bardziej zagmatwany i uniemożliwia korzystanie z wbudowanej.

Wskazanie typu może być jednak pomocne, więc mogę wprowadzić tutaj wskazówki dotyczące typów

from typing import Dict

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

To mówi, że funkcje akceptują Dictodwzorowanie jonowe strna ints i zwracają wartość string


Również w przypadku sort_and_format, odkryłem, że kiedy zaczynasz trzymać andsię nazw, może to sugerować, że funkcja robi za dużo. Może się okazać, że kod będzie bardziej sensowny, jeśli sortowanie i formatowanie będą wykonywane osobno. Te funkcje mogą obsługiwać wyłącznie formatowanie i zamiast tego można im przekazać sekwencję do pracy. Jeśli ta sekwencja jest posortowana, świetnie, jeśli nie, to też świetnie. Nie ma znaczenia dla celów formatowania, jaki jest porządek sortowania.