2

I have an XPath which has a single quote in XPath which is causing a SyntaxError: error.

I've tried with escape sequence:

xpath = "//label[contains(text(),'Ayuntamiento de la Vall d'Uixó  - Festivales Musix')]"

But I am still facing an error:

SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//label[contains(text(),'Ayuntamiento de la Vall d'Uixó - Festivales Musix')]' is not a valid XPath expression.

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352

4 Answers4

2

There is no quote escaping in XPath string literals. (Note: This answer applies to XPath 1.0. In higher versions of XPath, this issue is addressed - see the comment below.)

The only way to get the desired result in pure XPath is by concatenating alternately-quoted strings.

//label[contains(., concat('Ayuntamiento de la Vall d', "'", 'Uixó - Festivales Musix'))]

You can build these kinds of expressions mechanically by splitting the target string at the single quote and joining the parts again with ', "'" , ' as the new separator. Python example:

search_value = "Ayuntamiento de la Vall d'Uixó - Festivales Musix"  # could contain both " and '

xpath = "//label[contains(., %s)]" % xpath_string_escape(search_value)

def xpath_string_escape(input_str):
    """ creates a concatenation of alternately-quoted strings that is always a valid XPath expression """
    parts = input_str.split("'")
    return "concat('" + "', \"'\" , '".join(parts) + "', '')"

Some XPath libraries support bound parameters (much like SQL) to get around this, but the above is the only approach that works everywhere.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 1
    Actually, in XPath 2.0 and later, there is such quote escaping mechanism. a `"` is escaped by representing it with two adjacent quotes: `""` and escaped apostrophe is represented by two apostrophes: `''`. See rules 74 to 76 in: https://www.w3.org/TR/xpath20/#id-literals – Dimitre Novatchev Aug 25 '19 at 02:52
  • 1
    You're right, I presumed XPath 1.0 for this question. I'll update my wording. – Tomalak Aug 25 '19 at 04:31
  • @Tomalak your snippet should return this instead: `return "concat('" + '\',"\'",\''.join(parts) + "', '')"` – Néstor Jan 15 '20 at 06:27
  • 1
    @Néstor You are right, this was borked. Fixed now, thanks! – Tomalak Jan 15 '20 at 07:50
0

Try the below xpath.

xpath = "//label[contains(text(), \"Ayuntamiento de la Vall d'Uixó  - Festivales Musix\")]"
KunduK
  • 32,888
  • 5
  • 17
  • 41
  • What do you do when the string being searched for contains a double quote? What do you do when you don't know what kind of quotes it contains, because the string being because it's a run-time value? – Tomalak Aug 26 '19 at 17:26
0

To construct an within double quotes which includes text with single quotes in Python you can use the following Locator Strategy:

xpath = "//label[text()=\"Ayuntamiento de la Vall d'Uixó  - Festivales Musix\"]"
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • What if the search string contains a double quote? – Tomalak Aug 25 '19 at 11:13
  • That can be handled too. – undetected Selenium Aug 25 '19 at 15:11
  • 1
    Okay... how? What if it contains both types of quotes? The question is not about how to construct a Python string, the question is about how to construct an XPath string. – Tomalak Aug 25 '19 at 17:11
  • @Tomalak Can you raise a new question with your exact requirement. StackOverflow contributors will be happy to help you out. – undetected Selenium Aug 25 '19 at 19:24
  • I have no requirement. I'm asking *you*, because your answer does not work in the context of the OP's question. It's technically correct, but not useful in practice. – Tomalak Aug 25 '19 at 19:30
  • @Tomalak The fact is the question is tagged with [tag:selenium] and [tag:selenium-webdriver]. As a selenium contributor we are still working for Selenium to support XPath 2.x and 3.x. Unfortunately as of now Selenium supports on XPath 1.x. So there are virtually only a few options to offer. Hence my answer. – undetected Selenium Aug 25 '19 at 19:35
  • @Tomalak In-case of a double quote you would need to use _JavascriptExecutor_ – undetected Selenium Aug 25 '19 at 19:38
  • That's why I said, it's technically correct but not practical. The OP very likely has run into this while they tried this kind of thing: `xpath = "//label[contains(., '%s')]" % dynamic_value`. This works most of the time, until it doesn't. And nothing you can do to Python strings will remedy this. – Tomalak Aug 25 '19 at 19:39
-1

You could define the search string using triple quotes - then you won't have to worry about any potential special characters and quotes inside your string.

Here is an example:

xpath = """//label[contains(text(), "Ayuntamiento de la Vall d'Uixó  - Festivales Musix")]"""

If you also want to include backslashes in your string, you can use raw triple quotes:

xpath = r"""raw triple quotes string allow the use of '\'"""

See PEP257 for more details.

m_____z
  • 1,521
  • 13
  • 22
  • 2
    You don't need triple quotes to get a single quote into a double quoted string. The issue is not syntactic validity at the Python level, it's syntactic validity at the XPath level, and triple quoted strings are not going to cut it there. Especially when the value being searched for is user-supplied and could contain both single- and double quotes. – Tomalak Aug 24 '19 at 17:39
  • The original XPath string posted by the user consists of both double and single quotes - in which case the triple quote is a solution to resolve issues regarding single and double quotes. The syntactical validity of the XPath itself is another question of course. – m_____z Aug 24 '19 at 19:47
  • 1
    No, it's not. It only solves the issue on the Python level, and that's not enough. An XPath string that contains both double and single quotes is still illegal. – Tomalak Aug 25 '19 at 11:14
  • 1
    Imagine this situation in Python code. `xpath = "//label[contains(text(), '%s')]" % dynamic_value`, which is extremely likely what the OP has. What do you do to prevent run-time errors no matter what `dynamic_value` contains? – Tomalak Aug 25 '19 at 11:20