We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Duden search engine not working anymore
They changed the site layout.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `searx/engines/duden.py`
Content:
```
1 """
2  Duden
3  @website     https://www.duden.de
4  @provide-api no
5  @using-api   no
6  @results     HTML (using search portal)
7  @stable      no (HTML can change)
8  @parse       url, title, content
9 """
10 
11 from lxml import html, etree
12 import re
13 from searx.engines.xpath import extract_text
14 from searx.url_utils import quote
15 from searx import logger
16 
17 categories = ['general']
18 paging = True
19 language_support = False
20 
21 # search-url
22 base_url = 'https://www.duden.de/'
23 search_url = base_url + 'suchen/dudenonline/{query}?page={offset}'
24 
25 
26 def request(query, params):
27     '''pre-request callback
28     params<dict>:
29       method  : POST/GET
30       headers : {}
31       data    : {} # if method == POST
32       url     : ''
33       category: 'search category'
34       pageno  : 1 # number of the requested page
35     '''
36 
37     offset = (params['pageno'] - 1)
38     params['url'] = search_url.format(offset=offset, query=quote(query))
39     return params
40 
41 
42 def response(resp):
43     '''post-response callback
44     resp: requests response object
45     '''
46     results = []
47 
48     dom = html.fromstring(resp.text)
49 
50     try:
51         number_of_results_string = re.sub('[^0-9]', '', dom.xpath(
52             '//a[@class="active" and contains(@href,"/suchen/dudenonline")]/span/text()')[0]
53         )
54 
55         results.append({'number_of_results': int(number_of_results_string)})
56 
57     except:
58         logger.debug("Couldn't read number of results.")
59         pass
60 
61     for result in dom.xpath('//section[@class="wide" and not(contains(@style,"overflow:hidden"))]'):
62         try:
63             logger.debug("running for %s" % str(result))
64             link = result.xpath('.//h2/a')[0]
65             url = link.attrib.get('href')
66             title = result.xpath('string(.//h2/a)')
67             content = extract_text(result.xpath('.//p'))
68             # append result
69             results.append({'url': url,
70                             'title': title,
71                             'content': content})
72         except:
73             logger.debug('result parse error in:\n%s', etree.tostring(result, pretty_print=True))
74             continue
75 
76     return results
77
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/searx/engines/duden.py b/searx/engines/duden.py
--- a/searx/engines/duden.py
+++ b/searx/engines/duden.py
@@ -11,7 +11,7 @@
 from lxml import html, etree
 import re
 from searx.engines.xpath import extract_text
-from searx.url_utils import quote
+from searx.url_utils import quote, urljoin
 from searx import logger
 
 categories = ['general']
@@ -20,7 +20,7 @@
 
 # search-url
 base_url = 'https://www.duden.de/'
-search_url = base_url + 'suchen/dudenonline/{query}?page={offset}'
+search_url = base_url + 'suchen/dudenonline/{query}?search_api_fulltext=&page={offset}'
 
 
 def request(query, params):
@@ -35,7 +35,11 @@
     '''
 
     offset = (params['pageno'] - 1)
-    params['url'] = search_url.format(offset=offset, query=quote(query))
+    if offset == 0:
+        search_url_fmt = base_url + 'suchen/dudenonline/{query}'
+        params['url'] = search_url_fmt.format(query=quote(query))
+    else:
+        params['url'] = search_url.format(offset=offset, query=quote(query))
     return params
 
 
@@ -58,12 +62,11 @@
         logger.debug("Couldn't read number of results.")
         pass
 
-    for result in dom.xpath('//section[@class="wide" and not(contains(@style,"overflow:hidden"))]'):
+    for result in dom.xpath('//section[not(contains(@class, "essay"))]'):
         try:
-            logger.debug("running for %s" % str(result))
-            link = result.xpath('.//h2/a')[0]
-            url = link.attrib.get('href')
-            title = result.xpath('string(.//h2/a)')
+            url = result.xpath('.//h2/a')[0].get('href')
+            url = urljoin(base_url, url)
+            title = result.xpath('string(.//h2/a)').strip()
             content = extract_text(result.xpath('.//p'))
             # append result
             results.append({'url': url,

{"golden_diff": "diff --git a/searx/engines/duden.py b/searx/engines/duden.py\n--- a/searx/engines/duden.py\n+++ b/searx/engines/duden.py\n@@ -11,7 +11,7 @@\n from lxml import html, etree\n import re\n from searx.engines.xpath import extract_text\n-from searx.url_utils import quote\n+from searx.url_utils import quote, urljoin\n from searx import logger\n \n categories = ['general']\n@@ -20,7 +20,7 @@\n \n # search-url\n base_url = 'https://www.duden.de/'\n-search_url = base_url + 'suchen/dudenonline/{query}?page={offset}'\n+search_url = base_url + 'suchen/dudenonline/{query}?search_api_fulltext=&page={offset}'\n \n \n def request(query, params):\n@@ -35,7 +35,11 @@\n     '''\n \n     offset = (params['pageno'] - 1)\n-    params['url'] = search_url.format(offset=offset, query=quote(query))\n+    if offset == 0:\n+        search_url_fmt = base_url + 'suchen/dudenonline/{query}'\n+        params['url'] = search_url_fmt.format(query=quote(query))\n+    else:\n+        params['url'] = search_url.format(offset=offset, query=quote(query))\n     return params\n \n \n@@ -58,12 +62,11 @@\n         logger.debug(\"Couldn't read number of results.\")\n         pass\n \n-    for result in dom.xpath('//section[@class=\"wide\" and not(contains(@style,\"overflow:hidden\"))]'):\n+    for result in dom.xpath('//section[not(contains(@class, \"essay\"))]'):\n         try:\n-            logger.debug(\"running for %s\" % str(result))\n-            link = result.xpath('.//h2/a')[0]\n-            url = link.attrib.get('href')\n-            title = result.xpath('string(.//h2/a)')\n+            url = result.xpath('.//h2/a')[0].get('href')\n+            url = urljoin(base_url, url)\n+            title = result.xpath('string(.//h2/a)').strip()\n             content = extract_text(result.xpath('.//p'))\n             # append result\n             results.append({'url': url,\n", "issue": "Duden search engine not working anymore\nThey changed the site layout.\n", "before_files": [{"content": "\"\"\"\n Duden\n @website     https://www.duden.de\n @provide-api no\n @using-api   no\n @results     HTML (using search portal)\n @stable      no (HTML can change)\n @parse       url, title, content\n\"\"\"\n\nfrom lxml import html, etree\nimport re\nfrom searx.engines.xpath import extract_text\nfrom searx.url_utils import quote\nfrom searx import logger\n\ncategories = ['general']\npaging = True\nlanguage_support = False\n\n# search-url\nbase_url = 'https://www.duden.de/'\nsearch_url = base_url + 'suchen/dudenonline/{query}?page={offset}'\n\n\ndef request(query, params):\n    '''pre-request callback\n    params<dict>:\n      method  : POST/GET\n      headers : {}\n      data    : {} # if method == POST\n      url     : ''\n      category: 'search category'\n      pageno  : 1 # number of the requested page\n    '''\n\n    offset = (params['pageno'] - 1)\n    params['url'] = search_url.format(offset=offset, query=quote(query))\n    return params\n\n\ndef response(resp):\n    '''post-response callback\n    resp: requests response object\n    '''\n    results = []\n\n    dom = html.fromstring(resp.text)\n\n    try:\n        number_of_results_string = re.sub('[^0-9]', '', dom.xpath(\n            '//a[@class=\"active\" and contains(@href,\"/suchen/dudenonline\")]/span/text()')[0]\n        )\n\n        results.append({'number_of_results': int(number_of_results_string)})\n\n    except:\n        logger.debug(\"Couldn't read number of results.\")\n        pass\n\n    for result in dom.xpath('//section[@class=\"wide\" and not(contains(@style,\"overflow:hidden\"))]'):\n        try:\n            logger.debug(\"running for %s\" % str(result))\n            link = result.xpath('.//h2/a')[0]\n            url = link.attrib.get('href')\n            title = result.xpath('string(.//h2/a)')\n            content = extract_text(result.xpath('.//p'))\n            # append result\n            results.append({'url': url,\n                            'title': title,\n                            'content': content})\n        except:\n            logger.debug('result parse error in:\\n%s', etree.tostring(result, pretty_print=True))\n            continue\n\n    return results\n", "path": "searx/engines/duden.py"}], "after_files": [{"content": "\"\"\"\n Duden\n @website     https://www.duden.de\n @provide-api no\n @using-api   no\n @results     HTML (using search portal)\n @stable      no (HTML can change)\n @parse       url, title, content\n\"\"\"\n\nfrom lxml import html, etree\nimport re\nfrom searx.engines.xpath import extract_text\nfrom searx.url_utils import quote, urljoin\nfrom searx import logger\n\ncategories = ['general']\npaging = True\nlanguage_support = False\n\n# search-url\nbase_url = 'https://www.duden.de/'\nsearch_url = base_url + 'suchen/dudenonline/{query}?search_api_fulltext=&page={offset}'\n\n\ndef request(query, params):\n    '''pre-request callback\n    params<dict>:\n      method  : POST/GET\n      headers : {}\n      data    : {} # if method == POST\n      url     : ''\n      category: 'search category'\n      pageno  : 1 # number of the requested page\n    '''\n\n    offset = (params['pageno'] - 1)\n    if offset == 0:\n        search_url_fmt = base_url + 'suchen/dudenonline/{query}'\n        params['url'] = search_url_fmt.format(query=quote(query))\n    else:\n        params['url'] = search_url.format(offset=offset, query=quote(query))\n    return params\n\n\ndef response(resp):\n    '''post-response callback\n    resp: requests response object\n    '''\n    results = []\n\n    dom = html.fromstring(resp.text)\n\n    try:\n        number_of_results_string = re.sub('[^0-9]', '', dom.xpath(\n            '//a[@class=\"active\" and contains(@href,\"/suchen/dudenonline\")]/span/text()')[0]\n        )\n\n        results.append({'number_of_results': int(number_of_results_string)})\n\n    except:\n        logger.debug(\"Couldn't read number of results.\")\n        pass\n\n    for result in dom.xpath('//section[not(contains(@class, \"essay\"))]'):\n        try:\n            url = result.xpath('.//h2/a')[0].get('href')\n            url = urljoin(base_url, url)\n            title = result.xpath('string(.//h2/a)').strip()\n            content = extract_text(result.xpath('.//p'))\n            # append result\n            results.append({'url': url,\n                            'title': title,\n                            'content': content})\n        except:\n            logger.debug('result parse error in:\\n%s', etree.tostring(result, pretty_print=True))\n            continue\n\n    return results\n", "path": "searx/engines/duden.py"}]}