我在scraperwiki.com上开发了一个Python scraper,我需要解析一个包含以下内容的html页面:
<div class="items">
<div class="item">
ItemLine1 ItemLine1 ItemLine1
<br>
ItemLine2 ItemLine2 ItemLine2
</div>
<br>
</div>我现在要做的是:
import scraperwiki
import lxml.html
#.......................
raw_string = lxml.html.fromstring(scraperwiki.scrape(url_to_scrape))
my_line = ((raw_string.cssselect("div.items div.item")[0]).text)
print (my_line)而且它只打印ItemLine1 ItemLine1 ItemLine1。当我更改为1时,它会抛出一个异常。
我该怎么去掉它呢?我应该使用xpath吗?
发布于 2012-10-29 21:50:10
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子节点;preceding-sibling::轴包含出现在第一个br之前的所有节点;node()选择该轴中的每一种节点(文本或元素)。
如果您的更大目标是通过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')第一种方法是简单地获取段落中的所有节点,并通过br将它们划分为不同的列表。如果使用这种方法,请不要使用ElementTree应用编程接口的text和tail属性,因为最终可能会复制文本。
def paras_by_br_nodes(parent):
"""Return a list of node children of parent (including text nodes) grouped by "paragraphs" which are delimited by <br/> elements."""
paralist = []
paras = []
for node in parent.xpath('node()'):
if getattr(node, 'tag', None) == 'br':
paralist.append(paras)
paras = []
else:
paras.append(node)
paralist.append(paras)
return paralist
print paras_by_br_nodes(itemlist[0])这将生成如下列表:
[['\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 ']]第二种方法是利用ElementTree API并将文本节点保留在text和tail属性中。这种方法的缺点是,如果没有附加文本的元素,我们只需要包含文本节点。这个非同构类型的列表在使用时会有一些麻烦。
def paras_by_br_text(parent):
paralist=[]
para=[parent.text]
for item in parent:
if item.tag=='br':
paralist.append(para)
para = [item.tail]
else:
para.append(item)
paralist.append(para)
return paralist
print paras_by_br_text(itemlist[0])这将生成如下列表。请注意,与前面的列表相比,它在列表的第一个位置只有文本节点节点。这与br.tail文本或parent.text (第一个元素之前的文本)相对应。
[['\n '],
['\n ItemLine1 ItemLine1 ItemLine1\n\t\t', <Element a at 0x1042f5170>],
[<Element a at 0x1042f5290>],
[],
['\n Itemline3\n ']]我认为最好的方法是引入新的元素。此html在本应使用p或其他容器元素时却在使用br。因此,让我们修复html并返回一个元素列表,而不是节点列表:
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/>了解如何通过新的para元素对项目进行分组,该元素在原始文档中不存在。
https://stackoverflow.com/questions/13122353
复制相似问题