Selaa lähdekoodia

Merge pull request #1061 from a01200356/bing

[fix] Language support for Bing Images and Videos
Adam Tauber 7 vuotta sitten
vanhempi
commit
c8a66a090a

+ 1
- 1
.travis.yml Näytä tiedosto

@@ -13,7 +13,7 @@ python:
13 13
 before_install:
14 14
   - "export DISPLAY=:99.0"
15 15
   - "sh -e /etc/init.d/xvfb start"
16
-  - npm install less less-plugin-clean-css grunt-cli
16
+  - npm install less@2.7 less-plugin-clean-css grunt-cli
17 17
   - export PATH=`pwd`/node_modules/.bin:$PATH
18 18
   - ./manage.sh install_geckodriver ~/drivers
19 19
   - export PATH=~/drivers:$PATH

+ 1
- 1
searx/data/engines_languages.json
File diff suppressed because it is too large
Näytä tiedosto


+ 47
- 9
searx/engines/bing_images.py Näytä tiedosto

@@ -18,7 +18,6 @@
18 18
 from lxml import html
19 19
 from json import loads
20 20
 import re
21
-from searx.engines.bing import _fetch_supported_languages, supported_languages_url
22 21
 from searx.url_utils import urlencode
23 22
 
24 23
 # engine dependent config
@@ -26,6 +25,8 @@ categories = ['images']
26 25
 paging = True
27 26
 safesearch = True
28 27
 time_range_support = True
28
+language_support = True
29
+supported_languages_url = 'https://www.bing.com/account/general'
29 30
 
30 31
 # search-url
31 32
 base_url = 'https://www.bing.com/'
