XPath: как получить текст между двумя html-тегами с одинаковым уровнем?

Я новичок в и работаю с , чтобы получить текст с разных созданных html-страниц.
Я получаю {id} тега заголовка от пользователя (<h1|2|.. id = "title-{id}">text</h1|2|3..>). Мне нужно получить текст из всех тегов html между этим заголовком и следующим заголовком того же уровня. Поэтому, если заголовок h1, мне нужно получить весь текст всех тегов до следующего заголовка h1. Все идентификаторы заголовков имеют одинаковый шаблон «title-{id}», где генерируется {id}.
Чтобы было понятнее, вот пример :

<html>
    <body>
        ...
        <h2 id = "tittle-id1">id1</h2>
        bunch of tags containing text I want to get
        <h2 id = "tittle-id2">id2</h2>
        ...
    </body>
</html>

ПРИМЕЧАНИЕ. Я не знаю, что это может быть за заголовок. Это может быть любой из тегов заголовка html от <h1> до <h6>.


ОБНОВИТЬ :
Пробуя несколько вещей, я заметил, что не уверен, что следующий заголовок того же уровня или даже существует. Так как заголовки используются как заголовки и подзаголовки. Данный идентификатор может быть последним подзаголовком, поэтому у меня будет заголовок более высокого уровня после или даже будет последним на странице. Так что в основном у меня есть только идентификатор заголовка, и мне нужно получить весь текст «абзаца».


Обойти:
Я нашел обходное решение:
Я делаю это в 3 шага :
Во-первых, я использую //*[@id='title-{id}], что позволяет мне получить полную строку с тегом, поэтому теперь я знаю, какой это заголовок тега.
Во-вторых, я использую //*[id='title-{id}]/following-sibling::*, это позволяет искать следующий заголовок того же или более высокого уровня {myHeader}.
Наконец, я использую //*[id='title-{id}]/following-sibling::* и //{myHeader}//preceding-sibling::*, чтобы получить то, что находится между ними, или перейти до конца страницы, если заголовок не найден.

Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
0
0
1 448
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вот xpath для получения всех элементов между тегами h2.

//h2/following-sibling::*[count(following-sibling::h2)=1]

Вот пример html, который я использовал для имитации сценария. (обновите идентификатор, чтобы проверить различные параметры, показанные ниже).

//[@id='tittle-id1' ]/following::[count(following-sibling::[name()=name(preceding-sibling::[@id='tittle-id1'])])=1]

<html><head></head><body>
 
        ...
        <h2 id = "tittle-id1">id1</h2>
		  <h3 id = "tittle-id3"> h3 tag</h3>
		  <h4 id = "tittle-id4"> h4 tag</h4>
		  <h3 id = "tittle-id5"> 2nd h3  tag</h3>
        bunch of tags containing text I want to get
		   <h5 id = "tittle-id6"> h5 tag </h5>
        <h2 id = "tittle-id2">id2</h2>
		<h4 id = "tittle-id7"> 2nd h4 tag</h4>
        ...
    
	
</body></html>

вывод, если ввод пользователя: {id1}

вывод, если пользовательский ввод: {id4}

вывод, если пользовательский ввод: {id3}

Примечание. Этот xpath предназначен для исходного сценария публикации.

Это сработало бы, если бы я знал, что будет тегом html заголовка (<h2> в примере). Но если вы внимательно читаете, у меня есть только идентификатор. Я не знаю, что это за тег заголовка. Это может быть любой из них от <h1> до <h6>

Deyesta 30.04.2019 15:03

Понял, позвольте мне обновить мой ответ, который получит все элементы между двумя тегами на основе идентификатора.

supputuri 30.04.2019 15:06

ваше решение, кажется, подразумевает, что я знаю это n, чего я не знаю. Но я думаю, что это моя вина, что я не был более точным. когда я сказал id1 или id2, это был просто пример, фактический идентификатор - это сгенерированная строка из фактического заголовка.

Deyesta 02.05.2019 11:41
n здесь нет номера из id. Это n-й элемент, вы должны использовать цикл, чтобы получить всю информацию о заголовках, и как часть цикла вы можете обновить значение n.
supputuri 02.05.2019 14:27

Да, это то, что я сделал. Проверьте мое последнее обновление, вы можете найти новую работу вокруг хорошего решения. В этом случае я опубликую как решение и проверю его для использования другими.

Deyesta 02.05.2019 18:16

