Rico Sonntag
Rico Sonntag

Reputation: 1516

Xpath: Extract text between tags, but stop as soon as an embedded tag occurs

I would like to extract the text within the following HTML. However, everything that occurs within an enclosed HTML tag and everything that comes after it should be ignored.

The HTML appears in different forms.

<span class="classA">Text 1 <span class="classB">Text 2</span> Text 3 <span class="classC">Text 4</span> Text 5</span>

Desired result: "Text 1 Text 2 Text 3"

Other variants:

<span class="classA">Text 1 <span class="classC">Text 2</span></span>
<span class="classA">Text 1 <span class="classC">Text 2</span> Text 3</span>
<span class="classA">Text 1</span>

Desired result: "Text 1"

<span class="classA">Text 1 <span class="classB">Text 2</span> Text 3</span>

Desired result: "Text 1 Text 2 Text3"

So everything after the occurrence of a span element with class "classC" should be ignored. It's also possible that "classC" doesn't appear at all.

I already tried //span[@class="classA"]//text()[parent::*[not(@class="classC")]], this ignores "classC" content, but returns the text after <span class="classC"> (Text 5 from the first example).

How can I achieve this?

Update:

With //span[@class="classC"]//parent::*/preceding::text() I'm getting a little closer to the matter. However, it still doesn't work with <span class="classA">Text 1</span>, which returns noting.

Upvotes: 1

Views: 50

Answers (2)

Siebe Jongebloed
Siebe Jongebloed

Reputation: 4834

Try this XPath:

//text()[not(preceding::span[@class="classC"]|ancestor::span[@class="classC"])]

but as Michael Kay said it could be very inefficient, depending on your source html.

Upvotes: 1

Michael Kay
Michael Kay

Reputation: 163262

You haven't said which XPath version you are using. This is quite hard to achieve using XPath 1.0 which is all that PHP's DOMXPath supports.

Logically you can do

.//text() except .//span[@class="ClassC"]/following::text()

but the except operator requires XPath 2.0. A workaround is that in XPath 1.0 you can rewrite (A except B) as A[count(.|B)!=count(B)] but it's potentially very inefficient.

Upvotes: 1

Related Questions