from lxml import *  
import lxml.html  
import urllib2  
import lxml.html as H
import sys 

if __name__ == '__main__':  
    ss = u"<html>  <body>    <form>      <div id='leftmenu'>        <h3>档案</h3>\
        <ul><!-- 找到这里 --></ul>      </div>    </form>  </body> </html>"
    ss = urllib2.urlopen("http://auto.qq.com/a/20090910/000052.htm").read()
    doc = H.document_fromstring(ss)
    nodes = doc.xpath("//div[@id='ArticleTit']")
    if nodes is not None:
        #print nodes[0].text.encode(sys.getfilesystemencoding())
        print nodes[0].text.encode(sys.getfilesystemencoding())
    nodes = doc.xpath("//div[@id='ArticleCnt']")
    if nodes is not None:
        print nodes[0].text_content().encode(sys.getfilesystemencoding())
        #print nodes[0].text.encode(sys.getfilesystemencoding())

呵呵, 简单高效. 之前用java写了N长的代码. 真是不爽. 参考: http://www.go4pro.org/?p=118

附录:XPATH的简单语法介绍

XPATH基本上是用一种类似目录树的方法来描述在XML文档中的路径。比如用“/”来作为上下层级间的分隔。第一个“/”表示文档的根节点(注意,不是指文档最外层的tag节点,而是指文档本身)。比如对于一个HTML文件来说,最外层的节点应该是”/html”。
同样的,“..”和“.”分别被用来表示父节点和本节点。
XPATH返回的不一定就是唯一的节点,而是符合条件的所有节点。比如在HTML文档里使用“/html/head/scrpt”就会把head里的所有script节点都取出来。
为了缩小定位范围,往往还需要增加过滤条件。过滤的方法就是用“[”“]”把过滤条件加上。比如在HTML文档里使用“/html/body/div[@id='main']”,即可取出body里id为main的div节点。
其中@id表示属性id,类似的还可以使用如@name, @value, @href, @src, @class….
而 函数text()的意思则是取得节点包含的文本。比如:<div>hello<p>world</p>< /div>中,用”div[text()='hello']“即可取得这个div,而world则是p的text()。
函数position()的意思是取得节点的位置。比如“li[position()=2]”表示取得第二个li节点,它也可以被省略为“li[2]”。
不过要注意的是数字定位和过滤 条件的顺序。比如“ul/li[5][@name='hello']”表示取ul下第五项li,并且其name必须是hello,否则返回空。而如果用 “ul/li[@name='hello'][5]”的意思就不同,它表示寻找ul下第五个name为”hello“的li节点。
此外,“*”可以代替所有的节点名,比如用”/html/body/*/span”可以取出body下第二级的所有span,而不管它上一级是div还是p或是其它什么东东。
而 “descendant::”前缀可以指代任意多层的中间节点,它也可以被省略成一个“/”。比如在整个HTML文档中查找id为“leftmenu”的 div,可以用“/descendant::div[@id='leftmenu']”,也可以简单地使用“ //div[@id='leftmenu']”。
至于“following-sibling::”前缀就如其名所说,表示同一层的下一个节点。”following-sibling::*”就是任意下一个节点,而“following-sibling::ul”就是下一个ul节点。
更复杂的XPATH语法还是请参考官文档《XML Path Language (XPath)》。