From 0ae2f039b32789f392182c16d22b889b5ce4bca9 Mon Sep 17 00:00:00 2001 From: bhaveshAn Date: Mon, 30 Oct 2017 01:38:54 +0530 Subject: [PATCH] Added Mojeek support --- .flake8.ini | 1 + .hound.yml | 3 ++- .travis.yml | 6 +++--- README.md | 4 ++-- app/scrapers/__init__.py | 9 +++++++-- app/scrapers/ask.py | 3 +++ app/scrapers/baidu.py | 2 +- app/scrapers/bing.py | 2 +- app/scrapers/duckduckgo.py | 2 +- app/scrapers/generalized.py | 3 ++- app/scrapers/google.py | 2 +- app/scrapers/mojeek.py | 26 ++++++++++++++++++++++++++ app/scrapers/yahoo.py | 2 +- app/scrapers/yandex.py | 2 +- app/scrapers/youtube.py | 28 ++++++++++++++++++++++++++++ app/server.py | 8 +++----- app/static/images/mojeek_icon.png | Bin 0 -> 17490 bytes app/static/images/youtube_icon.png | Bin 0 -> 7858 bytes app/templates/index.html | 2 ++ 19 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 app/scrapers/mojeek.py create mode 100644 app/scrapers/youtube.py create mode 100644 app/static/images/mojeek_icon.png create mode 100644 app/static/images/youtube_icon.png diff --git a/.flake8.ini b/.flake8.ini index 8bea7d48..9525ff8c 100644 --- a/.flake8.ini +++ b/.flake8.ini @@ -1,5 +1,6 @@ [flake8] max-line-length = 150 +ignore = W191 exclude = .git, __pycache__ diff --git a/.hound.yml b/.hound.yml index 13f0d6a5..6f707b04 100644 --- a/.hound.yml +++ b/.hound.yml @@ -1,4 +1,5 @@ python: enabled: true config_file: .flake8.ini - \ No newline at end of file + +fail_on_violations: true diff --git a/.travis.yml b/.travis.yml index b2ccd13d..ec54c240 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python python: - - "2.7" - - "3.6" + - 2.7 + - 3.6 install: - pip install flake8 pytest - pip install -r requirements.txt @@ -10,4 +10,4 @@ before_script: script: - pytest --capture=sys after_success: - - coveralls + - codecov diff --git a/README.md b/README.md index a3afc349..942a07d8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Dependency Status](https://david-dm.org/fossasia/query-server.svg)](https://david-dm.org/ossasia/query-server) [![Join the chat at https://gitter.im/fossasia/query-server](https://badges.gitter.im/fossasia/query-server.svg)](https://gitter.im/fossasia/query-server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -> The query server can be used to search a keyword/phrase on a search engine (Google, Yahoo, Bing, Ask, DuckDuckGo, Yandex, Baidu and Exalead) and get the results as `json` or `xml`. The tool also stores the searched query string in a MongoDB database for analytical purposes. (The search engine scrapper is based on the scraper at [fossasia/searss](https://github.com/fossasia/searss).) +> The query server can be used to search a keyword/phrase on a search engine (Google, Yahoo, Bing, Ask, DuckDuckGo, Yandex, Baidu, Exalead, Quora and Youtube) and get the results as `json` or `xml`. The tool also stores the searched query string in a MongoDB database for analytical purposes. (The search engine scrapper is based on the scraper at [fossasia/searss](https://github.com/fossasia/searss).) [![Deploy to Docker Cloud](https://files.cloud.docker.com/images/deploy-to-dockercloud.svg)](https://cloud.docker.com/stack/deploy/?repo=https://github.com/fossasia/query-server) [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/fossasia/query-server) [![Deploy on Scalingo](https://cdn.scalingo.com/deploy/button.svg)](https://my.scalingo.com/deploy?source=https://github.com/fossasia/query-server#master) [![Deploy to Bluemix](https://bluemix.net/deploy/button.png)](https://bluemix.net/deploy?repository=https://github.com/fossasia/query-server&branch=master) @@ -23,7 +23,7 @@ The API(s) provided by query-server are as follows: ` GET /api/v1/search/?query=query&format=format ` -> *search-engine* : [`google`, `ask`, `bing`, `duckduckgo`, `yahoo`, `yandex`, `baidu`, `exalead`] +> *search-engine* : [`google`, `ask`, `bing`, `duckduckgo`, `yahoo`, `yandex`, `baidu`, `exalead`, `quora`, `youtube`] > *query* : query can be any string diff --git a/app/scrapers/__init__.py b/app/scrapers/__init__.py index 535e9042..6c4cfbca 100644 --- a/app/scrapers/__init__.py +++ b/app/scrapers/__init__.py @@ -10,6 +10,9 @@ from baidu import Baidu from exalead import Exalead from quora import Quora +from youtube import Youtube +from mojeek import Mojeek + scrapers = { 'g': Google(), @@ -20,7 +23,9 @@ 'yd': Yandex(), 'u': Baidu(), 'e': Exalead(), - 'q': Quora() + 'q': Quora(), + 't': Youtube(), + 'm': Mojeek() } @@ -34,7 +39,7 @@ def small_test(): def feedgen(query, engine, count=10): - if engine == 'q': + if engine in ['q', 't']: urls = scrapers[engine].search_without_count(query) else: urls = scrapers[engine].search(query, count) diff --git a/app/scrapers/ask.py b/app/scrapers/ask.py index 731c2956..74c276aa 100644 --- a/app/scrapers/ask.py +++ b/app/scrapers/ask.py @@ -19,10 +19,13 @@ def parseResponse(self, soup): [[Tile1,url1], [Title2, url2],..] """ urls = [] + if soup.find('div', {'class': 'PartialSearchResults-noresults'}): + return None for div in soup.findAll('div', {'class': 'PartialSearchResults-item'}): title = div.div.a.text url = div.div.a['href'] p = div.find('p', {'class': 'PartialSearchResults-item-abstract'}) desc = p.text.replace('\n', '') urls.append({'title': title, 'link': url, 'desc': desc}) + print('Ask parsed: ' + str(urls)) return urls diff --git a/app/scrapers/baidu.py b/app/scrapers/baidu.py index 0c84d30a..5011cc19 100644 --- a/app/scrapers/baidu.py +++ b/app/scrapers/baidu.py @@ -22,6 +22,6 @@ def parseResponse(self, soup): url = div.h3.a['href'] urls.append({'title': title, 'link': url}) - print('parsed' + str(urls)) + print('Baidu parsed: ' + str(urls)) return urls diff --git a/app/scrapers/bing.py b/app/scrapers/bing.py index 4a439b3a..50be0c6c 100644 --- a/app/scrapers/bing.py +++ b/app/scrapers/bing.py @@ -25,6 +25,6 @@ def parseResponse(self, soup): 'desc': desc} urls.append(url_entry) - print('parsed' + str(urls)) + print('Bing parsed: ' + str(urls)) return urls diff --git a/app/scrapers/duckduckgo.py b/app/scrapers/duckduckgo.py index 89393f21..a4879c32 100644 --- a/app/scrapers/duckduckgo.py +++ b/app/scrapers/duckduckgo.py @@ -20,6 +20,6 @@ def parseResponse(self, soup): urls.append({'title': links.getText(), 'link': links.get('href')}) - print('parsed' + str(urls)) + print('DuckDuckGo parsed: ' + str(urls)) return urls diff --git a/app/scrapers/generalized.py b/app/scrapers/generalized.py index 2d1648d0..306375f9 100644 --- a/app/scrapers/generalized.py +++ b/app/scrapers/generalized.py @@ -46,7 +46,8 @@ def search(self, query, numResults): response = self.get_page(query, currentStart) soup = BeautifulSoup(response.text, 'html.parser') newResults = self.parseResponse(soup) - + if newResults is None: + break urls.extend(newResults) currentStart = self.nextStart(currentStart, newResults) return urls[: numResults] diff --git a/app/scrapers/google.py b/app/scrapers/google.py index c9969bb7..fcaa5d22 100644 --- a/app/scrapers/google.py +++ b/app/scrapers/google.py @@ -24,6 +24,6 @@ def parseResponse(self, soup): links = h3.find('a') urls.append({'title': links.getText(), 'link': links.get('href')}) - print('parsed' + str(urls)) + print('Google parsed: ' + str(urls)) return urls diff --git a/app/scrapers/mojeek.py b/app/scrapers/mojeek.py new file mode 100644 index 00000000..2caf7e4e --- /dev/null +++ b/app/scrapers/mojeek.py @@ -0,0 +1,26 @@ +from __future__ import print_function +from generalized import Scraper + + +class Mojeek(Scraper): + """Scraper class for Mojeek""" + + def __init__(self): + self.url = 'https://www.mojeek.co.uk/search' + self.defaultStart = 1 + self.startKey = 's' + + def parseResponse(self, soup): + """ Parse the response and return set of urls + Returns: urls (list) + [[Tile1,url1], [Title2, url2],..] + """ + urls = [] + for a in soup.findAll('a', {'class': 'ob'}): + title = a.getText() + url = a.get('href') + urls.append({'title': title, 'link': url}) + + print('Mojeek parsed: ' + str(urls)) + + return urls diff --git a/app/scrapers/yahoo.py b/app/scrapers/yahoo.py index 35d09bf6..65aa87c0 100644 --- a/app/scrapers/yahoo.py +++ b/app/scrapers/yahoo.py @@ -30,6 +30,6 @@ def parseResponse(self, soup): 'link': u }) - print('parsed' + str(urls)) + print('Yahoo parsed: ' + str(urls)) return urls diff --git a/app/scrapers/yandex.py b/app/scrapers/yandex.py index feea31f9..c232af9d 100644 --- a/app/scrapers/yandex.py +++ b/app/scrapers/yandex.py @@ -18,6 +18,6 @@ def parseResponse(self, soup): for a in soup.findAll('a', {'class': 'link link_theme_normal'}): urls.append({'title': a.getText(), 'link': a.get('href')}) - print('parsed' + str(urls)) + print('Yandex parsed: ' + str(urls)) return urls diff --git a/app/scrapers/youtube.py b/app/scrapers/youtube.py new file mode 100644 index 00000000..0397637e --- /dev/null +++ b/app/scrapers/youtube.py @@ -0,0 +1,28 @@ +from __future__ import print_function +from generalized import Scraper + + +class Youtube(Scraper): + """Scraper class for Youtube""" + + def __init__(self): + self.url = 'https://www.youtube.com/results' + self.queryKey = 'search_query' + + def parseResponse(self, soup): + """ Parse the response and return list of urls + Returns: urls (list) + [[Tile1,url1], [Title2, url2],..] + """ + urls = [] + for a in soup.findAll('a'): + if a.get('href').startswith('/watch?'): + link = 'https://www.youtube.com' + str(a.get('href')) + if not a.getText().startswith('\n\n'): + urls.append({'title': a.getText(), 'link': link}) + else: + continue + + print('Youtube parsed: ' + str(urls)) + + return urls diff --git a/app/server.py b/app/server.py index 24e367ae..75d9ace2 100644 --- a/app/server.py +++ b/app/server.py @@ -40,7 +40,8 @@ def search(search_engine): engine = search_engine if engine not in ('google', 'bing', 'duckduckgo', 'yahoo', 'ask', - 'yandex', 'ubaidu', 'exalead', 'quora'): + 'yandex', 'ubaidu', 'exalead', 'quora', 'tyoutube', + 'mojeek'): err = [404, 'Incorrect search engine', qformat] return bad_request(err) @@ -49,10 +50,7 @@ def search(search_engine): err = [400, 'Not Found - missing query', qformat] return bad_request(err) - if engine[0] == 'q': - result = feedgen(query, engine[0]) - else: - result = feedgen(query, engine[0], count) + result = feedgen(query, engine[0], count) if not result: err = [404, 'No response', qformat] return bad_request(err) diff --git a/app/static/images/mojeek_icon.png b/app/static/images/mojeek_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0f1f5c9192bbaae824ab6252544a9ac5178027b3 GIT binary patch literal 17490 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 GGywotSf5t_ literal 0 HcmV?d00001 diff --git a/app/static/images/youtube_icon.png b/app/static/images/youtube_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5924c8d653dbaaf8b603bf33f21435fd381681b4 GIT binary patch literal 7858 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 literal 0 HcmV?d00001 diff --git a/app/templates/index.html b/app/templates/index.html index 057961f5..0940caf5 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -64,6 +64,8 @@

query-server

+ +