Python, использующий Beautiful Soup для обработки HTML на конкретном контенте - PullRequest
5 голосов
/ 11 апреля 2011

Итак, когда я решил разобрать контент с сайта.Например, http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx

Я хочу разобрать ингредиенты в текстовый файл.Ингредиенты расположены в:

, и внутри каждого ингредиента сохраняетсямежду

Кто-то был достаточно хорош, чтобы предоставить код с помощью регулярных выражений, но это сбивает с толку, когда вы модифицируете сайт-сайт.Поэтому я хотел использовать Beautiful Soup, поскольку в нем много встроенных функций.За исключением того, что я не могу понять, как на самом деле это сделать.

Код:

import re
import urllib2,sys
from BeautifulSoup import BeautifulSoup, NavigableString
html = urllib2.urlopen("http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx")
soup = BeautifulSoup(html)

try:

        ingrdiv = soup.find('div', attrs={'class': 'ingredients'})

except IOError: 
        print 'IO error'

Так вы начинаете?Я хочу найти фактический класс div, а затем разобрать все ингредиенты, находящиеся в классе li.

Любая помощь будет принята с благодарностью!Спасибо!

Ответы [ 2 ]

4 голосов
/ 11 апреля 2011
import urllib2
import BeautifulSoup

def main():
    url = "http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx"
    data = urllib2.urlopen(url).read()
    bs = BeautifulSoup.BeautifulSoup(data)

    ingreds = bs.find('div', {'class': 'ingredients'})
    ingreds = [s.getText().strip() for s in ingreds.findAll('li')]

    fname = 'PorkChopsRecipe.txt'
    with open(fname, 'w') as outf:
        outf.write('\n'.join(ingreds))

if __name__=="__main__":
    main()

результат в

1/4 cup olive oil
1 cup chicken broth
2 cloves garlic, minced
1 tablespoon paprika
1 tablespoon garlic powder
1 tablespoon poultry seasoning
1 teaspoon dried oregano
1 teaspoon dried basil
4 thick cut boneless pork chops
salt and pepper to taste

.


Последующий ответ на @eyquem:

from time import clock
import urllib
import re
import BeautifulSoup
import lxml.html

start = clock()
url = 'http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx'
data = urllib.urlopen(url).read()
print "Loading took", (clock()-start), "s"

# by regex
start = clock()
x = data.find('Ingredients</h3>')
patingr = re.compile('<li class="plaincharacterwrap">\r\n +(.+?)</li>\r\n')
res1 = '\n'.join(patingr.findall(data,x))
print "Regex parse took", (clock()-start), "s"

# by BeautifulSoup
start = clock()
bs = BeautifulSoup.BeautifulSoup(data)
ingreds = bs.find('div', {'class': 'ingredients'})
res2 = '\n'.join(s.getText().strip() for s in ingreds.findAll('li'))
print "BeautifulSoup parse took", (clock()-start), "s  - same =", (res2==res1)

# by lxml
start = clock()
lx = lxml.html.fromstring(data)
ingreds = lx.xpath('//div[@class="ingredients"]//li/text()')
res3 = '\n'.join(s.strip() for s in ingreds)
print "lxml parse took", (clock()-start), "s  - same =", (res3==res1)

дает

Loading took 1.09091222621 s
Regex parse took 0.000432703726233 s
BeautifulSoup parse took 0.28126133314 s  - same = True
lxml parse took 0.0100940499505 s  - same = True

Regex намного быстрее (кроме случаев, когда это неправильно); но если вы подумаете о загрузке страницы и ее синтаксическом анализе, BeautifulSoup по-прежнему занимает только 20% времени выполнения. Если вас сильно беспокоит скорость, я рекомендую вместо этого lxml.

2 голосов
/ 11 апреля 2011

Да, для каждого сайта должен быть написан специальный шаблон регулярных выражений.

Но я думаю, что

1 - процедуры, выполняемые с помощью Beautiful Soup, также должны быть адаптированы к каждому месту.

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

Мне любопытно посмотреть, какие процедуры нужно проводить с Beautiful Soup, чтобы получить те же результаты, которые я получил за несколько минут. Когда-то я пытался выучить красивый суп, но я ничего не понимал в этом беспорядке. Я должен попробовать еще раз, теперь я немного опытнее в Python. Но регулярные выражения были в порядке и достаточны для меня до сих пор

Вот код для этого нового сайта:

import urllib
import re

url = 'http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx'

sock = urllib.urlopen(url)
ch = sock.read()
sock.close()

x = ch.find('Ingredients</h3>')

patingr = re.compile('<li class="plaincharacterwrap">\r\n +(.+?)</li>\r\n')

print '\n'.join(patingr.findall(ch,x))

.

EDIT

Я скачал и установил BeautifulSoup и провел сравнение с регулярным выражением.

Я не думаю, что сделал ошибку в своем коде сравнения

import urllib
import re
from time import clock
import BeautifulSoup

url = 'http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx'
data = urllib.urlopen(url).read()


te = clock()
x = data.find('Ingredients</h3>')
patingr = re.compile('<li class="plaincharacterwrap">\r\n +(.+?)</li>\r\n')
res1 = '\n'.join(patingr.findall(data,x))
t1 = clock()-te

te = clock()
bs = BeautifulSoup.BeautifulSoup(data)
ingreds = bs.find('div', {'class': 'ingredients'})
ingreds = [s.getText().strip() for s in ingreds.findAll('li')]
res2 = '\n'.join(ingreds)
t2 = clock()-te

print res1
print
print res2
print
print 'res1==res2 is ',res1==res2

