Веб-парсинг Python с помощью BeautifulSoup

Sep 16 2020

Я относительно новичок в python, и для выполнения задания мне пришлось написать программу, которая извлекает веб-страницу с помощью BeautifulSoup, извлекает из нее все абзацы и извлекает все слова, оканчивающиеся на «ing», и в конце сохраняет ее в файл с формат «Слово» + табуляция + «количество слов» + «новая строка».

Пока это мой код. Есть ли более питонический способ справиться с этим? Или вообще способы улучшить код?

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

Ответы

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

Если вы проверяете, есть ли в словаре ключ перед добавлением к нему, defaultdictлучшим вариантом может быть a:

from collections import defaultdict

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

intвозвращает 0при вызове без аргументов и 0используется в качестве значения по умолчанию для словаря, когда ключ не существует.


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

Это не очень эффективно. В +=более поздних версиях Python производительность строк улучшилась, но в целом она все еще медленнее. Типичная альтернатива - использование join:

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

# Or just

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

Затем аналогично в sort_and_format:

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

В sort_and_formatвы назвали параметр dict. Это неоптимально по двум причинам:

  • dict - общее название, которое не описывает данные должным образом.
  • dict - это имя встроенного класса, и его затенение делает ваш код более запутанным и не позволяет вам использовать встроенный.

Однако указание типа может быть полезно, поэтому я могу ввести здесь подсказки типа

from typing import Dict

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

Это говорит о том, что функции принимает Dictionary отображения strIngs в intсек, и возвращает strIng


Кроме того sort_and_format, я обнаружил, что когда вы начинаете придерживаться andимен, это может указывать на то, что функция делает слишком много. Вы можете обнаружить, что код будет иметь больше смысла, если сортировка и форматирование будут выполняться отдельно. Эти функции могут обрабатывать только форматирование, и вместо этого им можно передать последовательность для работы. Если эта последовательность отсортирована - отлично, если нет - тоже отлично. Порядок сортировки не имеет значения для целей форматирования.