summaryrefslogtreecommitdiff
path: root/searx/engines/yep.py
blob: e87a1bef286a9a196dd8b53638631370652fd577 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Yep (general, images, news)"""

import re

import typing as t

from urllib.parse import urlencode

from searx.result_types import EngineResults
from searx.utils import html_to_text, eval_xpath_getindex, extract_text

if t.TYPE_CHECKING:
    from searx.enginelib.traits import EngineTraits
    from searx.extended_types import SXNG_Response
    from searx.search.processors import OnlineParams

about = {
    "website": "https://yep.com/",
    "official_api_documentation": "https://docs.developer.yelp.com",
    "use_official_api": False,
    "require_api_key": False,
    "results": "JSON",
}
language_support = True

base_url = "https://api.yep.com"
web_base_url = "https://yep.com"

safesearch = True
safesearch_map = {0: "off", 1: "moderate", 2: "strict"}

enable_http2 = False

results_per_page = 20

_IMPORT_RE = re.compile(r"import\"(.*?)\";")
_LANGUAGE_RE = re.compile(r"\{english:\".*?\",code_string:\"(.*?)\",code:\".*?\"\}")


def request(query: str, params: "OnlineParams") -> None:
    args = {"query": query, "safeSearch": safesearch_map[params["safesearch"]], "limit": results_per_page}

    engine_language: str | None = traits.get_language(params["searxng_locale"])
    if engine_language:
        args["hl"] = engine_language

    params["url"] = f"{base_url}/search?{urlencode(args)}"
    params["headers"].update(
        {
            "Referer": f"{web_base_url}/",
            "Origin": web_base_url,
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-site",
        }
    )


def response(resp: "SXNG_Response") -> EngineResults:
    res = EngineResults()

    result: dict[str, str]
    for result in resp.json()[1]["results"]:
        res.add(
            res.types.MainResult(
                url=result["url"],
                title=result["title"],
                content=html_to_text(result["snippet"]),
            )
        )

    return res


def fetch_traits(engine_traits: "EngineTraits"):
    """Fetch :ref:`languages <yep languages>` and :ref:`regions <yep
    regions>` from Yep.

    The language options are very well hidden on Yep. To get it, we have to do the following:
    - Load the yep.com mainpage and extract the URL of the JavaScript app
    - Load the JavaScript source code and extract the URL of all imported modules from it
    - Load the imported modules to search for the right one that contains the languages
    """

    # pylint: disable=import-outside-toplevel, too-many-branches

    from lxml import html
    import babel

    from searx.locales import language_tag
    from searx.network import get  # see https://github.com/searxng/searxng/issues/762

    from searx.utils import gen_useragent

    headers = {
        "User-Agent": gen_useragent(),
        "Referer": f"{web_base_url}/",
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "same-origin",
    }

    resp = get(web_base_url, headers=headers, timeout=5)
    if not resp.ok:
        raise RuntimeError("Response from Yep languages is not OK.")

    doc = html.fromstring(resp.text)
    url = eval_xpath_getindex(doc, "//script[contains(@src, 'PageApp')]/@src", index=0)

    resp = get("https:" + extract_text(url), headers=headers, timeout=5)
    if not resp.ok:
        raise RuntimeError("Response from Yep languages is not OK.")

    language_codes = []
    for script_path in _IMPORT_RE.findall(resp.text):
        resp = get(f"{web_base_url}{script_path}", headers=headers, timeout=5)
        if not resp.ok:
            raise RuntimeError("Response from Yep languages is not OK.")

        for match in _LANGUAGE_RE.findall(resp.text):
            language_codes.append(match)

        if language_codes:
            break

    for language_code in language_codes:
        try:
            sxng_tag = language_tag(babel.Locale.parse(language_code, sep="-"))
        except babel.UnknownLocaleError:
            # silently ignore unknown languages
            continue
        # print("%-20s: %s <-- %s" % (extract_text(option), country_tag, sxng_tag))

        conflict = engine_traits.languages.get(sxng_tag)
        if conflict:
            if conflict != sxng_tag:
                print("CONFLICT: babel %s --> %s, %s" % (sxng_tag, conflict, language_code))
            continue
        engine_traits.languages[sxng_tag] = language_code