Использование многопроцессорной обработки для улучшения очистки Википедии с BeautifulSoup - PullRequest
1 голос
/ 08 ноября 2019

Я использую BeautifulSoup, чтобы очистить некоторую основную информацию от множества страниц Википедии. Программа работает, но медленно (около 20 минут на 650 страниц). Я пытаюсь использовать многопроцессорность, чтобы ускорить это, но это не работает, как ожидалось. Кажется, что он либо задерживается и ничего не делает, либо выполняет только первые буквы имени каждой страницы.

Используемый код очистки:

#dict where key is person's name and value is proper wikipedia url formatting
all_wikis = { 'Adam Ferrara': 'Adam_Ferrara',
              'Adam Hartle': 'Adam_Hartle',
              'Adam Ray': 'Adam_Ray_(comedian)',
              'Adam Sandler': 'Adam_Sandler',
              'Adele Givens': 'Adele_Givens'}
bios = {}
def scrape(dictionary):
    for key in dictionary:
        #search each page
        page = requests.get(("https://en.wikipedia.org/wiki/" + str(key)))
        data = page.text
        soup = BeautifulSoup(data, "html.parser")
        #get data
        try:
            bday = soup.find('span', attrs={'class' : 'bday'}).text
        except:
            bday = 'Birthday Unknown'
        try:
            birthplace = soup.find('div', attrs={'class' : 'birthplace'}).text
        except:
            birthplace = 'Birthplace Unknown'
        try:
            death_date = (soup.find('span', attrs={'style' : "display:none"}).text
                                                                            .replace("(", "")
                                                                            .replace(")", ""))
            living_status = 'Deceased'
        except:
            living_status = 'Alive'
        try:
            summary = wikipedia.summary(dictionary[key].replace("_", " "))
        except:
            summary = "No Summary"
        bios[key] = {}
        bios[key]['birthday'] = bday
        bios[key]['home_town'] = birthplace
        bios[key]['summary'] = summary
        bios[key]['living_status'] = living_status
        bios[key]['passed_away'] = death_date

IЯ пытался добавить обработку в конец сценария, используя приведенный ниже код, но он не работает или вытягивает только первую букву каждой страницы (например, если я ищу страницу Брюса Ли, вместо этогооткройте страницу Википедии, чтобы найти букву B, а затем выдавите кучу ошибок).

from multiprocessing import Pool, cpu_count

if __name__ == '__main__':
    pool = Pool(cpu_count())
    results = pool.map(func=scrape, iterable=all_wiki)
    pool.close()
    pool.join()

Есть ли лучший способ структурировать мой сценарий для многопроцессорной обработки?

1 Ответ

0 голосов
/ 08 ноября 2019

Здесь есть несколько проблем:

  • dictionary - это ключ каждой строки в диктовке all_wikis. Когда вы затем перебираете эту строку с for key in dictionary:, это дает доступ к каждому символу в строке. Ваш первый запрос - https://en.wikipedia.org/wiki/A, что не является желаемым результатом.
  • str(key) не очень полезно, даже если dictionary было именем. Нам нужно найти правильный URL с all_wikis[name]. Кроме того, избегайте общих переменных, таких как dictionary.
  • Поскольку вы работаете в многопроцессорном режиме, данные, такие как bios, должны использоваться совместно для манипулирования. Проще всего просто использовать возвращаемое значение из функции map, которая представляет собой совокупность всех возвращаемых значений рабочей функции.
  • Есть логические проблемы с вашим анализом. wikipedia.summary не определено. Не будучи уверенным в вашем желаемом результате, он сообщает об Адаме Сэндлере как умершем. Я оставлю это как упражнение для читателя, поскольку этот вопрос в основном касается многопроцессорной обработки.
  • Я не уверен, что многопроцессорная обработка здесь так же желательна, как многопоточность. Так как ваш процесс будет заблокирован с выполнением запросов в 99% случаев, я уверен, что вы можете повысить эффективность, используя гораздо больше потоков (или процессов), чем количество ядер, которые у вас есть. Многопроцессорная обработка более удобна, когда вы выполняете работу, связанную с процессором, что здесь не так;на самом деле очень мало времени будет потрачено на сам процесс Python. Я бы рекомендовал тестировать код, увеличивая количество процессов (или потоков, если вы реорганизуете их для этого) за пределы количества ядер, пока не перестанете видеть улучшения.

Вот некоторый код, с которого можно начать. Я придерживался многопроцессорной обработки в вашем примере и не настраивал логику веб-очистки.

import requests
from bs4 import BeautifulSoup
from multiprocessing import Pool, cpu_count

all_wikis = {'Adam Ferrara': 'Adam_Ferrara',
             'Adam Hartle': 'Adam_Hartle',
             'Adam Ray': 'Adam_Ray_(comedian)',
             'Adam Sandler': 'Adam_Sandler',
             'Adele Givens': 'Adele_Givens'}

def scrape(name):
    data = requests.get("https://en.wikipedia.org/wiki/" + all_wikis[name]).text
    soup = BeautifulSoup(data, "html.parser")
    bio = {}

    try:
        bio['birthday'] = soup.find('span', attrs={'class': 'bday'}).text
    except:
        bio['birthday'] = 'Birthday Unknown'

    try:
        bio['home_town'] = soup.find('div', attrs={'class': 'birthplace'}).text
    except:
        bio['home_town'] = 'Birthplace Unknown'

    try:
        bio['passed_away'] = (soup.find('span', attrs={'style': "display:none"}).text
                                                                        .replace("(", "")
                                                                        .replace(")", ""))
        bio['living_status'] = 'Deceased'
    except:
        bio['living_status'] = 'Alive'

    bio['summary'] = "No Summary"
    return name, bio


if __name__ == '__main__':
    pool = Pool(cpu_count())
    bios = dict(pool.map(func=scrape, iterable=all_wikis))
    pool.close()
    pool.join()
    print(bios)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...