@@ -1,23 +1,23 @@
1−# WolframAlpha (Maths)
1+# Wolfram|Alpha (Science)
3−# @website http://www.wolframalpha.com/
3+# @website https://www.wolframalpha.com/
4−# @provide-api yes (http://api.wolframalpha.com/v2/)
4+# @provide-api yes (https://api.wolframalpha.com/v2/)
6# @using-api no
6# @using-api no
7−# @results HTML
7+# @results JSON
8# @stable no
8# @stable no
9−# @parse answer
9+# @parse url, infobox
11from cgi import escape
11from cgi import escape
12from json import loads
12from json import loads
13from time import time
13from time import time
14from urllib import urlencode
14from urllib import urlencode
15+from lxml.etree import XML
16from searx.poolrequests import get as http_get
17from searx.poolrequests import get as http_get
18# search-url
19# search-url
19url = 'https://www.wolframalpha.com/'
20url = 'https://www.wolframalpha.com/'
20−search_url = url + 'input/?{query}'
22search_url = url + 'input/json.jsp'\
22search_url = url + 'input/json.jsp'\
23 '?async=true'\
23 '?async=true'\
@@ -33,13 +33,25 @@ search_url = url + 'input/json.jsp'\
33 '&sponsorcategories=true'\
33 '&sponsorcategories=true'\
34 '&statemethod=deploybutton'
34 '&statemethod=deploybutton'
36−# xpath variables
36+referer_url = url + 'input/?{query}'
37−scripts_xpath = '//script'
37+
38−title_xpath = '//title'
39−failure_xpath = '//p[attribute::class="pfail"]'
40token = {'value': '',
38token = {'value': '',
41 'last_updated': None}
39 'last_updated': None}
42+success_xpath = '/pod[attribute::error="false"]'
43+plaintext_xpath = './plaintext'
44+title_xpath = './@title'
46+img_src_xpath = './img/@src'
47+img_alt_xpath = './img/@alt'
49+# pods to display as image in infobox
50+# this pods do return a plaintext, but they look better and are more useful as images
51+image_pods = {'Visual representation',
52+ 'Manipulatives illustration',
44# seems, wolframalpha resets its token in every hour
56# seems, wolframalpha resets its token in every hour
45def obtain_token():
57def obtain_token():
@@ -62,13 +74,42 @@ def request(query, params):
62 if time() - token['last_updated'] > 3600:
74 if time() - token['last_updated'] > 3600:
63 obtain_token()
75 obtain_token()
64 params['url'] = search_url.format(query=urlencode({'input': query}), token=token['value'])
76 params['url'] = search_url.format(query=urlencode({'input': query}), token=token['value'])
65− params['headers']['Referer'] = 'https://www.wolframalpha.com/input/?i=' + query
77+ params['headers']['Referer'] = referer_url.format(query=urlencode({'i': query}))
67 return params
79 return params
83+# NOTE: this makes an additional requests to server, so the response will take longer and might reach timeout
84+def get_async_pod(url):
85+ pod = {'subpods': []}
88+ resp = http_get(url, timeout=2.0)
90+ resp_pod = XML(resp.content)
91+ if resp_pod.xpath(success_xpath):
93+ for subpod in resp_pod:
94+ plaintext = subpod.xpath(plaintext_xpath)[0].text
96+ pod['subpods'].append({'title': subpod.xpath(title_xpath)[0],
97+ 'plaintext': plaintext})
98+ elif subpod.xpath(image_xpath):
99+ pod['subpods'].append({'title': subpod.xpath(title_xpath)[0],
101+ 'img': {'src': subpod.xpath(img_src_xpath)[0],
102+ 'alt': subpod.xpath(img_alt_xpath)[0]}})
70# get response from search-request
109# get response from search-request
71def response(resp):
110def response(resp):
72 resp_json = loads(resp.text)
113 resp_json = loads(resp.text)
74 if not resp_json['queryresult']['success']:
115 if not resp_json['queryresult']['success']:
@@ -76,20 +117,45 @@ def response(resp):
77 # TODO handle resp_json['queryresult']['assumptions']
118 # TODO handle resp_json['queryresult']['assumptions']
78 result_chunks = []
119 result_chunks = []
120+ infobox_title = None
79 for pod in resp_json['queryresult']['pods']:
121 for pod in resp_json['queryresult']['pods']:
80 pod_title = pod.get('title', '')
122 pod_title = pod.get('title', '')
81 if 'subpods' not in pod:
124 if 'subpods' not in pod:
125+ # comment this section if your requests always reach timeout
127+ result = get_async_pod(pod['async'])
82− continue
131+ continue
133+ # infobox title is input or text content on first pod
134+ if pod_title.startswith('Input') or not infobox_title:
136+ infobox_title = pod['subpods'][0]['plaintext']
83 for subpod in pod['subpods']:
141 for subpod in pod['subpods']:
84− if 'img' in subpod:
142+ if subpod['plaintext'] != '' and pod_title not in image_pods:
85− result_chunks.append(u'<p>{0}<br /><img src="{1}" alt="{2}" /></p>'
143+ # append unless it's not an actual answer
86− .format(escape(pod_title or subpod['img']['alt']),
144+ if subpod['plaintext'] != '(requires interactivity)':
87− escape(subpod['img']['src']),
145+ result_chunks.append({'label': pod_title, 'value': subpod['plaintext']})
88− escape(subpod['img']['alt'])))
146+
147+ elif 'img' in subpod:
148+ result_chunks.append({'label': pod_title, 'image': subpod['img']})
90 if not result_chunks:
150 if not result_chunks:
91 return []
151 return []
93− return [{'url': resp.request.headers['Referer'].decode('utf-8'),
153+ results.append({'infobox': infobox_title,
94− 'title': 'Wolframalpha',
154+ 'attributes': result_chunks,
95− 'content': ''.join(result_chunks)}]
155+ 'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]})
157+ results.append({'url': resp.request.headers['Referer'],
158+ 'title': 'Wolfram|Alpha',
159+ 'content': infobox_title})