Python使用Beautiful Soup对特定内容进行HTML处理

时间:2011-04-11 00:19:47

标签: python html parsing beautifulsoup

所以当我决定解析网站上的内容时。例如,http://allrecipes.com/Recipe/Slow-Cooker-Pork-Chops-II/Detail.aspx

我想将成分解析成文本文件。成分位于:

  

< div class =“ingredients”   style =“margin-top:10px;”>

并且在此之内,每种成分都存储在

之间
  

< li class =“plaincharacterwrap”>

有人很好地使用正则表达式提供代码,但是当你从一个站点到另一个站点进行修改时会让人感到困惑。所以我想使用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 个答案:

答案 0 :(得分:4)

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

正则表达式要快得多(除非它错了);但是如果考虑加载页面并将其解析在一起,BeautifulSoup仍然只占运行时的20%。如果你非常关心速度,我建议使用lxml。

答案 1 :(得分:2)

是的,必须为每个站点编写特殊的正则表达式模式。

但我认为

1-使用Beautiful Soup进行的治疗也必须适应每个地点。

2-regexes写起来并不复杂,并且有一点习惯,可以快速完成

我很想知道必须用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))

修改

我下载并安装了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中,我在另一个帖子中考虑了Achim的评论:使用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%。

嗯,无论是否有x / y,结果都没有太大变化。

  

所以我的结论是一样的

     

使用正则表达式,诉诸于    find()与BeautifulSoup相比,大约快了1000倍,   而我估计要快100倍   lxml(我没有安装lxml)

对于你所写的,休,我会说:

当正则表达式出错时,它不会更快或更慢。它没有运行。

当正则表达式出错时,编码器会使它变得正确,就是这样。

我不明白为什么stackoverflow.com上有95%的人想要说服其他5%的人不得使用正则表达式来分析HTML或XML或其他任何内容。我说“分析”,而不是“解析”。据我所知,解析器首先分析文本的整体,然后显示我们想要的元素的内容。相反,正则表达式适用于搜索的内容,它不会构建HTML / XML文本的树或解析器的其他任何内容,而且我不太了解。

所以,我对正则表达式非常满意。即使是非常长的RE也没有问题,正则表达式允许我运行在分析文本后必须迅速做出反应的程序。 BS或lxml会起作用,但那会很麻烦。

我还有其他意见要做,但我没有时间讨论一个主题,事实上,我让别人按照自己的意愿去做。