on master help sign in
Add Nyaa.se search engine
commite5677ae6b6b23c943e64d8e2abcb64c13c0e8bbf authorKirill Isakov <ukwt@ya.ru> dateFri 25 Mar 2016, 00:24 (11 years ago) parentd748b84
3 files changed+185
searx/engines/nyaa.py+115 searx/settings.yml+4 tests/unit/engines/test_nyaa.py+66
searx/engines/nyaa.pynew file+115
@@ -0,0 +1,115 @@
1+"""
2+ Nyaa.se (Anime Bittorrent tracker)
3+
4+ @website http://www.nyaa.se/
5+ @provide-api no
6+ @using-api no
7+ @results HTML
8+ @stable no (HTML can change)
9+ @parse url, title, content, seed, leech, torrentfile
10+"""
11+
12+from cgi import escape
13+from urllib import urlencode
14+from lxml import html
15+from searx.engines.xpath import extract_text
16+
17+# engine dependent config
18+categories = ['files', 'images', 'videos', 'music']
19+paging = True
20+
21+# search-url
22+base_url = 'http://www.nyaa.se/'
23+search_url = base_url + '?page=search&{query}&offset={offset}'
24+
25+# xpath queries
26+xpath_results = '//table[@class="tlist"]//tr[contains(@class, "tlistrow")]'
27+xpath_category = './/td[@class="tlisticon"]/a'
28+xpath_title = './/td[@class="tlistname"]/a'
29+xpath_torrent_file = './/td[@class="tlistdownload"]/a'
30+xpath_filesize = './/td[@class="tlistsize"]/text()'
31+xpath_seeds = './/td[@class="tlistsn"]/text()'
32+xpath_leeches = './/td[@class="tlistln"]/text()'
33+xpath_downloads = './/td[@class="tlistdn"]/text()'
34+
35+
36+# convert a variable to integer or return 0 if it's not a number
37+def int_or_zero(num):
38+ if isinstance(num, list):
39+ if len(num) < 1:
40+ return 0
41+ num = num[0]
42+ if num.isdigit():
43+ return int(num)
44+ return 0
45+
46+
47+# do search-request
48+def request(query, params):
49+ query = urlencode({'term': query})
50+ params['url'] = search_url.format(query=query, offset=params['pageno'])
51+ return params
52+
53+
54+# get response from search-request
55+def response(resp):
56+ results = []
57+
58+ dom = html.fromstring(resp.text)
59+
60+ for result in dom.xpath(xpath_results):
61+ # category in which our torrent belongs
62+ category = result.xpath(xpath_category)[0].attrib.get('title')
63+
64+ # torrent title
65+ page_a = result.xpath(xpath_title)[0]
66+ title = escape(extract_text(page_a))
67+
68+ # link to the page
69+ href = page_a.attrib.get('href')
70+
71+ # link to the torrent file
72+ torrent_link = result.xpath(xpath_torrent_file)[0].attrib.get('href')
73+
74+ # torrent size
75+ try:
76+ file_size, suffix = result.xpath(xpath_filesize)[0].split(' ')
77+
78+ # convert torrent size to bytes.
79+ # if there is no correct index in this dictionary,
80+ # the try block fails as it should
81+ multiplier = {
82+ 'KIB': 1024,
83+ 'MIB': 1024 ** 2,
84+ 'GIB': 1024 ** 3,
85+ 'TIB': 1024 ** 4
86+ }[suffix.upper()]
87+
88+ file_size = int(float(file_size) * multiplier)
89+ except Exception as e:
90+ file_size = None
91+
92+ # seed count
93+ seed = int_or_zero(result.xpath(xpath_seeds))
94+
95+ # leech count
96+ leech = int_or_zero(result.xpath(xpath_leeches))
97+
98+ # torrent downloads count
99+ downloads = int_or_zero(result.xpath(xpath_downloads))
100+
101+ # content string contains all information not included into template
102+ content = 'Category: "{category}". Downloaded {downloads} times.'
103+ content = content.format(category=category, downloads=downloads)
104+ content = escape(content)
105+
106+ results.append({'url': href,
107+ 'title': title,
108+ 'content': content,
109+ 'seed': seed,
110+ 'leech': leech,
111+ 'filesize': file_size,
112+ 'torrentfile': torrent_link,
113+ 'template': 'torrent.html'})
114+
115+ return results
searx/settings.yml+4
@@ -175,6 +175,10 @@ engines:
175175 engine : mixcloud
176176 shortcut : mc
177177
178+ - name : nyaa
179+ engine : nyaa
180+ shortcut : nt
181+
178182 - name : openstreetmap
179183 engine : openstreetmap
180184 shortcut : osm
tests/unit/engines/test_nyaa.pynew file+66
@@ -0,0 +1,66 @@
1+from collections import defaultdict
2+import mock
3+from searx.engines import nyaa
4+from searx.testing import SearxTestCase
5+
6+
7+class TestNyaaEngine(SearxTestCase):
8+
9+ def test_request(self):
10+ query = 'test_query'
11+ dic = defaultdict(dict)
12+ dic['pageno'] = 1
13+ params = nyaa.request(query, dic)
14+ self.assertTrue('url' in params)
15+ self.assertTrue(query in params['url'])
16+ self.assertTrue('nyaa.se' in params['url'])
17+
18+ def test_response(self):
19+ resp = mock.Mock(text='<html></html>')
20+ self.assertEqual(nyaa.response(resp), [])
21+
22+ html = """
23+ <table class="tlist">
24+ <tbody>
25+ <tr class="trusted tlistrow">
26+ <td class="tlisticon">
27+ <a href="//www.nyaa.se" title="English-translated Anime">
28+ <img src="//files.nyaa.se" alt="English-translated Anime">
29+ </a>
30+ </td>
31+ <td class="tlistname">
32+ <a href="//www.nyaa.se/?page3">
33+ Sample torrent title
34+ </a>
35+ </td>
36+ <td class="tlistdownload">
37+ <a href="//www.nyaa.se/?page_dl" title="Download">
38+ <img src="//files.nyaa.se/www-dl.png" alt="DL">
39+ </a>
40+ </td>
41+ <td class="tlistsize">10 MiB</td>
42+ <td class="tlistsn">1</td>
43+ <td class="tlistln">3</td>
44+ <td class="tlistdn">666</td>
45+ <td class="tlistmn">0</td>
46+ </tr>
47+ </tbody>
48+ </table>
49+ """
50+
51+ resp = mock.Mock(text=html)
52+ results = nyaa.response(resp)
53+
54+ self.assertEqual(type(results), list)
55+ self.assertEqual(len(results), 1)
56+
57+ r = results[0]
58+ self.assertTrue(r['url'].find('www.nyaa.se/?page3') >= 0)
59+ self.assertTrue(r['torrentfile'].find('www.nyaa.se/?page_dl') >= 0)
60+ self.assertTrue(r['content'].find('English-translated Anime') >= 0)
61+ self.assertTrue(r['content'].find('Downloaded 666 times.') >= 0)
62+
63+ self.assertEqual(r['title'], 'Sample torrent title')
64+ self.assertEqual(r['seed'], 1)
65+ self.assertEqual(r['leech'], 3)
66+ self.assertEqual(r['filesize'], 10 * 1024 * 1024)