XPath: выбор текста текущего и следующего узла по атрибутам текущего узла - PullRequest
2 голосов
/ 05 марта 2011

Если это повторный вопрос, я прошу прощения, но я не могу найти другой вопрос ни по SO, ни где-либо еще, который, кажется, справляется с тем, что мне нужно.Вот мой вопрос:

Я использую scrapy, чтобы получить некоторую информацию с этой веб-страницы.Для ясности ниже приведен блок исходного кода с этой веб-страницы, который мне интересен:

<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

Проблема в том, что 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

I 'используя следующий код:

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": []}]

Это не тот вывод, который мне нужен.Что я делаю неправильно?Имейте в виду, что я запускаю этот скрипт на на этом , как уже упоминалось.

Ответы [ 3 ]

3 голосов
/ 05 марта 2011

.1.ANT101H5 Введение в биологическую антропологию и археологию

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

.2.Исключение: ANT100Y5

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

.3.Необходимое условие: ANT102H5

concat(
    span/span[@class='title2'][2],
    span/span[@class='title2'][2]/following-sibling::a[1]
    )
2 голосов
/ 05 марта 2011

Нетрудно выбрать три узла, на которые вы ссылаетесь (используя такие методы, как Flack). Трудно (а) выбрать их, не выбирая также другие вещи, которые вам не нужны, и (б) сделать ваш выбор достаточно надежным, чтобы он по-прежнему выбирал их, если ввод немного отличается. Мы должны предположить, что вы не знаете точно, что находится на входе - если бы вы это сделали, вам не нужно было бы писать выражение XPath, чтобы выяснить это.

Вы сказали нам три вещи, которые вы хотите захватить. Но каковы ваши критерии выбора этих трех вещей, а не выбора чего-то другого? Как много известно о том, что вы ищете?

Вы выразили свою проблему как проблему XPath, но я бы решил ее по-другому. Я бы начал с преобразования введенного вами материала во что-то с лучшей структурой, используя XSLT. В частности, я бы попытался обернуть все родственные элементы, которые не входят в элемент <p>, в элементы <p>, рассматривая каждую группу последовательных элементов, заканчивающихся на <br>, как абзац. Это можно сделать без особых сложностей, используя конструкцию <xsl:for-each-group group-ending-with> в XSLT 2.0.

1 голос
/ 05 марта 2011

Мои ответы очень похожи на ответы @ Flack :

Наличие этого XML-документа (исправление предоставленного документа при закрытии множества незамеченных <br> s и оборачивании всего в один верхний элемент):

<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. Также это демонстрирует, как правильно индексировать результаты использования // (обратите внимание на окружающие скобки) - помогая предотвратить очень частую ошибку при попытке сделать это

ОБНОВЛЕНИЕ : ОП запросил одно выражение 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 выбирает ровно все требуемые текстовые узлы на странице HTML с предоставленной ссылкой (после того, как она убрана, чтобы стать правильно сформированным XML):

  (//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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...