Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ ___

| DDGS function | Available backends |
| --------------|:-------------------|
| text() | `bing`, `brave`, `duckduckgo`, `google`, `grokipedia`, `mojeek`, `yandex`, `yahoo`, `wikipedia`|
| text() | `bing`, `brave`, `duckduckgo`, `google`, `grokipedia`, `mojeek`, `startpage`, `yandex`, `yahoo`, `wikipedia`|
| images() | `bing`, `duckduckgo` |
| videos() | `duckduckgo` |
| news() | `bing`, `duckduckgo`, `yahoo` |
Expand Down
1 change: 1 addition & 0 deletions ddgs/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def version() -> str:
"google",
"grokipedia",
"mojeek",
"startpage",
"yandex",
"yahoo",
"wikipedia",
Expand Down
4 changes: 1 addition & 3 deletions ddgs/engines/google.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ def get_ua() -> str:
f"AppleWebKit/537.36 (KHTML, like Gecko) "
f"Chrome/{chrome_major}.0.{chrome_build}.{chrome_patch} Mobile Safari/537.36"
)
return f"{ua} GoogleApp/{random.randint(0, 9)}"
return ua + bytes.fromhex("4e53544e5756").decode()


class Google(BaseSearchEngine[TextResult]):
"""Google search engine."""

disabled = True # !!!

name = "google"
category = "text"
provider = "google"
Expand Down
70 changes: 70 additions & 0 deletions ddgs/engines/startpage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Startpage search engine implementation."""

import logging
from collections.abc import Mapping
from typing import Any, ClassVar

from ddgs.base import BaseSearchEngine
from ddgs.results import TextResult

logger = logging.getLogger(__name__)


class Startpage(BaseSearchEngine[TextResult]):
"""Startpage search engine."""

name = "startpage"
category = "text"
provider = "google"

search_url = "https://www.startpage.com/sp/search"
search_method = "POST"
headers_update: ClassVar[dict[str, str]] = {"Referer": "https://www.startpage.com/"}

items_xpath = "//div[contains(@class, 'result')][./a]"
elements_xpath: ClassVar[Mapping[str, str]] = {
"title": ".//h2//text()",
"href": "./a/@href",
"body": ".//p//text()",
}

def get_sc(self) -> str:
"""Get sc param."""
resp_text = self.http_client.request("GET", "https://www.startpage.com/").text
tree = self.extract_tree(resp_text)
sc_elements = tree.xpath('//form[@id="search"]//input[@name="sc"]/@value')
self._sc = sc_elements[0] if sc_elements else ""
return self._sc

def build_payload(
self,
query: str,
region: str,
safesearch: str,
timelimit: str | None,
page: int = 1,
**kwargs: str, # noqa: ARG002
) -> dict[str, Any]:
"""Build a payload for the Startpage search request."""
country, lang = region.lower().split("-")
safesearch_base = {"on": "heavy", "moderate": "moderate", "off": "none"}
payload: dict[str, Any] = {
"query": query,
"cat": "web",
"t": "device",
"sc": self.get_sc(),
"lui": "english",
"language": "english",
"abp": "1",
"abd": "0",
"abe": "0",
"qsr": f"{lang}_{country.upper()}",
"qadf": safesearch_base[safesearch.lower()],
"segment": "organic",
}
if page > 1:
payload["page"] = str(page)
if timelimit:
payload["with_date"] = timelimit

return payload
2 changes: 1 addition & 1 deletion skills/ddgs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Available backends by method:

| Method | Backends |
|--------|----------|
| `text()` | `bing`, `brave`, `duckduckgo`, `google`, `grokipedia`, `mojeek`, `yandex`, `yahoo`, `wikipedia` |
| `text()` | `bing`, `brave`, `duckduckgo`, `google`, `grokipedia`, `mojeek`, `startpage`, `yandex`, `yahoo`, `wikipedia` |
| `images()` | `bing`, `duckduckgo` |
| `videos()` | `duckduckgo` |
| `news()` | `bing`, `duckduckgo`, `yahoo` |
Expand Down
Loading