2020-05-24 23:03:11 +03:00
|
|
|
from lxml import etree
|
2020-05-12 09:45:56 +03:00
|
|
|
import random
|
2020-06-02 21:54:47 +03:00
|
|
|
import requests
|
|
|
|
from requests import Response
|
2020-04-24 05:59:43 +03:00
|
|
|
import urllib.parse as urlparse
|
|
|
|
|
2020-05-24 23:03:11 +03:00
|
|
|
# Core Google search URLs
|
2020-04-24 05:59:43 +03:00
|
|
|
SEARCH_URL = 'https://www.google.com/search?gbv=1&q='
|
2020-05-24 23:03:11 +03:00
|
|
|
AUTOCOMPLETE_URL = 'https://suggestqueries.google.com/complete/search?client=toolbar&'
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
MOBILE_UA = '{}/5.0 (Android 0; Mobile; rv:54.0) Gecko/54.0 {}/59.0'
|
|
|
|
DESKTOP_UA = '{}/5.0 (X11; {} x86_64; rv:75.0) Gecko/20100101 {}/75.0'
|
|
|
|
|
2020-04-29 03:19:34 +03:00
|
|
|
# Valid query params
|
2020-04-30 03:53:58 +03:00
|
|
|
VALID_PARAMS = ['tbs', 'tbm', 'start', 'near']
|
2020-04-29 03:19:34 +03:00
|
|
|
|
2020-04-24 05:59:43 +03:00
|
|
|
|
2020-06-02 21:54:47 +03:00
|
|
|
def gen_user_agent(is_mobile):
|
2020-05-12 09:45:56 +03:00
|
|
|
mozilla = random.choice(['Moo', 'Woah', 'Bro', 'Slow']) + 'zilla'
|
|
|
|
firefox = random.choice(['Choir', 'Squier', 'Higher', 'Wire']) + 'fox'
|
|
|
|
linux = random.choice(['Win', 'Sin', 'Gin', 'Fin', 'Kin']) + 'ux'
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
if is_mobile:
|
|
|
|
return MOBILE_UA.format(mozilla, firefox)
|
|
|
|
else:
|
|
|
|
return DESKTOP_UA.format(mozilla, linux, firefox)
|
|
|
|
|
|
|
|
|
2020-05-23 23:27:23 +03:00
|
|
|
def gen_query(query, args, config, near_city=None):
|
2020-04-30 03:53:58 +03:00
|
|
|
param_dict = {key: '' for key in VALID_PARAMS}
|
2020-04-24 05:59:43 +03:00
|
|
|
# Use :past(hour/day/week/month/year) if available
|
|
|
|
# example search "new restaurants :past month"
|
2020-04-29 03:59:33 +03:00
|
|
|
if ':past' in query:
|
|
|
|
time_range = str.strip(query.split(':past', 1)[-1])
|
2020-04-30 03:53:58 +03:00
|
|
|
param_dict['tbs'] = '&tbs=qdr:' + str.lower(time_range[0])
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
# Ensure search query is parsable
|
2020-04-29 03:59:33 +03:00
|
|
|
query = urlparse.quote(query)
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
# Pass along type of results (news, images, books, etc)
|
|
|
|
if 'tbm' in args:
|
2020-04-30 03:53:58 +03:00
|
|
|
param_dict['tbm'] = '&tbm=' + args.get('tbm')
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
# Get results page start value (10 per page, ie page 2 start val = 20)
|
|
|
|
if 'start' in args:
|
2020-04-30 03:53:58 +03:00
|
|
|
param_dict['start'] = '&start=' + args.get('start')
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
# Search for results near a particular city, if available
|
2020-05-23 23:27:23 +03:00
|
|
|
if near_city:
|
2020-04-30 03:53:58 +03:00
|
|
|
param_dict['near'] = '&near=' + urlparse.quote(near_city)
|
2020-04-24 05:59:43 +03:00
|
|
|
|
2020-05-13 02:15:53 +03:00
|
|
|
# Set language for results (lr) and interface (hl)
|
2020-05-23 23:27:23 +03:00
|
|
|
param_dict['lr'] = '&lr=' + config.lang + '&hl=' + config.lang.replace('lang_', '')
|
|
|
|
param_dict['cr'] = ('&cr=' + config.ctry) if config.ctry else ''
|
|
|
|
param_dict['safe'] = '&safe=' + ('active' if config.safe else 'off')
|
2020-05-13 02:15:53 +03:00
|
|
|
|
2020-04-30 03:53:58 +03:00
|
|
|
for val in param_dict.values():
|
2020-04-29 03:59:33 +03:00
|
|
|
if not val or val is None:
|
|
|
|
continue
|
|
|
|
query += val
|
|
|
|
|
|
|
|
return query
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
|
|
|
|
class Request:
|
2020-05-13 02:15:53 +03:00
|
|
|
def __init__(self, normal_ua, language='lang_en'):
|
|
|
|
self.language = language
|
2020-05-24 23:03:11 +03:00
|
|
|
self.mobile = 'Android' in normal_ua or 'iPhone' in normal_ua
|
2020-06-02 21:54:47 +03:00
|
|
|
self.modified_user_agent = gen_user_agent(self.mobile)
|
2020-04-24 05:59:43 +03:00
|
|
|
|
|
|
|
def __getitem__(self, name):
|
|
|
|
return getattr(self, name)
|
|
|
|
|
2020-05-24 23:03:11 +03:00
|
|
|
def autocomplete(self, query):
|
|
|
|
ac_query = dict(hl=self.language, q=query)
|
2020-06-02 21:54:47 +03:00
|
|
|
response = self.send(base_url=AUTOCOMPLETE_URL, query=urlparse.urlencode(ac_query)).text
|
2020-05-24 23:03:11 +03:00
|
|
|
|
|
|
|
if response:
|
|
|
|
dom = etree.fromstring(response)
|
|
|
|
return dom.xpath('//suggestion/@data')
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
2020-06-02 21:54:47 +03:00
|
|
|
def send(self, base_url=SEARCH_URL, query='') -> Response:
|
|
|
|
headers = {
|
|
|
|
'User-Agent': self.modified_user_agent
|
|
|
|
}
|
|
|
|
|
|
|
|
return requests.get(base_url + query, headers=headers)
|