diff --git a/changedetectionio/forms.py b/changedetectionio/forms.py index 7199b445..3cbe20db 100644 --- a/changedetectionio/forms.py +++ b/changedetectionio/forms.py @@ -316,10 +316,11 @@ class ValidateCSSJSONXPATHInput(object): if not self.allow_xpath: raise ValidationError("XPath not permitted in this field!") from lxml import etree, html + import elementpath tree = html.fromstring("") try: - tree.xpath(line.strip()) + elementpath.select(tree, line) except etree.XPathEvalError as e: message = field.gettext('\'%s\' is not a valid XPath expression. (%s)') raise ValidationError(message % (line, str(e))) diff --git a/changedetectionio/html_tools.py b/changedetectionio/html_tools.py index 0cdaeea4..eeaa68f4 100644 --- a/changedetectionio/html_tools.py +++ b/changedetectionio/html_tools.py @@ -51,12 +51,13 @@ def element_removal(selectors: List[str], html_content): # Return str Utf-8 of matched rules def xpath_filter(xpath_filter, html_content, append_pretty_line_formatting=False): + import elementpath from lxml import etree, html tree = html.fromstring(bytes(html_content, encoding='utf-8')) html_block = "" - r = tree.xpath(xpath_filter.strip(), namespaces={'re': 'http://exslt.org/regular-expressions'}) + r = elementpath.select(tree, xpath_filter.strip()) #@note: //title/text() wont work where CDATA.. for element in r: diff --git a/changedetectionio/tests/test_xpath_selector.py b/changedetectionio/tests/test_xpath_selector.py index fa8f4e8d..a40ec8b1 100644 --- a/changedetectionio/tests/test_xpath_selector.py +++ b/changedetectionio/tests/test_xpath_selector.py @@ -2,7 +2,7 @@ import time from flask import url_for -from . util import live_server_setup +from .util import live_server_setup, wait_for_all_checks from ..html_tools import * @@ -164,6 +164,7 @@ def test_check_xpath_text_function_utf8(client, live_server): assert b'Deleted' in res.data def test_check_markup_xpath_filter_restriction(client, live_server): + live_server_setup(live_server) sleep_time_for_fetch_thread = 3 xpath_filter = "//*[contains(@class, 'sametext')]" @@ -183,7 +184,7 @@ def test_check_markup_xpath_filter_restriction(client, live_server): assert b"1 Imported" in res.data # Give the thread time to pick it up - time.sleep(sleep_time_for_fetch_thread) + wait_for_all_checks(client) # Goto the edit page, add our ignore text # Add our URL to the import page @@ -195,7 +196,7 @@ def test_check_markup_xpath_filter_restriction(client, live_server): assert b"Updated watch." in res.data # Give the thread time to pick it up - time.sleep(sleep_time_for_fetch_thread) + wait_for_all_checks(client) # view it/reset state back to viewed client.get(url_for("diff_history_page", uuid="first"), follow_redirects=True) @@ -206,7 +207,7 @@ def test_check_markup_xpath_filter_restriction(client, live_server): # Trigger a check client.get(url_for("form_watch_checknow"), follow_redirects=True) # Give the thread time to pick it up - time.sleep(sleep_time_for_fetch_thread) + wait_for_all_checks(client) res = client.get(url_for("index")) assert b'unviewed' not in res.data diff --git a/requirements.txt b/requirements.txt index 2e53e1b7..6d51bfd9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,6 +42,8 @@ paho-mqtt # (introduced once apprise became a dep) cryptography~=3.4 +elementpath + # Used for CSS filtering beautifulsoup4