XPath:按当前节点属性选择当前节点和下一节点的文本

时间:2011-03-05 06:40:17

标签: python xpath scrapy

如果这是一个重复的问题,我道歉,但我无法在SO或其他地方找到另一个似乎能够处理我需要的问题。这是我的问题:

我正在使用scrapythis网页中获取一些信息。为清楚起见,以下是该网页的源代码块,我感兴趣的是:

<p class="titlestyle">ANT101H5 Introduction to Biological Anthropology and Archaeology 
                        <span class='distribution'>(SCI)</span></p> 

<span class='normaltext'> 
Anthropology is the global and holistic study of human biology and behaviour, and includes four subfields: biological anthropology, archaeology, sociocultural anthropology and linguistics. The material covered is  directed  to answering the question: What makes us human? This course is a survey of  biological  anthropology and  archaeology.  [<span class='Helpcourse'
            onMouseover="showtip(this,event,'24 Lectures')"
            onMouseout="hidetip()">24L</span>, <span class='Helpcourse'
            onMouseover="showtip(this,event,'12 Tutorials')"
            onMouseout="hidetip()">12T</span>]<br> 

<span class='title2'>Exclusion: </span><a href='javascript:OpenCourse("WEBCOURSENOTFOUND.html")'>ANT100Y5</a><br>

<span class='title2'>Prerequisite: </span><a href='javascript:OpenCourse("WEBCOURSEANT102H5.pl?fv=1")'>ANT102H5</a><br> 
</span><br/><br/<br/> 

该页面上的几乎所有代码都与上面的代码类似。

从这一切开始,我需要抓住:

  1. ANT101H5生物人类学和考古学导论
  2. 排除:ANT100Y5
  3. 先决条件:ANT102H5
  4. 问题是Exclusion:位于<span class="title2">内,ANT100Y5位于以下<a>内。

    我似乎无法从这个源代码中抓取它们。目前,我的代码尝试(并且失败)抓取ANT100Y5,如下所示:

    hxs = HtmlXPathSelector(response)
        sites = hxs.select("//*[(name() = 'p' and @class = 'titlestyle') or (name() = 'a' and @href and preceding-sibling::'//span/@class=title2')]")
    

    我很感激任何对此的帮助,即使它是“你因为没有看到这个完全回答这个问题的其他问题而失明”(在这种情况下,我自己会投票决定关闭这个)。在我的智慧结束时,我真的很喜欢。

    提前致谢

    编辑:在@Dimitre

    建议的更改后填写原始代码

    我正在使用以下代码:

    class regcalSpider(BaseSpider):
        name = "disc"
        allowed_domains = ['www.utm.utoronto.ca']
        start_urls = ['http://www.utm.utoronto.ca/regcal/WEBLISTCOURSES1.html']
    
        def parse(self, response):
                items = []
                hxs = HtmlXPathSelector(response)
                sites = hxs.select("/*/p/text()[1] | \
                                  (//span[@class='title2'])[1]/text() | \
                                  (//span[@class='title2'])[1]/following-sibling::a[1]/text() | \
                                  (//span[@class='title2'])[2]/text() | \
                                  (//span[@class='title2'])[2]/following-sibling::a[1]/text()")
    
                for site in sites:
                        item = RegcalItem()
                        item['title'] = site.select("a/text()").extract()
                        item['link'] = site.select("a/@href").extract()
                        item['desc'] = site.select("text()").extract()
                        items.append(item)
                return items
    
                filename = response.url.split("/")[-2]
                open(filename, 'wb').write(response.body)
    

    这给了我这个结果:

    [{"title": [], "link": [], "desc": []},
     {"title": [], "link": [], "desc": []},
     {"title": [], "link": [], "desc": []}]
    

    这不是我需要的输出。我究竟做错了什么?请记住,我正在this上运行此脚本,如上所述。

3 个答案:

答案 0 :(得分:3)

  

0.1。 ANT101H5生物人类学和考古学导论

p[@class='titlestyle']/text()
  

0.2。排除:ANT100Y5

concat(
    span/span[@class='title2'][1],
    span/span[@class='title2'][1]/following-sibling::a[1]
    )
  

0.3。先修课程:ANT102H5

concat(
    span/span[@class='title2'][2],
    span/span[@class='title2'][2]/following-sibling::a[1]
    )

答案 1 :(得分:2)

选择你引用的三个节点并不困难(使用Flack等技术)。困难的是(a)选择它们而不选择其他你不想要的东西,以及(b)使你的选择足够强大,如果输入略有不同,它仍然会选择它们。我们必须假设您不确切知道输入中的内容 - 如果您这样做,则不需要编写XPath表达式来查找。