@supputuri Разве первый xpath не должен быть //h2/following-sibling::*[count(following-sibling::h2)<2], чтобы вы включали следующих братьев и сестер последнего h2?

CJ7 08.05.2019 04:52

@ CJ7 OP не знает тег h?, поэтому вы не можете жестко запрограммировать его в xpath.

supputuri 08.05.2019 05:16

@Deyesta Не могли бы вы проверить этот xpath //*[@id='tittle-id1' ]/following::*[count(following-sibling::*[name()=name(preced‌​ing-sibling::*[@id='‌​tittle-id1'])])=1] и дайте мне знать. Чтобы я мог обновить это в ответе. Это приведет к тому, что все элементы между двумя тегами h? будут основаны на первом h? идентификатор тега, предоставленный пользователем.

supputuri 08.05.2019 05:27

@supputuri Хорошо, но моя разница в том, что я делаю <2 вместо =1

CJ7 08.05.2019 05:35

@ CJ7, если это <2, то он найдет все следующие элементы, которые находятся после следующего тега h2. Проверьте свой xpath с помощью html, который я добавил в ответ. Согласно ОП все эти h? являются братьями и сестрами, вот где сложность, и мы не уверены в порядке тегов.

supputuri 08.05.2019 05:50

@supputuri ваш xpath дал мне ожидаемый абзац, единственная проблема в том, что он дал другой, а не тот, который помечен идентификатором, который я дал

Deyesta 09.05.2019 18:01

Можете ли вы пропинговать мне xpath, который вы использовали?

supputuri 09.05.2019 18:03

Я использовал этот xpath ://*[@id='tittle-id1' ]/following::*[count(following-sibling::*[name()=name(preced‌​ing-sibling::*[@id='‌​tittle-id1'])])=1]. Похоже, что этот xpath дает именно «абзац», за исключением того, что он для title-id?. Я не вправе приводить пример из реальной жизни, и это может быть причиной того, что вы не можете получить правильный ответ.

Deyesta 10.05.2019 10:08

Попробовал ваш xpath и образец страницы, который я загрузил в ответ, и он работает, как и ожидалось. Можно ли смоделировать данные и поделиться точной структурой?

supputuri 10.05.2019 20:08

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

//*[name()=name(//*[@id=$generated-id-string])]

Теперь для "проблема между отметками" используйте, как обычно, метод Кайса для пересечения:

//*[name()=name(//*[@id=$generated-id-string])]/preceding-sibling::node()[
   count(.|//*[@id=$generated-id-string]/following-sibling::node())
      =
   count(//*[@id=$generated-id-string]/following-sibling::node())
]

Тест в http://www.xpathtester.com/xpath/0dcfdf59dccb8faf3705c22167ae45f1

Когда я тестирую ваш код на реальных данных на вашей тестовой платформе, я получаю следующую ошибку: ERROR - Seem like XML is not well formed:The entity name must immediately follow the '&' in the entity reference.

Deyesta 02.05.2019 11:38

Я попробовал это с помощью scrapy. Я получаю весь текст от идентификатора до конца документа, возможно, это связано с тем же фактом, неправильно сформированным xml

Deyesta 02.05.2019 11:57

@Deyesta Это работает, и есть тестовый пример с вашим собственным входным образцом. Если есть другие входные данные, для которых это выражение XPath не выбирает то, что вы хотите, пожалуйста, опубликуйте свой тестовый пример, как это сделал я.

Alejandro 02.05.2019 21:13
Ответ принят как подходящий

Это то, что сработало для меня:
Для этого имейте в виду, что я использую с :

name_query = u"//*[name()=name(//*[@id='"+id+"'])]"
all = response.xpath(name_query)
for selector in all.getall():
     if self.id in selector:
          position = all.getall().index(selector)
balise = "h" + all.getall()[position].split("<h")[1][0]
title = all.getall()[position].split(">")[1].split("<")[0]
query = u"//*[preceding-sibling::"+balise+"[1] ='"+title+"' and following-sibling::"+balise+"]"
self.log('query = '+query)
results = response.xpath(query)
results.pop(len(results)-1)
with open(filename,'wb') as f:
    for text in results.css("::text").getall():
        f.write(text.encode('utf-8')+"\n")

В целом это должно работать. Я тестировал его на нескольких заголовках с разными уровнями, и у меня он отлично работает.

Другие вопросы по теме