【问题标题】:Python using Beautiful Soup for HTML processing on specific contentPython 使用 Beautiful Soup 对特定内容进行 HTML 处理
【发布时间】:2011-04-11 00:19:47
【问题描述】:

所以当我决定解析网站内容时。比如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 类中的所有这些成分。

    任何帮助将不胜感激!谢谢!

    【问题讨论】:

      标签: python html parsing beautifulsoup


      【解决方案1】:
      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。

      【讨论】:

      • 美汤看起来很简单,确实。对于这种情况,正则表达式和 BS 相当容易。但我认为在更复杂的情况下,BS 可能更容易管理。有一天我会结束学习 BS。
      【解决方案2】:

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

      但我认为

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

      .

      编辑

      我下载并安装了 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 对简单正则表达式的速度没有影响。

      带有 flags 的正则表达式,没有 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 可以,但那会很麻烦。

      我还有其他的 cmets 要做,但我没有时间做一个主题,事实上,我让其他人随心所欲地做。

      【讨论】:

      • 再次感谢 eyquem!我对python很陌生,今年才开始编程(至少在解析方面),只是做了一些小程序之类的事情。但是似乎python很擅长处理这种东西。
      • 一般来说,用正则表达式解析 html 是邪恶的(参见 stackoverflow.com/questions/1732348/… 的规范响应) - html 解析为带注释的树,正则表达式无法正确处理此问题。是的,在有限的情况下,您可以用螺丝刀敲钉子 - 但您为什么要这样做?
      • @Hugh Bothwell 我厌倦了不断地看到对这个 4352 次投票的帖子的引用。这篇文章是 98% 令人惊叹的文学作品。这是 2% 的其他部分:HTML is not a regular language and hence cannot be parsed by regular expressions. 这是很少的解释。休,我发现你的代码比引用的帖子更有说服力。不,在有限的情况下,人们可能会选择在螺丝上使用锤子而不是螺丝刀:每次都希望程序运行得更快,正如您将在我的帖子中的编辑中看到的那样。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 2017-05-27
      • 1970-01-01
      • 2016-08-02
      • 1970-01-01
      相关资源
      最近更新 更多