Firstly, you do not tell us the version of your XPath. There are XPath 1.0, 2.0, and 3.0/3.1. There are different. Each is built on the previous version.
For XPath 1.0
You can refer to XPath between two elements.
The idea is to get two lists, one is from <h6> Text1 </h6> to end (following-sibling), the other from <h6> Text 2</h6> to start(preceding-sibling). Then, get the intersect of the two lists.
In the last predicate, we put the node to the list, get the count, and we compare the count of the list. If they equal, the node is in the list.
The following is the code in Python for your problem:
from lxml import etree
root = etree.XML("""
<root>
<h6> Text 1 </h6>
<ul>
<li><span> list1 </span></li>
<li><span> list2 </span></li>
<li><span> list3 </span></li>
</ul>
<div> </div>
<h6> Text 2 </h6>
</root>
""")
root.xpath("h6[text()=' Text 2 ']/preceding-sibling::*[count(.|/root/h6[text()=' Text 1 ']/following-sibling::*)=count(/root/h6[text()=' Text 1 ']/following-sibling::*)]")
For XPath 2.0
There is an intersect keyword, so the previous XPath can be simplified to:
h6[text()=' Text 1 ']/following-sibling::* intersect h6[text()=' Text 2 ']/preceding-sibling::*
For XPath 3.0/3.1
You can use variable declaration to find the start position and end position first. And select elements between the two positions.
let $x := index-of(h6[text()=' Text 1 ']),
$y := index-of(h6[text()=' Text 2 '])
return *[position()>=$x and position()<=$y]
The solution in XPath 3.x will be much faster because the time complexity is only n, while for XPath 2.0 and 1.0, the time complexity is n-squared.
Again, I do not know your XPath version. for lxml, you can only use XPath 1.0. For other packages, please find out yourself.