Python Web-Scraping mit BeautifulSoup

Sep 16 2020

Ich bin relativ neu in Python und für eine Aufgabe musste ich ein Programm schreiben, das eine Webseite mit BeautifulSoup abruft, alle Absätze daraus extrahiert und alle Wörter extrahiert, die mit "ing" enden, und sie am Ende in einer Datei mit speichern das Format "Word" + Tab + "Wordcount" + "Newline".

Dies ist mein bisheriger Code. Gibt es eine pythonischere Möglichkeit, damit umzugehen? Oder generell Möglichkeiten zur Verbesserung des Codes?

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

Antworten

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

Wenn Sie vor dem Hinzufügen prüfen, ob ein Wörterbuch einen Schlüssel enthält, ist a defaultdictmöglicherweise die bessere Option:

from collections import defaultdict

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

intGibt ein zurück, 0wenn es ohne Argumente aufgerufen wird. Dies 0wird als Standardwert für das Wörterbuch verwendet, wenn der Schlüssel nicht vorhanden ist.


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

Das ist nicht sehr effizient. Die Leistung von +=On-Strings wurde in späteren Versionen von Python verbessert, ist jedoch im Allgemeinen immer noch langsamer. Die typische Alternative ist join:

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

# Or just

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

Dann ähnlich in sort_and_format:

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

In sort_and_formathaben Sie den Parameter benannt dict. Dies ist aus mehreren Gründen nicht optimal:

  • dict ist ein generischer Name, der die Daten nicht richtig beschreibt.
  • dict ist der Name einer integrierten Klasse, und das Abschatten macht Ihren Code verwirrender und verhindert, dass Sie die integrierte Klasse verwenden.

Die Angabe des Typs kann jedoch hilfreich sein, daher möchte ich hier Typhinweise einführen

from typing import Dict

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

Dies besagt , dass die Funktionen einen nehmen Dictionary Mapping strIngs zu ints, und gibt einen string


Außerdem sort_and_formathabe ich festgestellt, dass wenn Sie anfangen, andsich an Namen zu halten, dies darauf hindeuten kann, dass die Funktion zu viel tut. Möglicherweise ist der Code sinnvoller, wenn die Sortierung und Formatierung getrennt erfolgt. Diese Funktionen können rein formatiert werden und können stattdessen mit einer Sequenz versehen werden, mit der gearbeitet werden kann. Wenn diese Sequenz sortiert ist, großartig, wenn nicht, auch großartig. Für die Formatierung der Sortierreihenfolge spielt dies keine Rolle.