你已经告诉我们你要抓住的三件事。但是你选择这三件事的标准是什么,而不是选择别的东西呢?关于你在寻找什么知道多少?

您已将问题表达为XPath问题,但我会以不同的方式解决它。我将首先使用XSLT将您显示的输入转换为具有更好结构的内容。特别是,我会尝试将不在<p>元素内的所有兄弟元素包装到<p>元素中,将每个以<br>结尾的连续元素组作为段落处理。使用XSLT 2.0中的<xsl:for-each-group group-ending-with>构造可以毫不费力地完成。

答案 2 :(得分:1)

我的答案与@Flack 的答案非常相似:

拥有这个XML文档(更正提供的文档以关闭众多未关闭的<br>并将所有内容包装在单个顶部元素中):

<body>
    <p class="titlestyle">ANT101H5 Introduction to Biological Anthropology and Archaeology 
        <span class='distribution'>(SCI)</span>
    </p>
    <span class='normaltext'> Anthropology is the global and holistic study of human biology and behaviour, and includes four subfields: biological anthropology, archaeology, sociocultural anthropology and linguistics. The material covered is directed to answering the question: What makes us human? This course is a survey of biological anthropology and archaeology. [
        <span class='Helpcourse' onMouseover="showtip(this,event,'24 Lectures')" onMouseout="hidetip()">24L</span>, 
        <span class='Helpcourse' onMouseover="showtip(this,event,'12 Tutorials')" onMouseout="hidetip()">12T</span>]
        <br/>
        <span class='title2'>Exclusion: </span>
        <a href='javascript:OpenCourse("WEBCOURSENOTFOUND.html")'>ANT100Y5</a>
        <br/>
        <span class='title2'>Prerequisite: </span>
        <a href='javascript:OpenCourse("WEBCOURSEANT102H5.pl?fv=1")'>ANT102H5</a>
        <br/>
    </span>
    <br/>
    <br/>
    <br/>
</body>

此XPath表达式

normalize-space(/*/p/text()[1])

当计算产生想要的字符串时(周围的引号不在结果中。我添加它们以显示生成的确切字符串):

"ANT101H5 Introduction to Biological Anthropology and Archaeology"

此XPath表达式

concat((//span[@class='title2'])[1],
            (//span[@class='title2'])[1]
                   /following-sibling::a[1]
            )

当评估产生以下想要的结果时:

"Exclusion: ANT100Y5"

此XPath表达式

concat((//span[@class='title2'])[2],
            (//span[@class='title2'])[2]
                   /following-sibling::a[1]
            )

当评估产生以下想要的结果时:

"Prerequisite: ANT102H5"

注意:在这种特殊情况下,不需要缩写//,实际上这个缩写应该始终在可能的情况下避免,因为它会导致对表达式的评估速度变慢,导致很多情况下完整的(子)树遍历。我故意使用'//',因为提供的XML片段没有给我们XML文档的完整结构。此外,这演示了如何正确索引使用//的结果(注意周围的括号) - 有助于防止在尝试这样做时经常出现错误

UPDATE :OP已请求一个XPath表达式,它选择所有必需的文本节点 - 这里是:

/*/p/text()[1]
   |
    (//span[@class='title2'])[1]/text()
   |
    (//span[@class='title2'])[1]/following-sibling::a[1]/text()
   |
    (//span[@class='title2'])[2]/text()
   |
    (//span[@class='title2'])[2]/following-sibling::a[1]/text()

当应用于上述相同的XML文档时,文本节点的串联正是所需要的:

ANT101H5 Introduction to Biological Anthropology and Archaeology          
        Exclusion: ANT100Y5Prerequisite: ANT102H5

可以通过运行以下XSLT转换来确认此结果:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <xsl:copy-of select=
   "/*/p/text()[1]
   |
    (//span[@class='title2'])[1]/text()
   |
    (//span[@class='title2'])[1]/following-sibling::a[1]/text()
   |
    (//span[@class='title2'])[2]/text()
   |
    (//span[@class='title2'])[2]/following-sibling::a[1]/text()
   "/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于同一XML文档(此答案中先前指定)时,生成所需的正确结果

ANT101H5 Introduction to Biological Anthropology and Archaeology          
        Exclusion: ANT100Y5Prerequisite: ANT102H5

最后:以下单个XPath表达式使用提供的链接(在将其整理成格式良好的XML之后)精确选择HTML页面中所有想要的文本节点:

  (//p[@class='titlestyle'])[2]/text()[1]
|
  (//span[@class='title2'])[2]/text()
|
  (//span[@class='title2'])[2]/following-sibling::a[1]/text()
|
  (//span[@class='title2'])[3]/text()
|
  (//span[@class='title2'])[3]/following-sibling::a[1]/text()