Как выбрать все узлы между определенными заголовками? - PullRequest
0 голосов
/ 30 апреля 2019

Каждый тег <header> содержит название конференции. Каждый тег <ul> содержит ссылки на эту конференцию.

Когда я пытаюсь сканировать веб-сайт, я пытаюсь связать тег <header> с вашими ссылками в тегах <ul>. Но я не знаю, как я могу выбрать только теги <ul> из двух родственных <headers>.

HTML:

<header>... 0 ... </header>
<ul class="publ-list">... 0 ...</ul>
<header>... 1 ... </header> 
<ul class="publ-list">... 0 ...</ul>
<header>... 2 ... </header>
<ul class="publ-list">... 0 ...</ul>
<p>...</p>
<ul class="publ-list">... 1 ...</ul>
<header>... 3 ...</header>
<ul class="publ-list">... 0 ...</ul>
<ul class="publ-list">... 1 ...</ul>
<ul class="publ-list">... 2 ....</ul>
<ul class="publ-list">... 3 ....</ul>
<ul class="publ-list">... 4 ....</ul>
<header>... 4 ...</header>

Пример:

  • <ul> теги являются родственными для заголовка [0] и заголовка [1]

    <ul class="publ-list">... 0 ...</ul>
    
  • <ul> являются родственными для заголовка [2] и заголовка [3]

    <ul class="publ-list">... 0 ...</ul>
    <ul class="publ-list">... 1 ...</ul>
    

В некоторых случаях:

  • Возможно несколько тегов ul между тегами заголовка
  • Иногда между тегами ul есть p-тег
  • Все теги являются братьями и сестрами!
  • У всех ul есть класс "pub-list"

Мой код:

TITLE_OF_EDITIONS_SELECTIOR = 'header h2'
GROUP_OF_TYPES_OF_EDITION_SELECTOR = ".publ-list"

size_editions = len(response.css(GROUP_OF_TYPES_OF_EDITION_SELECTOR))
i = 0
while i < size_editions:

    # Get the title of conference
    title_edition_conference = response.css(TITLE_OF_EDITIONS_SELECTIOR)[i]


    # Get datas and links of <ul> tags "(.publ-list)"
    TYPES_OF_CONFERENCE = response.css(GROUP_OF_TYPES_OF_EDITION_SELECTOR)[i]
    TYPE = TYPES_OF_CONFERENCE.css('.entry')
    types_of_edition = {}
    size_type_editions = 0
    for type_of_conference in TYPE:
        title_type = type_of_conference.css('.data .title ::text').extract()
        link_type = type_of_conference.css('.publ ul .drop-down .body ul li a ::attr(href)').extract_first()
        types_of_edition[size_type_editions] = {
            "title": title_type,
            "link": link_type,
            }
        size_type_editions = size_type_editions + 1

    editions[i] = {
        "title_edition_conference": title_edition_conference,
        "types_of_edition": types_of_edition
        }
    i = i + 1

Проблема моего кода

  • Иногда есть много тегов ul
  • Иногда имеет тег <p>, и он ломает мой xPath и получает только предыдущие теги <ul>.

Я тестировал его с помощью JQuery на консоли Google Chrome, пример:

"$($('header')[0]).nextUntil($('header')[1])"

Но как я могу выбрать это, используя xPath или CSS Selector? Спасибо!

Ответы [ 3 ]

0 голосов
/ 01 мая 2019

Это может быть то, что вы ищете.

html = """
<ul class="publ-list">...</ul>
<header>..</header>
<ul class="publ-list">...</ul>
<header>..</header>
<ul class="publ-list">...</ul>
<header>..</header>
<ul class="publ-list">...</ul>
<p>...</p>
<ul class="publ-list">...</ul>
<header>..</header>
<ul class="publ-list">...</ul>
<ul class="publ-list">...</ul>
<header>..</header>
<ul class="publ-list">...</ul>
"""

Примечание. Я добавил <ul> до первого и после последнего набора <header>..</header>.

Это выражение

 //ul[   
preceding-sibling::header 
    and 
following-sibling::header
   ]

должен выбрать все теги <ul>, кроме тех, которые я добавил до и после, и ни один из тегов <p>, которые могут быть на пути.

0 голосов
/ 01 мая 2019

Следующая комбинация селекторов css и цикла python for может решить эту задачу.

from parsel import Selector

html  = """
<ul class="publ-list">p1</ul>
<header>h1</header>
<ul class="publ-list">p2</ul>
<header>h2</header>
<ul class="publ-list">p3</ul>
<header>h3</header>
<ul class="publ-list">p4</ul>
<p>p_tag_1</p>
<ul class="publ-list">p5</ul>
<header>h4</header>
<ul class="publ-list">p6</ul>
<ul class="publ-list">p7</ul>
<header>h5</header>
<ul class="publ-list">p8</ul>
"""
response = Selector(text=html)
tags = response.css("header, ul")
output = {}
key = False
for t in tags:
    if key and "<ul" in t.css("*").extract_first():
        output[key].append(t.css("::text").extract_first())
    elif "<header>" in t.css("*").extract_first():
        key = t.css("::text").extract_first()
        if key not in output.keys():
            output[key]=[]
    else:
        pass
print(output)

Вывод: {'h1': ['p2'], 'h2': ['p3'], 'h3': ['p4', 'p5'], 'h4': ['p6', 'p7'], 'h5': ['p8']}

Этот селектор CSS: tags = response.css("header, ul") возвращает список тегов <header> и <ul> в том же порядке, что и в HTML-коде.

После этого мы можем перебирать полученные теги с помощью цикла for и выбирать необходимые данные.

0 голосов
/ 30 апреля 2019

Попробуйте использовать following-sibling как здесь:

>>> txt = """<header>..</header>
... <ul class="publ-list">...</ul>
... <header>..</header>
... <ul class="publ-list">...</ul>
... <header>..</header>
... <ul class="publ-list">...</ul>
... <p>...</p>
... <ul class="publ-list">...</ul>
... <header>..</header>
... <ul class="publ-list">...</ul>
... <ul class="publ-list">...</ul>
... <header>..</header>"""
>>> from scrapy import Selector
>>> sel = Selector(text=txt)
>>> sel.xpath('//header/following-sibling::*[not(self::header)]').extract()
[u'<ul class="publ-list">...</ul>', u'<ul class="publ-list">...</ul>', u'<ul class="publ-list">...</ul>', u'<p>...</p>', u'<ul class="publ-list">...</ul>', u'<ul class="publ-list">...</ul>', u'<ul class="publ-list">...</ul>']

Так что с //header/following-sibling::*[not(self::header)] мы выбираем всех header братьев и сестер, но не header.

...