.oO SearXNG Developer Documentation Oo.
Loading...
Searching...
No Matches
autocomplete.py
Go to the documentation of this file.
1# SPDX-License-Identifier: AGPL-3.0-or-later
2"""This module implements functions needed for the autocompleter.
3
4"""
5# pylint: disable=use-dict-literal
6
7import json
8from urllib.parse import urlencode, quote_plus
9
10import lxml
11from httpx import HTTPError
12
13from searx import settings
14from searx.engines import (
15 engines,
16 google,
17)
18from searx.network import get as http_get, post as http_post
19from searx.exceptions import SearxEngineResponseException
20
21
22def update_kwargs(**kwargs):
23 if 'timeout' not in kwargs:
24 kwargs['timeout'] = settings['outgoing']['request_timeout']
25 kwargs['raise_for_httperror'] = True
26
27
28def get(*args, **kwargs):
29 update_kwargs(**kwargs)
30 return http_get(*args, **kwargs)
31
32
33def post(*args, **kwargs):
34 update_kwargs(**kwargs)
35 return http_post(*args, **kwargs)
36
37
38def brave(query, _lang):
39 # brave search autocompleter
40 url = 'https://search.brave.com/api/suggest?'
41 url += urlencode({'q': query})
42 country = 'all'
43 # if lang in _brave:
44 # country = lang
45 kwargs = {'cookies': {'country': country}}
46 resp = get(url, **kwargs)
47
48 results = []
49
50 if resp.ok:
51 data = resp.json()
52 for item in data[1]:
53 results.append(item)
54 return results
55
56
57def dbpedia(query, _lang):
58 # dbpedia autocompleter, no HTTPS
59 autocomplete_url = 'https://lookup.dbpedia.org/api/search.asmx/KeywordSearch?'
60
61 response = get(autocomplete_url + urlencode(dict(QueryString=query)))
62
63 results = []
64
65 if response.ok:
66 dom = lxml.etree.fromstring(response.content)
67 results = dom.xpath('//Result/Label//text()')
68
69 return results
70
71
72def duckduckgo(query, sxng_locale):
73 """Autocomplete from DuckDuckGo. Supports DuckDuckGo's languages"""
74
75 traits = engines['duckduckgo'].traits
76 args = {
77 'q': query,
78 'kl': traits.get_region(sxng_locale, traits.all_locale),
79 }
80
81 url = 'https://duckduckgo.com/ac/?type=list&' + urlencode(args)
82 resp = get(url)
83
84 ret_val = []
85 if resp.ok:
86 j = resp.json()
87 if len(j) > 1:
88 ret_val = j[1]
89 return ret_val
90
91
92def google_complete(query, sxng_locale):
93 """Autocomplete from Google. Supports Google's languages and subdomains
94 (:py:obj:`searx.engines.google.get_google_info`) by using the async REST
95 API::
96
97 https://{subdomain}/complete/search?{args}
98
99 """
100
101 google_info = google.get_google_info({'searxng_locale': sxng_locale}, engines['google'].traits)
102
103 url = 'https://{subdomain}/complete/search?{args}'
104 args = urlencode(
105 {
106 'q': query,
107 'client': 'gws-wiz',
108 'hl': google_info['params']['hl'],
109 }
110 )
111 results = []
112 resp = get(url.format(subdomain=google_info['subdomain'], args=args))
113 if resp.ok:
114 json_txt = resp.text[resp.text.find('[') : resp.text.find(']', -3) + 1]
115 data = json.loads(json_txt)
116 for item in data[0]:
117 results.append(lxml.html.fromstring(item[0]).text_content())
118 return results
119
120
121def mwmbl(query, _lang):
122 """Autocomplete from Mwmbl_."""
123
124 # mwmbl autocompleter
125 url = 'https://api.mwmbl.org/search/complete?{query}'
126
127 results = get(url.format(query=urlencode({'q': query}))).json()[1]
128
129 # results starting with `go:` are direct urls and not useful for auto completion
130 return [result for result in results if not result.startswith("go: ") and not result.startswith("search: ")]
131
132
133def seznam(query, _lang):
134 # seznam search autocompleter
135 url = 'https://suggest.seznam.cz/fulltext/cs?{query}'
136
137 resp = get(
138 url.format(
139 query=urlencode(
140 {'phrase': query, 'cursorPosition': len(query), 'format': 'json-2', 'highlight': '1', 'count': '6'}
141 )
142 )
143 )
144
145 if not resp.ok:
146 return []
147
148 data = resp.json()
149 return [
150 ''.join([part.get('text', '') for part in item.get('text', [])])
151 for item in data.get('result', [])
152 if item.get('itemType', None) == 'ItemType.TEXT'
153 ]
154
155
156def stract(query, _lang):
157 # stract autocompleter (beta)
158 url = f"https://stract.com/beta/api/autosuggest?q={quote_plus(query)}"
159
160 resp = post(url)
161
162 if not resp.ok:
163 return []
164
165 return [suggestion['raw'] for suggestion in resp.json()]
166
167
168def startpage(query, sxng_locale):
169 """Autocomplete from Startpage. Supports Startpage's languages"""
170 lui = engines['startpage'].traits.get_language(sxng_locale, 'english')
171 url = 'https://startpage.com/suggestions?{query}'
172 resp = get(url.format(query=urlencode({'q': query, 'segment': 'startpage.udog', 'lui': lui})))
173 data = resp.json()
174 return [e['text'] for e in data.get('suggestions', []) if 'text' in e]
175
176
177def swisscows(query, _lang):
178 # swisscows autocompleter
179 url = 'https://swisscows.ch/api/suggest?{query}&itemsCount=5'
180
181 resp = json.loads(get(url.format(query=urlencode({'query': query}))).text)
182 return resp
183
184
185def qwant(query, sxng_locale):
186 """Autocomplete from Qwant. Supports Qwant's regions."""
187 results = []
188
189 locale = engines['qwant'].traits.get_region(sxng_locale, 'en_US')
190 url = 'https://api.qwant.com/v3/suggest?{query}'
191 resp = get(url.format(query=urlencode({'q': query, 'locale': locale, 'version': '2'})))
192
193 if resp.ok:
194 data = resp.json()
195 if data['status'] == 'success':
196 for item in data['data']['items']:
197 results.append(item['value'])
198
199 return results
200
201
202def wikipedia(query, sxng_locale):
203 """Autocomplete from Wikipedia. Supports Wikipedia's languages (aka netloc)."""
204 results = []
205 eng_traits = engines['wikipedia'].traits
206 wiki_lang = eng_traits.get_language(sxng_locale, 'en')
207 wiki_netloc = eng_traits.custom['wiki_netloc'].get(wiki_lang, 'en.wikipedia.org')
208
209 url = 'https://{wiki_netloc}/w/api.php?{args}'
210 args = urlencode(
211 {
212 'action': 'opensearch',
213 'format': 'json',
214 'formatversion': '2',
215 'search': query,
216 'namespace': '0',
217 'limit': '10',
218 }
219 )
220 resp = get(url.format(args=args, wiki_netloc=wiki_netloc))
221 if resp.ok:
222 data = resp.json()
223 if len(data) > 1:
224 results = data[1]
225
226 return results
227
228
229def yandex(query, _lang):
230 # yandex autocompleter
231 url = "https://suggest.yandex.com/suggest-ff.cgi?{0}"
232
233 resp = json.loads(get(url.format(urlencode(dict(part=query)))).text)
234 if len(resp) > 1:
235 return resp[1]
236 return []
237
238
239backends = {
240 'dbpedia': dbpedia,
241 'duckduckgo': duckduckgo,
242 'google': google_complete,
243 'mwmbl': mwmbl,
244 'seznam': seznam,
245 'startpage': startpage,
246 'stract': stract,
247 'swisscows': swisscows,
248 'qwant': qwant,
249 'wikipedia': wikipedia,
250 'brave': brave,
251 'yandex': yandex,
252}
253
254
255def search_autocomplete(backend_name, query, sxng_locale):
256 backend = backends.get(backend_name)
257 if backend is None:
258 return []
259 try:
260 return backend(query, sxng_locale)
261 except (HTTPError, SearxEngineResponseException):
262 return []
search_autocomplete(backend_name, query, sxng_locale)
qwant(query, sxng_locale)
seznam(query, _lang)
yandex(query, _lang)
post(*args, **kwargs)
wikipedia(query, sxng_locale)
google_complete(query, sxng_locale)
dbpedia(query, _lang)
stract(query, _lang)
brave(query, _lang)
update_kwargs(**kwargs)
swisscows(query, _lang)
duckduckgo(query, sxng_locale)
mwmbl(query, _lang)
startpage(query, sxng_locale)
get(*args, **kwargs)
::1337x
Definition 1337x.py:1