diff --git a/requirements-dev.txt b/requirements-dev.txt index 189fd5a7d..2fad45f14 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,6 +8,7 @@ transifex-client==0.14.2 selenium==3.141.0 twine==3.4.1 Pallets-Sphinx-Themes==1.2.3 +docutils==0.16 Sphinx==3.5.3 sphinx-issues==1.2.0 sphinx-jinja==1.1.1 diff --git a/searx/engines/wordnik.py b/searx/engines/wordnik.py new file mode 100644 index 000000000..3abe9efa2 --- /dev/null +++ b/searx/engines/wordnik.py @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +"""Wordnik (general) + +""" + +from lxml.html import fromstring +from searx import logger +from searx.utils import extract_text +from searx.raise_for_httperror import raise_for_httperror + +logger = logger.getChild('Wordnik engine') + +# about +about = { + "website": 'https://www.wordnik.com', + "wikidata_id": 'Q8034401', + "official_api_documentation": None, + "use_official_api": False, + "require_api_key": False, + "results": 'HTML', +} + +categories = ['general'] +paging = False + +URL = 'https://www.wordnik.com' +SEARCH_URL = URL + '/words/{query}' + + +def request(query, params): + params['url'] = SEARCH_URL.format(query=query) + logger.debug(f"query_url --> {params['url']}") + return params + + +def response(resp): + results = [] + + raise_for_httperror(resp) + dom = fromstring(resp.text) + word = extract_text(dom.xpath('//*[@id="headword"]/text()')) + + definitions = [] + for src in dom.xpath('//*[@id="define"]//h3[@class="source"]'): + src_text = extract_text(src).strip() + if src_text.startswith('from '): + src_text = src_text[5:] + + src_defs = [] + for def_item in src.xpath('following-sibling::ul[1]/li'): + def_abbr = extract_text(def_item.xpath('.//abbr')).strip() + def_text = extract_text(def_item).strip() + if def_abbr: + def_text = def_text[len(def_abbr):].strip() + src_defs.append((def_abbr, def_text)) + + definitions.append((src_text, src_defs)) + + if not definitions: + return results + + infobox = '' + for src_text, src_defs in definitions: + infobox += f"{src_text}" + infobox += "" + + results.append({ + 'infobox': word, + 'content': infobox, + }) + + return results diff --git a/searx/settings.yml b/searx/settings.yml index 81ef737db..8f2e50bc9 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -1271,6 +1271,14 @@ engines: categories: videos disabled : True + - name: wordnik + engine: wordnik + shortcut: def + base_url: https://www.wordnik.com/ + categories: general + timeout: 5.0 + disabled: True + - name: słownik języka polskiego engine: sjp shortcut: sjp diff --git a/searx/webapp.py b/searx/webapp.py index 4c09f0ac6..072f140ca 100755 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -1133,11 +1133,14 @@ class ReverseProxyPathFix: base_url = urlparse(settings['server']['base_url']) self.script_name = base_url.path + if self.script_name.endswith('/'): + # remove trailing slash to avoid infinite redirect on the index + # see https://github.com/searx/searx/issues/2729 + self.script_name = self.script_name[:-1] self.scheme = base_url.scheme self.server = base_url.netloc def __call__(self, environ, start_response): - script_name = self.script_name or environ.get('HTTP_X_SCRIPT_NAME', '') if script_name: environ['SCRIPT_NAME'] = script_name