Я новичок в xpath и работаю с шероховатый, чтобы получить текст с разных созданных 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::*, чтобы получить то, что находится между ними, или перейти до конца страницы, если заголовок не найден.






Вот 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 предназначен для исходного сценария публикации.
Понял, позвольте мне обновить мой ответ, который получит все элементы между двумя тегами на основе идентификатора.
ваше решение, кажется, подразумевает, что я знаю это n, чего я не знаю. Но я думаю, что это моя вина, что я не был более точным. когда я сказал id1 или id2, это был просто пример, фактический идентификатор - это сгенерированная строка из фактического заголовка.
n здесь нет номера из id. Это n-й элемент, вы должны использовать цикл, чтобы получить всю информацию о заголовках, и как часть цикла вы можете обновить значение n.
Да, это то, что я сделал. Проверьте мое последнее обновление, вы можете найти новую работу вокруг хорошего решения. В этом случае я опубликую как решение и проверю его для использования другими.
@supputuri Разве первый xpath не должен быть //h2/following-sibling::*[count(following-sibling::h2)<2], чтобы вы включали следующих братьев и сестер последнего h2?
@ CJ7 OP не знает тег h?, поэтому вы не можете жестко запрограммировать его в xpath.
@Deyesta Не могли бы вы проверить этот xpath //*[@id='tittle-id1' ]/following::*[count(following-sibling::*[name()=name(preceding-sibling::*[@id='tittle-id1'])])=1] и дайте мне знать. Чтобы я мог обновить это в ответе. Это приведет к тому, что все элементы между двумя тегами h? будут основаны на первом h? идентификатор тега, предоставленный пользователем.
@supputuri Хорошо, но моя разница в том, что я делаю <2 вместо =1
@ CJ7, если это <2, то он найдет все следующие элементы, которые находятся после следующего тега h2. Проверьте свой xpath с помощью html, который я добавил в ответ. Согласно ОП все эти h? являются братьями и сестрами, вот где сложность, и мы не уверены в порядке тегов.
@supputuri ваш xpath дал мне ожидаемый абзац, единственная проблема в том, что он дал другой, а не тот, который помечен идентификатором, который я дал
Можете ли вы пропинговать мне xpath, который вы использовали?
Я использовал этот xpath ://*[@id='tittle-id1' ]/following::*[count(following-sibling::*[name()=name(preceding-sibling::*[@id='tittle-id1'])])=1]. Похоже, что этот xpath дает именно «абзац», за исключением того, что он для title-id?. Я не вправе приводить пример из реальной жизни, и это может быть причиной того, что вы не можете получить правильный ответ.
Попробовал ваш xpath и образец страницы, который я загрузил в ответ, и он работает, как и ожидалось. Можно ли смоделировать данные и поделиться точной структурой?
Поскольку предикаты в 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.
Я попробовал это с помощью scrapy. Я получаю весь текст от идентификатора до конца документа, возможно, это связано с тем же фактом, неправильно сформированным xml
@Deyesta Это работает, и есть тестовый пример с вашим собственным входным образцом. Если есть другие входные данные, для которых это выражение XPath не выбирает то, что вы хотите, пожалуйста, опубликуйте свой тестовый пример, как это сделал я.
Это то, что сработало для меня:
Для этого имейте в виду, что я использую шероховатый с питон-2.7:
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")
В целом это должно работать. Я тестировал его на нескольких заголовках с разными уровнями, и у меня он отлично работает.
Это сработало бы, если бы я знал, что будет тегом html заголовка (
<h2>в примере). Но если вы внимательно читаете, у меня есть только идентификатор. Я не знаю, что это за тег заголовка. Это может быть любой из них от<h1>до<h6>