2016-03-03 4 views
3

В настоящее время у меня есть две функции для извлечения HTML-кода <body> из Python и возврата его в виде пакета слов. Они дают эквивалентную мощность. Я также очищаю различные теги, которые в противном случае давали бы мне мусорный текст (например, код <script>).Каков самый быстрый, самый безошибочный способ извлечения и очистки основного текста HTML в Python?

def html_to_bow_bs(text): 
    if text is None or len(text)==0: 
     return [] 

    soup = BeautifulSoup(text, "lxml",parse_only=SoupStrainer('body')) 

    # Remove all irrelevant tags 
    for elem in soup.findAll(['script','style','a']): 
     elem.extract() 
    body_text = soup.findAll("body") 
    if len(body_text) == 0: 
     return [] 

    # Encoding. Remove extra whitespace and unprintable characters 
    the_text = body_text[0].get_text().encode('utf-8') 
    the_text = str(the_text) 
    the_text = the_text.strip() 
    the_text = re.sub(r'[^\x00-\x7F]+',' ',the_text) 
    return [w.lower() for w in the_text.split()] 




def html_to_bow_bs_lxml(text): 
    if text is None or len(text)==0: 
     return [] 
    body_re = re.findall('<body(.*?)</body>', text, flags=re.DOTALL) 
    if len(body_re) == 0: 
     return [] 
    fragment = body_re[0] 

    # Remove irrelevant tags 
    fragment = re.sub(r'<script.*?</script>', ' ', fragment, flags=re.DOTALL) 
    fragment = re.sub(r'<style.*?</style>', ' ', fragment, flags=re.DOTALL) 
    text = "<body" + fragment + "</body>" 
    soup = BeautifulSoup(text, "lxml") 

    if soup is None: 
     return [] 

    # Remote more irrelevant tags 
    for elem in soup.findAll(['a']): 
     elem.extract() 

    # Encoding. Remove extra whitespace and unprintable characters 
    the_text = body_text[0].get_text().encode('utf-8') 
    the_text = str(the_text) 
    the_text = the_text.strip() 
    the_text = re.sub(r'[^\x00-\x7F]+',' ',the_text) 
    return [w.lower() for w in the_text.split()] 

Мой Главное требование является соответствие вывод: что множество слов из html_to_bow_bs_lxml(text) матчей html_to_bow_bs(text). В настоящее время оба находятся на одном уровне с временем выполнения; для 330 страниц, они работают около 20 секунд (медленно!). Если я удалю и замените свой последний soup.findAll(['a'])...extract() в моей второй функции с помощью регулярных выражений, я могу сбрить 6 секунд с моего времени. Замена BeautifulSoup в целом с помощью lxml.etree может сбрить дополнительные 10 секунд, делая общее время работы около 3-4 секунд. Однако при замене с помощью регулярных выражений

  1. вывод не всегда совпадает. При замене BeautifulSoup либо выход не соответствует, либо
  2. моя программа вылетает во время обработки из-за плохо сформированного HTML. Как увеличить скорость при сохранении правильности?

Я видел различные рекомендации по извлечению HTML с Python, как правило, в StackOverflow, но это датируется несколькими годами (например, 2012 год). С тех пор, по понятным причинам, было много обновлений для библиотек.

(Я также попытался pyquery, но это не всегда извлечь тело правильно.)

ответ

1

Вы сделали много, чтобы сделать это быстро - суп ситечко и lxml анализатор, как правило, первым что нужно попробовать при оптимизации синтаксического анализа BeautifulSoup.

Ниже приведены некоторые улучшения данного кода.

Удалить проверку существования тела:

body_text = soup.findAll("body") 
if len(body_text) == 0: 
    return [] 

и использовать find() вместо этого.

Заменить if text is None or len(text)==0: только if not text:.

Strip via get_text(strip=True).


Усовершенствованный код:

def html_to_bow_bs(text): 
    if not text: 
     return [] 

    soup = BeautifulSoup(text, "lxml", parse_only=SoupStrainer('body')) 

    # Remove all irrelevant tags 
    for elem in soup.find_all(['script','style','a']): 
     elem.extract() 

    body = soup.find("body") 
    if not body: 
     return [] 

    the_text = body.get_text(strip=True).encode('utf-8') 
    the_text = re.sub(r'[^\x00-\x7F]+', ' ', the_text) 
    return [w.lower() for w in the_text.split()] 

Это просто микро-улучшений, и я не думаю, что они собираются изменить общую картину производительности. То, что я хотел бы также посмотреть на:

  • запуска сценария с помощью pypy (beautifulsoup4 это compatible, но вы не могли бы использовать lxml анализатор - попробуйте с html.parser или html5lib). Вы можете выиграть много, даже не изменив код вообще.
+0

Спасибо! Похоже, что были некоторые дополнительные улучшения. Однако 'body.get_text (strip = True)' дал намного меньше слов, чем разделение 'get_text()' и 'strip()' на две разные функции. Я заменил это на «.» .join ([текст для текста в body.stripped_strings]). Encode ('utf-8') 'дает больше слов, которые были фактически упущены моими подходами. Это редактирование, наряду с вышеизложенным, дает почти равную производительность во время выполнения. – Matt

+0

@Matt да, спасибо, я не ожидал значительного повышения производительности от этих изменений. А как насчет пипы? – alecxe

+0

Теперь, попробуйте сейчас. Извините, у меня закончилось свободное пространство комментариев. :) – Matt

0

Использование запросов модуля и BS4

Это простейший способ печати основного текста.

import requests 
from bs4 import BeautifulSoup 
url = "yourUrl" 
r = requests.get(url) 
soup = BeautifulSoup(r.content, 'lxml') 
items = soup.find_all('body') 
for item in items: 
    print item.text 

Примечание: Если вы печатаете все тело, он также будет печатать JQuery и функции JavaScript в случае, если есть в наличии.

+0

К сожалению, поскольку jQuery и JavaScript будут включены в вывод, это не соответствует вышеуказанным спецификациям. Мы также можем предположить, что текст HTML вводится в функции, поэтому «импортные запросы» не нужны. – Matt

Смежные вопросы