我该如何使用Python HTML解析库从特定的div标签中提取数据?

46

我正在尝试使用Python的HTMLParser库从一个HTML页面中获取一个值。 我想要获取的值位于这个HTML元素中:

...
<div id="remository">20</div>
...

这是我的HTMLParser类:

class LinksParser(HTMLParser.HTMLParser):
  def __init__(self):
    HTMLParser.HTMLParser.__init__(self)
    self.seen = {}

  def handle_starttag(self, tag, attributes):
    if tag != 'div': return
    for name, value in attributes:
    if name == 'id' and value == 'remository':
      #print value
      return

  def handle_data(self, data):
    print data

p = LinksParser()
f = urllib.urlopen("http://example.com/somepage.html")
html = f.read()
p.feed(html)
p.close()

我希望类的功能能够获取值为20。


1
如果你需要大量的HTML解析,可以尝试使用Beautiful Soup - zvone
4
那个库是否包含在Python标准库中?我遇到过它,但选择坚持使用HTMLParser。 - Martin
1
@zvone 为什么BeautifulSoup在HTML解析方面更好?它仍然是一个推荐的模块吗?谢谢。 - tommy.carstensen
1
@tommy.carstensen BeautifulSoup4 通常被推荐用于网络爬虫和解析特定标签的HTML。它具有定位特定标签的方法,使用lxml和html5lib库,并处理传入文档到Unicode的转换以及将输出文档转换为UTF-8。简而言之,它可以在几行代码中完成您想要对丑陋的HTML页面进行的所有操作。请查看bs4文档! :) - DJGrandpaJ
1
@tommy.carstensen 我已经好几年没使用过BeautifulSoup(或解析HTML)了。现在可能有更好的东西存在。无论如何,它的好处在于它可以更好地处理结构不良的HTML。无效的HTML比有效的HTML更常见,因此良好的处理它总是一种优势。 - zvone
@DJGrandpaJ 谢谢你提供文档和教程的链接。看起来使用起来非常简单。下次需要解析一些HTML时,我会试一试的。感谢! - tommy.carstensen
4个回答

69
class LinksParser(HTMLParser.HTMLParser):
  def __init__(self):
    HTMLParser.HTMLParser.__init__(self)
    self.recording = 0
    self.data = []

  def handle_starttag(self, tag, attributes):
    if tag != 'div':
      return
    if self.recording:
      self.recording += 1
      return
    for name, value in attributes:
      if name == 'id' and value == 'remository':
        break
    else:
      return
    self.recording = 1

  def handle_endtag(self, tag):
    if tag == 'div' and self.recording:
      self.recording -= 1

  def handle_data(self, data):
    if self.recording:
      self.data.append(data)

self.recording 记录从一个“触发”标签开始的嵌套 div 标签的数量。当我们在以触发标签为根的子树中时,将数据累积在 self.data 中。

解析结束时,数据保留在 self.data 中(是一个字符串列表,如果没有遇到触发标签可能为空)。您可以直接从解析结束时的实例中访问该列表,也可以根据需要添加适当的访问器方法来访问列表。这取决于您的目标是什么。

通过使用 'div''id''remository' 的实例属性 self.tagself.attnameself.attvalue 来代替上面代码中看到的常量字符串,可以轻松地使该类更加通用。这些属性由传递给 __init__ 的参数设置。我在上面的代码中避免了这种便宜的泛化步骤,以避免混淆核心要点(跟踪嵌套标记的计数并在记录状态处将数据累积到列表中)。


1
谢谢Alex,那段代码完美运行(除了这一行“if tag == div and self.recording:” - div 应该是一个字符串)。我所说的类返回值实际上就像你描述的那样,是类内部的一个函数来返回所需的值。或者我可以轻松地访问'data'变量。我在那里放置的字典只是我测试可能解决方案的残留物 :) 感谢您的帮助! - Martin
1
对于第一次接触 HTML 解析的人来说,嵌套 div 的计数可能不太明显,因此需要加 1。 - mg.
@Martin,不用谢,+1 是因为你发现了我的注意力分散。我现在会进行编辑以修复(引用“div”并删除那个字典和注释),以便更多读者可以受益。 - Alex Martelli
如果数据是Unicode,例如,数据是日语或中文,我该如何将其附加到data[]列表中? - おおさま

31

你尝试过BeautifulSoup吗?

from bs4 import BeautifulSoup
soup = BeautifulSoup('<div id="remository">20</div>')
tag=soup.div
print(tag.string)

这将在输出中给您 20


6

第三行需要做一点修改

HTMLParser.__init__(self)

应该这样写

HTMLParser.__init__(self)

不过下面这个对我也起作用了

import urllib2

from HTMLParser import HTMLParser

class MyHTMLParser(HTMLParser):

  def __init__(self):
    HTMLParser.__init__(self)
    self.recording = 0
    self.data = []
  def handle_starttag(self, tag, attrs):
    if tag == 'required_tag':
      for name, value in attrs:
        if name == 'somename' and value == 'somevale':
          print name, value
          print "Encountered the beginning of a %s tag" % tag
          self.recording = 1

  def handle_endtag(self, tag):
    if tag == 'required_tag':
      self.recording -=1
      print "Encountered the end of a %s tag" % tag

  def handle_data(self, data):
    if self.recording:
      self.data.append(data)

 p = MyHTMLParser()
 f = urllib2.urlopen('http://www.example.com')
 html = f.read()
 p.feed(html)
 print(p.data)
 p.close()

3
实际上你能够这样做,是因为你指定了 from HTMLParser import HTMLParser,这允许你直接调用 HTMLParser。很不幸,它们两个有相同的名称,但它们是两个不同的实体。你也可以做类似于 from HTMLParser import HTMLParser as parser 的事情,然后只需使用 class MyHTMLParser(parser) - Nona Urbiz

-2

这个完美运作:

print (soup.find('the tag').text)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接