print '\nRegex :',t1
print '\nBeautifulSoup :',t2
print '\nBeautifulSoup execution time / Regex execution time ==',t2/t1

результат

1/4 cup olive oil
1 cup chicken broth
2 cloves garlic, minced
1 tablespoon paprika
1 tablespoon garlic powder
1 tablespoon poultry seasoning
1 teaspoon dried oregano
1 teaspoon dried basil
4 thick cut boneless pork chops
salt and pepper to taste

1/4 cup olive oil
1 cup chicken broth
2 cloves garlic, minced
1 tablespoon paprika
1 tablespoon garlic powder
1 tablespoon poultry seasoning
1 teaspoon dried oregano
1 teaspoon dried basil
4 thick cut boneless pork chops
salt and pepper to taste

res1==res2 is  True

Regex : 0.00210892725193

BeautifulSoup : 2.32453566026

BeautifulSoup execution time / Regex execution time == 1102.23605776

Без комментариев!

.

РЕДАКТИРОВАТЬ 2

Я понял, что в своем коде я не использую регулярное выражение, я использую метод , использующий регулярное выражение и find () .

Это метод, который я использую, когда прибегаю к регулярным выражениям, потому что в некоторых случаях он повышает скорость лечения. Это связано с функцией find () , которая работает очень быстро.

Чтобы узнать, что мы сравниваем, нам нужны следующие коды.

В кодах 3 и 4 я учел замечания Ахима в другой ветке сообщений: используя re.IGNORECASE и re.DOTALL, ["\ '] вместо " .

Эти коды разделены, потому что они должны выполняться в разных файлах для получения надежных результатов: я не знаю почему, но если все коды выполняются в одном и том же файле, определенные результирующие времена сильно различаются (0,00075 вместо 0,0022 для примера)

import urllib
import re
import BeautifulSoup
from time import clock

url = 'http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx'
data = urllib.urlopen(url).read()

# Simple regex , without x
te = clock()
patingr = re.compile('<li class="plaincharacterwrap">\r\n +(.+?)</li>\r\n')
res0 = '\n'.join(patingr.findall(data))
t0 = clock()-te

print '\nSimple regex , without x :',t0

и

# Simple regex , with x
te = clock()
x = data.find('Ingredients</h3>')
patingr = re.compile('<li class="plaincharacterwrap">\r\n +(.+?)</li>\r\n')
res1 = '\n'.join(patingr.findall(data,x))
t1 = clock()-te

print '\nSimple regex , with x :',t1

и

# Regex with flags , without x and y
te = clock()
patingr = re.compile('<li class=["\']plaincharacterwrap["\']>\r\n +(.+?)</li>\r\n',
                     flags=re.DOTALL|re.IGNORECASE)
res10 = '\n'.join(patingr.findall(data))
t10 = clock()-te

print '\nRegex with flags , without x and y :',t10

и

# Regex with flags , with x and y 
te = clock()
x = data.find('Ingredients</h3>')
y = data.find('h3>\r\n                    Footnotes</h3>\r\n')
patingr = re.compile('<li class=["\']plaincharacterwrap["\']>\r\n +(.+?)</li>\r\n',
                     flags=re.DOTALL|re.IGNORECASE)
res11 = '\n'.join(patingr.findall(data,x,y))
t11 = clock()-te

print '\nRegex with flags , without x and y :',t11

и

# BeautifulSoup
te = clock()
bs = BeautifulSoup.BeautifulSoup(data)
ingreds = bs.find('div', {'class': 'ingredients'})
ingreds = [s.getText().strip() for s in ingreds.findAll('li')]
res2 = '\n'.join(ingreds)
t2 = clock()-te

print '\nBeautifulSoup                      :',t2

результат

Simple regex , without x           : 0.00230488284125

Simple regex , with x              : 0.00229121279385

Regex with flags , without x and y : 0.00758719458758

Regex with flags , with x and y    : 0.00183724493364

BeautifulSoup                      : 2.58728860791

Использование x не влияет на скорость для простого регулярного выражения.

Регулярное выражение с флагами, без x и y, выполняется дольше, но результат не совпадает с другими, потому что он захватывает дополнительный фрагмент текста. Вот почему в реальном приложении следует использовать регулярное выражение с флагами и x / y.

Более сложное регулярное выражение с флагами и с x и y занимает на 20% меньше времени.

Ну, результаты не очень сильно изменились, с х / у или без него.

Так что мой вывод тот же

использование регулярного выражения, прибегая к find () или нет, остается примерно в 1000 раз быстрее, чем BeautifulSoup, и я оцениваю в 100 раз быстрее чем lxml (я не установил lxml)

.

На то, что вы написали, Хью, я бы сказал:

Когда регулярное выражение неверно, оно не быстрее и не медленнее. Это не работает.

Когда регулярное выражение неверно, кодер делает его правильным, вот и все.

Я не понимаю, почему 95% людей на stackoverflow.com хотят убедить другие 5%, что регулярные выражения не должны использоваться для анализа HTML, XML или чего-либо еще. Я говорю «анализировать», а не «анализировать». Насколько я понял, парсер сначала анализирует ВЕСЬ текста, а затем отображает содержание элементов, которые мы хотим. Напротив, регулярное выражение подходит к тому, что ищется, оно не строит дерево текста HTML / XML или что-то еще, что делает парсер, и что я не очень хорошо знаю.

Итак, я очень доволен регулярными выражениями. У меня нет проблем с написанием даже очень длинных RE, и регулярные выражения позволяют мне запускать программы, которые должны быстро реагировать после анализа текста. BS или lxml будут работать, но это будет хлопотно.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...