Python 使用lxml.html解析html

Python 使用lxml.html解析html,python,parsing,screen-scraping,lxml,Python,Parsing,Screen Scraping,Lxml,我正在scraperwiki.com上开发一个Python scraper,我需要解析一个包含以下内容的html页面: <div class="items"> <div class="item"> ItemLine1 ItemLine1 ItemLine1 <br> ItemLine2 ItemLine2 ItemLine2 </div> <br> </div> <

我正在scraperwiki.com上开发一个Python scraper,我需要解析一个包含以下内容的html页面:

<div class="items">
  <div class="item">
       ItemLine1 ItemLine1 ItemLine1
       <br> 
       ItemLine2 ItemLine2 ItemLine2
 </div>
 <br>
</div>
<para>
       </para>
--------
<para>
       ItemLine1 ItemLine1 ItemLine1
        <a href="">item</a>
        Itemline1-b
       </para>
--------
<para><a class="z">item2</a>
       ItemLine2 ItemLine2 ItemLine2
       </para>
--------
<para/>
并且它只打印
ItemLine1 ItemLine1 ItemLine1
。当我将[0]更改为[1]时,它会引发异常


我怎么刮呢?我应该使用xpath吗?

xpath是最简单的解决方案:

items = raw_string.cssselect('div.items div.item')

texts = [item.xpath('br[1]/preceding-sibling::node()') for item in items]
XPath
br[1]
选择
div.item
的第一个子
br
;前面的同级::轴包含在第一个
br
之前出现的所有节点<代码>节点()选择该轴中的每种节点(文本或元素)

如果您更大的目标是通过
br
元素分割节点的子节点,那么您可以采取几种不同的方法。之所以如此棘手,是因为像
br
hr
这样的元素设计得很糟糕。使用类似于树的标记语言(如sgml、html或xml),应该放在一起的内容应该按公共父元素分组,而不是按无子分隔符元素拆分

我将扩展您的测试用例,以演示一些更复杂的情况:

html = """<div class="items">
  <div class="item">
   <br>
   ItemLine1 ItemLine1 ItemLine1
   <a href="">item</a>
   Itemline1-b
   <br> 
   <a class="z">item2</a>
   ItemLine2 ItemLine2 ItemLine2
   <br><br>
   Itemline3
 </div>
 <br>
</div>"""

doc = lxml.html.fromstring(html)
itemlist = doc.cssselect('div.items div.item')
这将生成如下列表:

[['\n       '],
 ['\n       ItemLine1 ItemLine1 ItemLine1\n\t\t', <Element a at 0x10498a350>, '\n\t\tItemline1-b\n       '],
 [<Element a at 0x10498a230>, '\n       ItemLine2 ItemLine2 ItemLine2\n       '],
 [], 
 ['\n       Itemline3\n ']]
这将生成这样的列表。请注意,与上一个列表相比,它在列表的第一个位置仅具有文本节点。这对应于
br.tail
文本或
parent.text
(第一个元素之前的文本)

这将打印以下内容:

<div class="items">
  <div class="item">
       ItemLine1 ItemLine1 ItemLine1
       <br> 
       ItemLine2 ItemLine2 ItemLine2
 </div>
 <br>
</div>
<para>
       </para>
--------
<para>
       ItemLine1 ItemLine1 ItemLine1
        <a href="">item</a>
        Itemline1-b
       </para>
--------
<para><a class="z">item2</a>
       ItemLine2 ItemLine2 ItemLine2
       </para>
--------
<para/>

--------
ItemLine1 ItemLine1 ItemLine1
项目1-b
--------
项目2
项目行2项目行2项目行2项目行2
--------

查看如何通过原始文档中不存在的新
para
元素对项目进行分组。

我猜这里的问题是br已打开但未关闭。beautysoup不可接受可能,那又怎样?我必须解析它。谢谢,但它也只返回
ItemLine1 ItemLine1 ItemLine1
。那
item2item2…
呢?我想这就是你想要的?我将用一个将它们分组的解决方案来扩展答案。
[['\n       '],
 ['\n       ItemLine1 ItemLine1 ItemLine1\n\t\t', <Element a at 0x1042f5170>],
 [<Element a at 0x1042f5290>],
 [],
 ['\n       Itemline3\n ']]
def paras_by_br(parent):
    paralist = []
    para = lxml.html.etree.Element('para')
    if parent.text:
        para.text = parent.text
    for item in parent:
        if item.tag=='br':
            paralist.append(para)
            para = lxml.html.etree.Element('para')
            if item.tail:
                para.text = item.tail
        else:
            para.append(item)
    return paralist

paralist = paras_by_br(itemlist[0])

print "\n--------\n".join(lxml.html.etree.tostring(para) for para in paralist)
<para>
       </para>
--------
<para>
       ItemLine1 ItemLine1 ItemLine1
        <a href="">item</a>
        Itemline1-b
       </para>
--------
<para><a class="z">item2</a>
       ItemLine2 ItemLine2 ItemLine2
       </para>
--------
<para/>