@@ -45,23 +46,41 @@ safesearch_types = {2: 'STRICT',
45 46
 _quote_keys_regex = re.compile('({|,)([a-z][a-z0-9]*):(")', re.I | re.U)
46 47
 
47 48
 
49
+# get supported region code
50
+def get_region_code(lang, lang_list=None):
51
+    region = None
52
+    if lang in (lang_list or supported_languages):
53
+        region = lang
54
+    elif lang.startswith('no'):
55
+        region = 'nb-NO'
56
+    else:
57
+        # try to get a supported country code with language
58
+        lang = lang.split('-')[0]
59
+        for lc in (lang_list or supported_languages):
60
+            if lang == lc.split('-')[0]:
61
+                region = lc
62
+                break
63
+    if region:
64
+        return region.lower()
65
+    else:
66
+        return 'en-us'
67
+
68
+
48 69
 # do search-request
49 70
 def request(query, params):
50 71
     offset = (params['pageno'] - 1) * 10 + 1
51 72
 
52
-    # required for cookie
53
-    if params['language'] == 'all':
54
-        language = 'en-US'
55
-    else:
56
-        language = params['language']
57
-
58 73
     search_path = search_string.format(
59 74
         query=urlencode({'q': query}),
60 75
         offset=offset)
61 76
 
77
+    language = get_region_code(params['language'])
78
+
62 79
     params['cookies']['SRCHHPGUSR'] = \
63
-        'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0] +\
64
-        '&ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
80
+        'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
81
+
82
+    params['cookies']['_EDGE_S'] = 'mkt=' + language +\
83
+        '&ui=' + language + '&F=1'
65 84
 
66 85
     params['url'] = base_url + search_path
67 86
     if params['time_range'] in time_range_dict:
@@ -106,3 +125,22 @@ def response(resp):
106 125
 
107 126
     # return results
108 127
     return results
128
+
129
+
130
+# get supported languages from their site
131
+def _fetch_supported_languages(resp):
132
+    supported_languages = []
133
+    dom = html.fromstring(resp.text)
134
+
135
+    regions_xpath = '//div[@id="region-section-content"]' \
136
+                    + '//ul[@class="b_vList"]/li/a/@href'
137
+
138
+    regions = dom.xpath(regions_xpath)
139
+    for region in regions:
140
+        code = re.search('setmkt=[^\&]+', region).group()[7:]
141
+        if code == 'nb-NO':
142
+            code = 'no-NO'
143
+
144
+        supported_languages.append(code)
145
+
146
+    return supported_languages

+ 4
- 1
searx/engines/bing_videos.py Näytä tiedosto

@@ -12,6 +12,7 @@
12 12
 
13 13
 from json import loads
14 14
 from lxml import html
15
+from searx.engines.bing_images import _fetch_supported_languages, supported_languages_url, get_region_code
15 16
 from searx.engines.xpath import extract_text
16 17
 from searx.url_utils import urlencode
17 18
 
@@ -21,6 +22,7 @@ paging = True
21 22
 safesearch = True
22 23
 time_range_support = True
23 24
 number_of_results = 10
25
+language_support = True
24 26
 
25 27
 search_url = 'https://www.bing.com/videos/asyncv2?{query}&async=content&'\
26 28
              'first={offset}&count={number_of_results}&CW=1366&CH=25&FORM=R5VR5'
@@ -45,7 +47,8 @@ def request(query, params):
45 47
         'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
46 48
 
47 49
     # language cookie
48
-    params['cookies']['_EDGE_S'] = 'mkt=' + params['language'].lower() + '&F=1'
50
+    region = get_region_code(params['language'], lang_list=supported_languages)
51
+    params['cookies']['_EDGE_S'] = 'mkt=' + region + '&F=1'
49 52
 
50 53
     # query and paging
51 54
     params['url'] = search_url.format(query=urlencode({'q': query}),

+ 1
- 1
searx/engines/duckduckgo.py Näytä tiedosto

@@ -134,4 +134,4 @@ def _fetch_supported_languages(resp):
134 134
     regions_json = loads(response_page)
135 135
     supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())
136 136
 
137
-    return supported_languages
137
+    return list(supported_languages)

+ 1
- 1
searx/engines/swisscows.py Näytä tiedosto

@@ -118,7 +118,7 @@ def _fetch_supported_languages(resp):
118 118
     dom = fromstring(resp.text)
119 119
     options = dom.xpath('//div[@id="regions-popup"]//ul/li/a')
120 120
     for option in options:
121
-        code = option.xpath('./@data-val')[0]
121
+        code = option.xpath('./@data-search-language')[0]
122 122
         if code.startswith('nb-'):
123 123
             code = code.replace('nb', 'no', 1)
124 124
         supported_languages.append(code)

+ 8
- 10
searx/languages.py Näytä tiedosto

@@ -5,6 +5,11 @@
5 5
 language_codes = (
6 6
     (u"ar-SA", u"العربية", u"", u"Arabic"),
7 7
     (u"bg-BG", u"Български", u"", u"Bulgarian"),
8
+    (u"ca", u"Català", u"", u"Catalan"),
9
+    (u"ca-AD", u"Català", u"Andorra", u"Catalan"),
10
+    (u"ca-CT", u"Català", u"", u"Catalan"),
11
+    (u"ca-ES", u"Català", u"Espanya", u"Catalan"),
12
+    (u"ca-FR", u"Català", u"França", u"Catalan"),
8 13
     (u"cs-CZ", u"Čeština", u"", u"Czech"),
9 14
     (u"da-DK", u"Dansk", u"", u"Danish"),
10 15
     (u"de", u"Deutsch", u"", u"German"),
@@ -15,9 +20,7 @@ language_codes = (
15 20
     (u"en", u"English", u"", u"English"),
16 21
     (u"en-AU", u"English", u"Australia", u"English"),
17 22
     (u"en-CA", u"English", u"Canada", u"English"),
18
-    (u"en-CY", u"English", u"Cyprus", u"English"),
19 23
     (u"en-GB", u"English", u"United Kingdom", u"English"),
20
-    (u"en-GD", u"English", u"Grenada", u"English"),
21 24
     (u"en-ID", u"English", u"Indonesia", u"English"),
22 25
     (u"en-IE", u"English", u"Ireland", u"English"),
23 26
     (u"en-IN", u"English", u"India", u"English"),
@@ -28,6 +31,7 @@ language_codes = (
28 31
     (u"en-US", u"English", u"United States", u"English"),
29 32
     (u"en-ZA", u"English", u"South Africa", u"English"),
30 33
     (u"es", u"Español", u"", u"Spanish"),
34
+    (u"es-AD", u"Español", u"Andorra", u"Spanish"),
31 35
     (u"es-AR", u"Español", u"Argentina", u"Spanish"),
32 36
     (u"es-CL", u"Español", u"Chile", u"Spanish"),
33 37
     (u"es-CO", u"Español", u"Colombia", u"Spanish"),
@@ -38,38 +42,32 @@ language_codes = (
38 42
     (u"et-EE", u"Eesti", u"", u"Estonian"),
39 43
     (u"fi-FI", u"Suomi", u"", u"Finnish"),
40 44
     (u"fr", u"Français", u"", u"French"),
45
+    (u"fr-AD", u"Français", u"Andorre", u"French"),
41 46
     (u"fr-BE", u"Français", u"Belgique", u"French"),
42 47
     (u"fr-CA", u"Français", u"Canada", u"French"),
43 48
     (u"fr-CH", u"Français", u"Suisse", u"French"),
44 49
     (u"fr-FR", u"Français", u"France", u"French"),
45 50
     (u"he-IL", u"עברית", u"", u"Hebrew"),
46
-    (u"hr-HR", u"Hrvatski", u"", u"Croatian"),
47 51
     (u"hu-HU", u"Magyar", u"", u"Hungarian"),
48
-    (u"id-ID", u"Bahasa Indonesia", u"", u"Indonesian"),
49 52
     (u"it", u"Italiano", u"", u"Italian"),
50 53
     (u"it-CH", u"Italiano", u"Svizzera", u"Italian"),
51 54
     (u"it-IT", u"Italiano", u"Italia", u"Italian"),
52 55
     (u"ja-JP", u"日本語", u"", u"Japanese"),
53 56
     (u"ko-KR", u"한국어", u"", u"Korean"),
54
-    (u"lt-LT", u"Lietuvių", u"", u"Lithuanian"),
55
-    (u"lv-LV", u"Latviešu", u"", u"Latvian"),
56
-    (u"ms-MY", u"Bahasa Melayu", u"", u"Malay"),
57 57
     (u"nl", u"Nederlands", u"", u"Dutch"),
58 58
     (u"nl-BE", u"Nederlands", u"België", u"Dutch"),
59 59
     (u"nl-NL", u"Nederlands", u"Nederland", u"Dutch"),
60 60
     (u"no-NO", u"Norsk", u"", u"Norwegian"),
61 61
     (u"pl-PL", u"Polski", u"", u"Polish"),
62 62
     (u"pt", u"Português", u"", u"Portuguese"),
63
+    (u"pt-AD", u"Português", u"Andorra", u"Portuguese"),
63 64
     (u"pt-BR", u"Português", u"Brasil", u"Portuguese"),
64 65
     (u"pt-PT", u"Português", u"Portugal", u"Portuguese"),
65 66
     (u"ro-RO", u"Română", u"", u"Romanian"),
66 67
     (u"ru-RU", u"Русский", u"", u"Russian"),
67
-    (u"sk-SK", u"Slovenčina", u"", u"Slovak"),
68
-    (u"sl", u"Slovenščina", u"", u"Slovenian"),
69 68
     (u"sv-SE", u"Svenska", u"", u"Swedish"),
70 69
     (u"th-TH", u"ไทย", u"", u"Thai"),
71 70
     (u"tr-TR", u"Türkçe", u"", u"Turkish"),
72
-    (u"vi-VN", u"Tiếng Việt", u"", u"Vietnamese"),
73 71
     (u"zh", u"中文", u"", u"Chinese"),
74 72
     (u"zh-CN", u"中文", u"中国", u"Chinese"),
75 73
     (u"zh-HK", u"中文", u"香港", u"Chinese"),

+ 38
- 4
tests/unit/engines/test_bing_images.py Näytä tiedosto

@@ -8,10 +8,12 @@ from searx.testing import SearxTestCase
8 8
 class TestBingImagesEngine(SearxTestCase):
9 9
 
10 10
     def test_request(self):
11
+        bing_images.supported_languages = ['fr-FR', 'en-US']
12
+
11 13
         query = 'test_query'
12 14
         dicto = defaultdict(dict)
13 15
         dicto['pageno'] = 1
14
-        dicto['language'] = 'fr_FR'
16
+        dicto['language'] = 'fr-FR'
15 17
         dicto['safesearch'] = 1
16 18
         dicto['time_range'] = ''
17 19
         params = bing_images.request(query, dicto)
@@ -19,12 +21,19 @@ class TestBingImagesEngine(SearxTestCase):
19 21
         self.assertTrue(query in params['url'])
20 22
         self.assertTrue('bing.com' in params['url'])
21 23
         self.assertTrue('SRCHHPGUSR' in params['cookies'])
22
-        self.assertTrue('fr' in params['cookies']['SRCHHPGUSR'])
24
+        self.assertTrue('DEMOTE' in params['cookies']['SRCHHPGUSR'])
25
+        self.assertTrue('_EDGE_S' in params['cookies'])
26
+        self.assertTrue('fr-fr' in params['cookies']['_EDGE_S'])
27
+
28
+        dicto['language'] = 'fr'
29
+        params = bing_images.request(query, dicto)
30
+        self.assertTrue('_EDGE_S' in params['cookies'])
31
+        self.assertTrue('fr-fr' in params['cookies']['_EDGE_S'])
23 32
 
24 33
         dicto['language'] = 'all'
25 34
         params = bing_images.request(query, dicto)
26
-        self.assertIn('SRCHHPGUSR', params['cookies'])
27
-        self.assertIn('en', params['cookies']['SRCHHPGUSR'])
35
+        self.assertTrue('_EDGE_S' in params['cookies'])
36
+        self.assertTrue('en-us' in params['cookies']['_EDGE_S'])
28 37
 
29 38
     def test_response(self):
30 39
         self.assertRaises(AttributeError, bing_images.response, None)
@@ -82,3 +91,28 @@ class TestBingImagesEngine(SearxTestCase):
82 91
         self.assertEqual(results[0]['content'], '')
83 92
         self.assertEqual(results[0]['thumbnail_src'], 'thumb_url')
84 93
         self.assertEqual(results[0]['img_src'], 'img_url')
94
+
95
+    def test_fetch_supported_languages(self):
96
+        html = """
97
+        <div>
98
+            <div id="region-section-content">
99
+                <ul class="b_vList">
100
+                    <li>
101
+                        <a href="https://bing...&setmkt=de-DE&s...">Germany</a>
102
+                        <a href="https://bing...&setmkt=nb-NO&s...">Norway</a>
103
+                    </li>
104
+                </ul>
105
+                <ul class="b_vList">
106
+                    <li>
107
+                        <a href="https://bing...&setmkt=es-AR&s...">Argentina</a>
108
+                    </li>
109
+                </ul>
110
+            </div>
111
+        </div>
112
+        """
113
+        response = mock.Mock(text=html)
114
+        languages = list(bing_images._fetch_supported_languages(response))
115
+        self.assertEqual(len(languages), 3)
116
+        self.assertIn('de-DE', languages)
117
+        self.assertIn('no-NO', languages)
118
+        self.assertIn('es-AR', languages)

+ 2
- 0
tests/unit/engines/test_bing_videos.py Näytä tiedosto

@@ -8,6 +8,8 @@ from searx.testing import SearxTestCase
8 8
 class TestBingVideosEngine(SearxTestCase):
9 9
 
10 10
     def test_request(self):
11
+        bing_videos.supported_languages = ['fr-FR', 'en-US']
12
+
11 13
         query = 'test_query'
12 14
         dicto = defaultdict(dict)
13 15
         dicto['pageno'] = 1

+ 3
- 3
tests/unit/engines/test_swisscows.py Näytä tiedosto

@@ -139,9 +139,9 @@ class TestSwisscowsEngine(SearxTestCase):
139 139
             <div id="regions-popup">
140 140
                 <div>
141 141
                     <ul>
142
-                        <li><a data-val="browser"></a></li>
143
-                        <li><a data-val="de-CH"></a></li>
144
-                        <li><a data-val="fr-CH"></a></li>
142
+                        <li><a data-search-language="browser"></a></li>
143
+                        <li><a data-search-language="de-CH"></a></li>
144
+                        <li><a data-search-language="fr-CH"></a></li>
145 145
                     </ul>
146 146
                 </div>
147 147
             </div>

+ 8
- 8
utils/fetch_languages.py Näytä tiedosto

@@ -8,13 +8,13 @@
8 8
 # are written in current directory to avoid overwriting in case something goes wrong.
9 9
 
10 10
 from requests import get
11
-from urllib import urlencode
12 11
 from lxml.html import fromstring
13
-from json import loads, dumps
12
+from json import loads, dump
14 13
 import io
15 14
 from sys import path
16 15
 path.append('../searx')  # noqa
17 16
 from searx import settings
17
+from searx.url_utils import urlencode
18 18
 from searx.engines import initialize_engines, engines
19 19
 
20 20
 # Geonames API for country names.
@@ -70,7 +70,7 @@ def get_country_name(locale):
70 70
     json = loads(response.text)
71 71
     content = json.get('geonames', None)
72 72
     if content is None or len(content) != 1:
73
-        print "No country name found for " + locale[0] + "-" + locale[1]
73
+        print("No country name found for " + locale[0] + "-" + locale[1])
74 74
         return ''
75 75
 
76 76
     return content[0].get('countryName', '')
@@ -84,11 +84,11 @@ def fetch_supported_languages():
84 84
             try:
85 85
                 engines_languages[engine_name] = engines[engine_name].fetch_supported_languages()
86 86
             except Exception as e:
87
-                print e
87
+                print(e)
88 88
 
89 89
     # write json file
90 90
     with io.open(engines_languages_file, "w", encoding="utf-8") as f:
91
-        f.write(unicode(dumps(engines_languages, ensure_ascii=False, encoding="utf-8")))
91
+        dump(engines_languages, f, ensure_ascii=False)
92 92
 
93 93
 
94 94
 # Join all language lists.
@@ -97,7 +97,7 @@ def join_language_lists():
97 97
     global languages
98 98
     # include wikipedia first for more accurate language names
99 99
     languages = {code: lang for code, lang
100
-                 in engines_languages['wikipedia'].iteritems()
100
+                 in engines_languages['wikipedia'].items()
101 101
                  if valid_code(code)}
102 102
 
103 103
     for engine_name in engines_languages:
@@ -121,7 +121,7 @@ def join_language_lists():
121 121
     # filter list to include only languages supported by most engines
122 122
     min_supported_engines = int(0.70 * len(engines_languages))
123 123
     languages = {code: lang for code, lang
124
-                 in languages.iteritems()
124
+                 in languages.items()
125 125
                  if len(lang.get('counter', [])) >= min_supported_engines or
126 126
                  len(languages.get(code.split('-')[0], {}).get('counter', [])) >= min_supported_engines}
127 127
 
@@ -165,7 +165,7 @@ def filter_single_country_languages():
165 165
 
166 166
 # Write languages.py.
167 167
 def write_languages_file():
168
-    new_file = open(languages_file, 'w')
168
+    new_file = open(languages_file, 'wb')
169 169
     file_content = '# -*- coding: utf-8 -*-\n'\
170 170
                    + '# list of language codes\n'\
171 171
                    + '# this file is generated automatically by utils/update_search_languages.py\n'\