4get/scraper/brave.php

2291 lines
42 KiB
PHP

<?php
/*
$brave = new brave();
$handle = fopen("captcha.html", "r");
$html = fread($handle, filesize("captcha.html"));
fclose($handle);
$brave->bypasscaptcha($html, "yes", "ca");*/
class brave{
public function __construct(){
include "lib/fuckhtml.php";
$this->fuckhtml = new fuckhtml();
include "lib/nextpage.php";
$this->nextpage = new nextpage("brave");
}
public function getfilters($page){
switch($page){
case "web":
return [
"country" => [
"display" => "Country",
"option" => [
"all" => "All Regions",
"ar" => "Argentina",
"au" => "Australia",
"at" => "Austria",
"be" => "Belgium",
"br" => "Brazil",
"ca" => "Canada",
"cl" => "Chile",
"cn" => "China",
"dk" => "Denmark",
"fi" => "Finland",
"fr" => "France",
"de" => "Germany",
"hk" => "Hong Kong",
"in" => "India",
"id" => "Indonesia",
"it" => "Italy",
"jp" => "Japan",
"kr" => "Korea",
"my" => "Malaysia",
"mx" => "Mexico",
"nl" => "Netherlands",
"nz" => "New Zealand",
"no" => "Norway",
"pl" => "Poland",
"pt" => "Portugal",
"ph" => "Philippines",
"ru" => "Russia",
"sa" => "Saudi Arabia",
"za" => "South Africa",
"es" => "Spain",
"se" => "Sweden",
"ch" => "Switzerland",
"tw" => "Taiwan",
"tr" => "Turkey",
"gb" => "United Kingdom",
"us" => "United States"
]
],
"nsfw" => [
"display" => "NSFW",
"option" => [
"yes" => "Yes",
"maybe" => "Maybe",
"no" => "No"
]
],
"newer" => [
"display" => "Newer than",
"option" => "_DATE"
],
"older" => [
"display" => "Older than",
"option" => "_DATE"
]
];
break;
case "images":
case "videos":
case "news":
return [
"country" => [
"display" => "Country",
"option" => [
"all" => "All regions",
"ar" => "Argentina",
"au" => "Australia",
"at" => "Austria",
"be" => "Belgium",
"br" => "Brazil",
"ca" => "Canada",
"cl" => "Chile",
"cn" => "China",
"dk" => "Denmark",
"fi" => "Finland",
"fr" => "France",
"de" => "Germany",
"hk" => "Hong Kong",
"in" => "India",
"id" => "Indonesia",
"it" => "Italy",
"jp" => "Japan",
"kr" => "Korea",
"my" => "Malaysia",
"mx" => "Mexico",
"nl" => "Netherlands",
"nz" => "New Zealand",
"no" => "Norway",
"pl" => "Poland",
"pt" => "Portugal",
"ph" => "Philippines",
"ru" => "Russia",
"sa" => "Saudi Arabia",
"za" => "South Africa",
"es" => "Spain",
"se" => "Sweden",
"ch" => "Switzerland",
"tw" => "Taiwan",
"tr" => "Turkey",
"gb" => "United Kingdom",
"us" => "United States"
]
],
"nsfw" => [
"display" => "NSFW",
"option" => [
"yes" => "Yes",
"maybe" => "Maybe",
"no" => "No"
]
]
];
break;
}
}
private function get($url, $get = [], $nsfw, $country){
switch($nsfw){
case "yes": $nsfw = "off"; break;
case "maybe": $nsfw = "moderate"; break;
case "no": $nsfw = "strict"; break;
}
$headers = [
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/110.0",
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language: en-US,en;q=0.5",
"Accept-Encoding: gzip",
"Cookie: safesearch={$nsfw}; country={$country}; useLocation=0; summarizer=0",
"DNT: 1",
"Connection: keep-alive",
"Upgrade-Insecure-Requests: 1",
"Sec-Fetch-Dest: document",
"Sec-Fetch-Mode: navigate",
"Sec-Fetch-Site: none",
"Sec-Fetch-User: ?1"
];
if($country == "any"){
$country = "all";
}
$curlproc = curl_init();
if($get !== []){
$get = http_build_query($get);
$url .= "?" . $get;
}
curl_setopt($curlproc, CURLOPT_URL, $url);
curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
$data = curl_exec($curlproc);
if(curl_errno($curlproc)){
throw new Exception(curl_error($curlproc));
}
curl_close($curlproc);
return $data;
}
public function web($get){
if($get["npt"]){
// get next page data
$q = json_decode($this->nextpage->get($get["npt"], "web"), true);
$search = $q["q"];
$q["spellcheck"] = 0;
$nsfw = $q["nsfw"];
unset($q["nsfw"]);
$country = $q["country"];
unset($q["country"]);
}else{
// get _GET data instead
$search = $get["s"];
if(strlen($search) === 0){
throw new Exception("Search term is empty!");
}
if(strlen($search) > 2048){
throw new Exception("Search query is too long!");
}
$nsfw = $get["nsfw"];
$country = $get["country"];
$older = $get["older"];
$newer = $get["newer"];
$q = [
"q" => $search
];
/*
Pass older/newer filters to brave
*/
if($newer !== false){
$newer = date("Y-m-d", $newer);
if($older === false){
$older = date("Y-m-d", time());
}
}
if(
is_string($older) === false &&
$older !== false
){
$older = date("Y-m-d", $older);
if($newer === false){
$newer = "1970-01-02";
}
}
if($older !== false){
$q["tf"] = "{$newer}to{$older}";
}
}
/*
$handle = fopen("scraper/brave.html", "r");
$html = fread($handle, filesize("scraper/brave.html"));
fclose($handle);
*/
try{
$html =
$this->get(
"https://search.brave.com/search",
$q,
$nsfw,
$country
);
}catch(Exception $error){
throw new Exception("Could not fetch search page");
}
$out = [
"status" => "ok",
"spelling" => [
"type" => "no_correction",
"using" => null,
"correction" => null
],
"npt" => null,
"answer" => [],
"web" => [],
"image" => [],
"video" => [],
"news" => [],
"related" => []
];
// load html
$this->fuckhtml->load($html);
/*
Get next page "token"
*/
$nextpage =
$this->fuckhtml
->getElementsByClassName(
"btn ml-15",
"a"
);
if(count($nextpage) !== 0){
preg_match(
'/offset=([0-9]+)/',
$this->fuckhtml->getTextContent($nextpage[0]["attributes"]["href"]),
$nextpage
);
$q["offset"] = (int)$nextpage[1];
$q["nsfw"] = $nsfw;
$q["country"] = $country;
$out["npt"] =
$this->nextpage->store(
json_encode($q),
"web"
);
}
/*
Get discussions (and append them to web results)
*/
// they're loaded using javascript!!
$discussion =
$this->fuckhtml
->getElementById(
"js-discussions",
"script"
);
if(
$discussion &&
isset($discussion["attributes"]["data"])
){
$discussion =
json_decode(
$this->fuckhtml
->getTextContent(
$discussion["attributes"]["data"]
),
true
);
foreach($discussion["results"] as $result){
$data = [
"title" => $this->titledots($result["title"]),
"description" => null,
"url" => $result["url"],
"date" => null,
"type" => "web",
"thumb" => [
"url" => null,
"ratio" => null
],
"sublink" => [],
"table" => []
];
// description
$data["description"] =
$this->limitstrlen(
$this->limitwhitespace(
$this->titledots(
$this->fuckhtml->getTextContent(
$result["description"]
)
)
)
);
if($result["age"] != ""){
$data["date"] = strtotime($result["age"]);
}
// populate table
if($result["data"]["num_answers"] != ""){
$data["table"]["Replies"] = (int)$result["data"]["num_answers"];
}
if($result["data"]["score"] != ""){
$score = explode("|", $result["data"]["score"]);
if(count($score) === 2){
$score = ((int)$score[1]) . " (" . trim($score[0]) . ")";
}else{
$score = (int)$score[0];
}
$data["table"]["Votes"] = $score;
}
if($result["thumbnail"] != ""){
$data["thumb"]["url"] = $result["thumbnail"];
$data["thumb"]["ratio"] = "16:9";
}
$out["web"][] = $data;
}
}
/*
Get related searches
*/
$faq =
$this->fuckhtml
->getElementById("js-faq", "script");
if(
$faq &&
isset($faq["attributes"]["data"])
){
$faq =
json_decode(
$this->fuckhtml
->getTextContent(
$faq["attributes"]["data"]
),
true
);
foreach($faq["items"] as $related){
$out["related"][] = $related["question"];
}
}
/*
Get spelling autocorrect
*/
$altered =
$this->fuckhtml
->getElementById("altered-query", "div");
if($altered){
$this->fuckhtml->load($altered);
$altered =
$this->fuckhtml
->getElementsByTagName("a");
if(count($altered) === 2){
$out["spelling"] = [
"type" => "including",
"using" =>
$this->fuckhtml
->getTextContent($altered[0]),
"correction" =>
$this->fuckhtml
->getTextContent($altered[1])
];
}
$this->fuckhtml->load($html);
}
/*
Get web results
*/
$resulthtml =
$this->fuckhtml
->getElementById(
"results",
"div"
);
$this->fuckhtml->load($resulthtml);
$items = 0;
foreach(
$this->fuckhtml
->getElementsByClassName("snippet fdb")
as $result
){
$data = [
"title" => null,
"description" => null,
"url" => null,
"date" => null,
"type" => "web",
"thumb" => [
"url" => null,
"ratio" => null
],
"sublink" => [],
"table" => []
];
if(
isset($result["attributes"]["data-type"]) &&
$result["attributes"]["data-type"] == "ad"
){
// is an ad, skip
continue;
}
$this->fuckhtml->load($result);
/*
Get title
*/
$title =
$this->fuckhtml
->getElementsByClassName(
"snippet-title",
"span"
);
if(count($title) === 0){
// encountered AI summarizer
// or misspelling indicator @TODO
continue;
}
if(isset($title[0]["attributes"]["title"])){
$data["title"] =
$this->titledots(
$this->fuckhtml
->getTextContent(
$title[0]["attributes"]["title"]
)
);
}else{
$data["title"] =
$this->titledots(
$this->fuckhtml
->getTextContent(
$title[0]
)
);
}
/*
Get description
*/
$description =
$this->fuckhtml
->getElementsByClassName(
"snippet-description",
"p"
);
if(count($description) !== 0){
$data["description"] =
$this->titledots(
$this->fuckhtml
->getTextContent(
$description[0]
)
);
// also check for thumbnail in here
$img =
$this->fuckhtml
->getElementsByClassName(
"thumb",
"img"
);
if(count($img) !== 0){
$data["thumb"] = [
"url" => $this->unshiturl($img[0]["attributes"]["src"]),
"ratio" => "16:9"
];
}else{
// might be a video thumbnail wrapper?
$wrapper =
$this->fuckhtml
->getElementsByClassName(
"video-thumb",
"a"
);
if(count($wrapper) !== 0){
// we found a video
$this->fuckhtml->load($wrapper[0]);
$img =
$this->fuckhtml
->getElementsByTagName("img");
$data["thumb"] = [
"url" => $this->unshiturl($img[0]["attributes"]["src"]),
"ratio" => "16:9"
];
// get the video length, if its there
$duration =
$this->fuckhtml
->getElementsByClassName(
"duration",
"div"
);
if(count($duration) !== 0){
$data["table"]["Duration"] = $duration[0]["innerHTML"];
}
// reset html load
$this->fuckhtml->load($result);
}
}
}else{
// is a steam/shop listing
$description_alt =
$this->fuckhtml
->getElementsByClassName(
"text-sm",
"div"
);
if(count($description_alt) !== 0){
switch($description_alt[0]["attributes"]["class"]){
case "text-sm text-gray":
case "description text-sm":
$data["description"] =
$this->titledots(
$this->fuckhtml
->getTextContent(
$description_alt[0]
)
);
break;
}
// get table sublink
$sublink =
$this->fuckhtml
->getElementsByClassName(
"r-attr text-sm",
"div"
);
if(count($sublink) !== 0){
$this->tablesublink($sublink, $data);
}
// check for thumb element
$data["thumb"] = $this->getimagelinkfromstyle("thumb");
}else{
// ok... finally...
// maybe its the instant answer thingy
$answer =
$this->fuckhtml
->getElementsByClassName("answer");
if(count($answer) !== 0){
$data["description"] =
$this->titledots(
$this->fuckhtml
->getTextContent($answer[0])
);
}
}
}
// finally, fix brave's date format sucking balls
$data["description"] = explode(" - ", $data["description"], 2);
if(count($data["description"]) === 0){
// nothing to do
$data["description"] = $data["description"][0];
}else{
// attempt to parse
$time = strtotime($data["description"][0]);
if($time !== false){
// got response
$data["date"] = $time;
array_shift($data["description"]);
}
// merge back
$data["description"] =
implode(" - ", $data["description"]);
}
/*
Check content type
*/
$content_type =
$this->fuckhtml
->getElementsByClassName(
"content-type",
"span"
);
if(count($content_type) !== 0){
$data["type"] =
strtolower($this->fuckhtml->getTextContent($content_type[0]));
}
/*
Check subtext table thingy
*/
$table_items =
array_merge(
$this->fuckhtml
->getElementsByClassName(
"item-attributes",
"div"
),
$this->fuckhtml
->getElementsByClassName(
"r",
"div"
)
);
/*
DIV: item-attributes
*/
if(count($table_items) !== 0){
foreach($table_items as $table){
$this->fuckhtml->load($table);
$span =
$this->fuckhtml
->getElementsByClassName(
"text-sm",
"*"
);
foreach($span as $item){
$item =
explode(
":",
$this->fuckhtml->getTextContent(preg_replace('/\n/', " ", $item["innerHTML"])),
2
);
if(count($item) === 2){
$data["table"][trim($item[0])] = trim($this->limitwhitespace($item[1]));
}
}
}
$this->fuckhtml->load($result);
}
// get video sublinks
$table_items =
$this->fuckhtml
->getElementsByClassName(
"snippet-description published-time",
"p"
);
if(count($table_items) !== 0){
$table_items =
explode(
'<span class="mr-15"></span>',
$table_items[0]["innerHTML"],
2
);
if(count($table_items) === 2){
$item2 = [];
$item2[] = explode(":", $this->fuckhtml->getTextContent($table_items[0]));
if(trim($table_items[1]) != ""){
$item2[] = explode(":", $this->fuckhtml->getTextContent($table_items[1]));
}
foreach($item2 as $it){
$data["table"][trim($it[0])] = trim($it[1]);
}
}
}
/*
Get URL
*/
$data["url"] =
$this->fuckhtml->getTextContent(
$this->fuckhtml
->getElementsByTagName("a")
[0]
["attributes"]
["href"]
);
/*
Get sublinks
*/
$sublinks_elems =
$this->fuckhtml
->getElementsByClassName(
"snippet",
"div"
);
$sublinks = [];
foreach($sublinks_elems as $sublink){
$this->fuckhtml->load($sublink);
$a =
$this->fuckhtml
->getElementsByTagName("a")[0];
$title =
$this->fuckhtml
->getTextContent($a);
$url = $a["attributes"]["href"];
$description =
$this->titledots(
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByTagName("p")[0]
)
);
$sublinks[] = [
"title" => $title,
"date" => null,
"description" => $description,
"url" => $url
];
}
/*
Get smaller sublinks
*/
$sublinks_elems =
$this->fuckhtml
->getElementsByClassName(
"deep-link",
"a"
);
foreach($sublinks_elems as $sublink){
$sublinks[] = [
"title" => $this->fuckhtml->getTextContent($sublink),
"date" => null,
"description" => null,
"url" => $sublink["attributes"]["href"]
];
}
// append sublinks to $data !!
$data["sublink"] = $sublinks;
// append first result to start of $out["web"]
// other results are after
if($items === 0){
$out["web"] = [$data, ...$out["web"]];
}else{
$out["web"][] = $data;
}
$items++;
}
/*
Get news
*/
$this->fuckhtml->load($resulthtml);
$news_carousel = $this->fuckhtml->getElementById("news-carousel");
$this->fuckhtml->load($news_carousel);
if($news_carousel){
$a =
$this->fuckhtml
->getElementsByClassName(
"card fdb",
"a"
);
foreach($a as $news){
$this->fuckhtml->load($news);
$out["news"][] = [
"title" =>
$this->titledots(
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"title",
"div"
)[0]
)
),
"description" => null,
"date" =>
strtotime(
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"card-footer__timestamp",
"span"
)[0]
)
),
"thumb" => $this->getimagelinkfromstyle("img-bg"),
"url" => $this->fuckhtml->getTextContent($news["attributes"]["href"])
];
}
}
/*
Get videos
*/
$this->fuckhtml->load($resulthtml);
$news_carousel = $this->fuckhtml->getElementById("video-carousel");
$this->fuckhtml->load($news_carousel);
if($news_carousel){
$a =
$this->fuckhtml
->getElementsByClassName(
"card fdb",
"a"
);
foreach($a as $video){
$this->fuckhtml->load($video);
$date = null;
$date_o =
$this->fuckhtml
->getElementsByClassName(
"text-gray text-xs",
"span"
);
if(count($date_o) !== 0){
$date =
strtotime(
$this->fuckhtml
->getTextContent(
$date_o[0]
)
);
}
$out["video"][] = [
"title" =>
$this->titledots(
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"title",
"div"
)[0]
)
),
"description" => null,
"date" => $date,
"duration" => null,
"views" => null,
"thumb" => $this->getimagelinkfromstyle("img-bg"),
"url" => $this->fuckhtml->getTextContent($video["attributes"]["href"])
];
}
}
/*
Get DEFINITION snippet
*/
$this->fuckhtml->load($html);
$infobox = $this->fuckhtml->getElementById("rh-definitions", "div");
if($infobox !== false){
$answer = [
"title" => null,
"description" => [],
"url" => null,
"thumb" => null,
"table" => [],
"sublink" => []
];
$this->fuckhtml->load($infobox);
$answer["title"] =
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"header",
"h5"
)[0]
);
$sections =
$this->fuckhtml
->getElementsByTagName("section");
$i = -1;
foreach($sections as $section){
$this->fuckhtml->load($section);
$items =
$this->fuckhtml
->getElementsByTagName("*");
$li = 1;
$pronounce = false;
foreach($items as $item){
switch($item["tagName"]){
case "h6":
if(
isset($item["attributes"]["class"]) &&
$item["attributes"]["class"] == "h6 pronunciation"
){
if($pronounce){
break;
}
$answer["description"][] = [
"type" => "quote",
"value" =>
$this->fuckhtml
->getTextContent(
$item
)
];
$answer["description"][] =
[
"type" => "audio",
"url" => "https://search.brave.com/api/rhfetch?rhtype=definitions&word={$answer["title"]}&source=ahd-5"
];
$pronounce = true;
$i = $i + 2;
break;
}
$answer["description"][] = [
"type" => "title",
"value" =>
$this->fuckhtml
->getTextContent(
$item
)
];
$i++;
break;
case "li":
if(
$i !== -1 &&
$answer["description"][$i]["type"] == "text"
){
$answer["description"][$i]["value"] .=
"\n" . $li . ". " .
$this->fuckhtml
->getTextContent(
$item
);
}else{
$answer["description"][] = [
"type" => "text",
"value" =>
$li . ". " .
$this->fuckhtml
->getTextContent(
$item
)
];
$i++;
}
$li++;
break;
case "a":
$answer["url"] =
$this->fuckhtml
->getTextContent(
$item["attributes"]["href"]
);
break;
}
}
}
$out["answer"][] = $answer;
}
/*
Get instant answer
*/
$this->fuckhtml->load($html);
$infobox = $this->fuckhtml->getElementById("infobox", "div");
if($infobox !== false){
$answer = [
"title" => null,
"description" => [],
"url" => null,
"thumb" => null,
"table" => [],
"sublink" => []
];
$this->fuckhtml->load($infobox);
$div = $this->fuckhtml->getElementsByTagName("div");
/*
Get title + url
*/
$title =
$this->fuckhtml
->getElementsByClassName("infobox-title", "a");
if(count($title) !== 0){
$answer["title"] =
$this->fuckhtml
->getTextContent(
$title[0]
);
$answer["url"] =
$this->fuckhtml
->getTextContent(
$title[0]["attributes"]["href"]
);
}
/*
Get thumbnail
*/
$thumb = $this->getimagelinkfromstyle("thumb");
if($thumb["url"] !== null){
$answer["thumb"] = $thumb["url"];
}
/*
Get table
*/
$title =
$this->fuckhtml
->getElementsByClassName(
"infobox-attr-header",
"div"
);
$rowhtml = $infobox;
if(count($title) >= 2){
$rowhtml =
explode(
$title[1]["outerHTML"],
$infobox["innerHTML"],
2
)[0];
}
$this->fuckhtml->load($rowhtml);
$rows =
$this->fuckhtml
->getElementsByClassName("infobox-attr", "div");
foreach($rows as $row){
if(!isset($row["innerHTML"])){
continue;
}
$this->fuckhtml->load($row);
$span =
$this->fuckhtml
->getElementsByTagName("span");
if(count($span) === 2){
$answer["table"][
$this->fuckhtml->getTextContent($span[0])
] = str_replace("\n", ", ", $this->fuckhtml->getTextContent($span[1], true));
}
}
$this->fuckhtml->load($infobox);
/*
Parse stackoverflow answers
*/
$code =
$this->fuckhtml
->getElementById("codebox-answer", $div);
if($code){
// this might be standalone text with no paragraphs, check for that
$author =
$this->fuckhtml
->getElementById("author");
$desc_tmp =
str_replace(
$author["outerHTML"],
"",
$code["innerHTML"]
);
$this->fuckhtml->load($desc_tmp);
$code =
$this->fuckhtml
->getElementsByTagName("*");
if(count($code) === 0){
$answer["description"] =
[
[
"type" => "text",
"value" =>
$this->fuckhtml
->getTextContent(
$desc_tmp
)
],
[
"type" => "quote",
"value" =>
$this->fuckhtml
->getTextContent(
$author
)
]
];
}else{
$text = [];
$i = 0;
foreach($code as $snippet){
switch($snippet["tagName"]){
case "p":
$this->fuckhtml->load($snippet["innerHTML"]);
$codetags =
$this->fuckhtml
->getElementsByTagName("*");
$tmphtml = $snippet["innerHTML"];
foreach($codetags as $tag){
if(!isset($tag["outerHTML"])){
continue;
}
$tmphtml =
explode(
$tag["outerHTML"],
$tmphtml,
2
);
$value = $this->fuckhtml->getTextContent($tmphtml[0], false, false);
$this->appendtext($value, $text, $i);
$type = null;
switch($tag["tagName"]){
case "code": $type = "inline_code"; break;
case "em": $type = "italic"; break;
case "blockquote": $type = "quote"; break;
default: $type = "text";
}
if($type !== null){
$value = $this->fuckhtml->getTextContent($tag, false, true);
if(trim($value) != ""){
if(
$i !== 0 &&
$type == "title"
){
$text[$i - 1]["value"] = rtrim($text[$i - 1]["value"]);
}
$text[] = [
"type" => $type,
"value" => $value
];
$i++;
}
}
if(count($tmphtml) === 2){
$tmphtml = $tmphtml[1];
}else{
break;
}
}
if(is_array($tmphtml)){
$tmphtml = $tmphtml[0];
}
if(strlen($tmphtml) !== 0){
$value = $this->fuckhtml->getTextContent($tmphtml, false, false);
$this->appendtext($value, $text, $i);
}
break;
case "pre":
switch($text[$i - 1]["type"]){
case "text":
case "italic":
$text[$i - 1]["value"] = rtrim($text[$i - 1]["value"]);
break;
}
$text[] =
[
"type" => "code",
"value" =>
rtrim(
$this->fuckhtml
->getTextContent(
$snippet,
true,
false
)
)
];
$i++;
break;
case "ol":
$o = 0;
$this->fuckhtml->load($snippet);
$li =
$this->fuckhtml
->getElementsByTagName("li");
foreach($li as $elem){
$o++;
$this->appendtext(
$o . ". " .
$this->fuckhtml
->getTextContent(
$elem
),
$text,
$i
);
}
break;
}
}
if(
$i !== 0 &&
$text[$i - 1]["type"] == "text"
){
$text[$i - 1]["value"] = rtrim($text[$i - 1]["value"]);
}
if($author){
$text[] = [
"type" => "quote",
"value" => $this->fuckhtml->getTextContent($author)
];
}
$answer["description"] = $text;
}
}else{
/*
Get normal description
*/
$description =
$this->fuckhtml
->getElementsByClassName(
"mb-6",
"div"
);
if(count($description) !== 0){
$description =
[
[
"type" => "text",
"value" =>
$this->titledots(
preg_replace(
'/ Wikipedia$/',
"",
$this->fuckhtml
->getTextContent(
$description[0]
)
)
)
]
];
$ratings =
$this->fuckhtml
->getElementById("ratings");
if($ratings){
$this->fuckhtml->load($ratings);
$ratings =
$this->fuckhtml
->getElementsByClassName(
"flex-hcenter mb-10",
"div"
);
$description[] = [
"type" => "title",
"value" => "Ratings"
];
foreach($ratings as $rating){
$this->fuckhtml->load($rating);
$num =
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"r-num",
"div"
)[0]
);
$href =
$this->fuckhtml
->getElementsByClassName(
"mr-10",
"a"
)[0];
$votes =
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"text-sm",
"span"
)[0]
);
$c = count($description) - 1;
if(
$c !== -1 &&
$description[$c]["type"] == "text"
){
$description[$c]["value"] .= $num . " ";
}else{
$description[] = [
"type" => "text",
"value" => $num . " "
];
}
$description[] = [
"type" => "link",
"value" => $this->fuckhtml->getTextContent($href),
"url" => $this->fuckhtml->getTextContent($href["attributes"]["href"])
];
$description[] = [
"type" => "text",
"value" => " (" . $votes . ")\n"
];
}
}
$answer["description"] = $description;
}
}
/*
Get sublinks
*/
$this->fuckhtml->load($infobox);
$profiles =
$this->fuckhtml
->getElementById("profiles");
if($profiles){
$profiles =
$this->fuckhtml
->getElementsByClassName(
"chip",
"a"
);
foreach($profiles as $profile){
$name = $this->fuckhtml->getTextContent($profile["attributes"]["title"]);
if(strtolower($name) == "steampowered"){
$name = "Steam";
}
$answer["sublink"][$name] =
$this->fuckhtml->getTextContent($profile["attributes"]["href"]);
}
}
$actors =
$this->fuckhtml
->getElementById("panel-movie-cast");
if($actors){
$this->fuckhtml->load($actors);
$actors =
$this->fuckhtml
->getElementsByClassName("card");
$answer["description"][] = [
"type" => "title",
"value" => "Cast"
];
foreach($actors as $actor){
$this->fuckhtml->load($actor);
$answer["description"][] = [
"type" => "text",
"value" =>
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName("card-body")
[0]
)
];
$answer["description"][] = [
"type" => "image",
"url" => $this->getimagelinkfromstyle("person-thumb")["url"]
];
}
}
$out["answer"][] = $answer;
}
/*
Get actor standalone thingy
*/
$this->fuckhtml->load($resulthtml);
$actors =
$this->fuckhtml
->getElementById("predicate-entity");
if($actors){
$this->fuckhtml->load($actors);
$cards =
$this->fuckhtml
->getElementsByClassName("card");
$url =
$this->fuckhtml
->getElementsByClassName(
"disclaimer",
"div"
)[0];
$this->fuckhtml->load($url);
$url =
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByTagName("a")
[0]
["attributes"]
["href"]
);
$this->fuckhtml->load($actors);
$answer = [
"title" =>
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"entity",
"span"
)[0]
) . " (Cast)",
"description" => [],
"url" => $url,
"sublink" => [],
"thumb" => null,
"table" => []
];
foreach($cards as $card){
$this->fuckhtml->load($card);
$answer["description"][] = [
"type" => "title",
"value" =>
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"title"
)[0]
)
];
$answer["description"][] = [
"type" => "text",
"value" =>
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"text-xs desc"
)[0]
)
];
$answer["description"][] = [
"type" => "image",
"url" => $this->getimagelinkfromstyle("img-bg")["url"]
];
}
$out["answer"][] = $answer;
}
return $out;
}
public function news($get){
$search = $get["s"];
if(strlen($search) === 0){
throw new Exception("Search term is empty!");
}
$nsfw = $get["nsfw"];
$country = $get["country"];
if(strlen($search) > 2048){
throw new Exception("Search query is too long!");
}
/*
$handle = fopen("scraper/brave-news.html", "r");
$html = fread($handle, filesize("scraper/brave-news.html"));
fclose($handle);*/
try{
$html =
$this->get(
"https://search.brave.com/news",
[
"q" => $search
],
$nsfw,
$country
);
}catch(Exception $error){
throw new Exception("Could not fetch search page");
}
$out = [
"status" => "ok",
"npt" => null,
"news" => []
];
// load html
$this->fuckhtml->load($html);
$news =
$this->fuckhtml
->getElementsByClassName(
"snippet inline gap-standard",
"div"
);
foreach($news as $article){
$data = [
"title" => null,
"author" => null,
"description" => null,
"date" => null,
"thumb" =>
[
"url" => null,
"ratio" => null
],
"url" => null
];
$this->fuckhtml->load($article);
$elems =
$this->fuckhtml
->getElementsByTagName("*");
// get title
$data["title"] =
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"snippet-title",
$elems
)
[0]
["innerHTML"]
);
// get description
$data["description"] =
$this->titledots(
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"snippet-description",
$elems
)
[0]
["innerHTML"]
)
);
// get date
$date =
explode(
"",
$this->fuckhtml
->getTextContent(
$this->fuckhtml
->getElementsByClassName(
"snippet-url",
$elems
)[0]
)
);
if(
count($date) !== 1 &&
trim($date[1]) != ""
){
$data["date"] =
strtotime(
$date[1]
);
}
// get URL
$data["url"] =
$this->fuckhtml->getTextContent(
$this->unshiturl(
$this->fuckhtml
->getElementsByClassName(
"result-header",
$elems
)
[0]
["attributes"]
["href"]
)
);
// get thumbnail
$thumb =
$this->fuckhtml
->getElementsByTagName(
"img"
);
if(
count($thumb) === 2 &&
trim(
$thumb[1]
["attributes"]
["src"]
) != ""
){
$data["thumb"] = [
"url" =>
$this->fuckhtml->getTextContent(
$this->unshiturl(
$thumb[1]
["attributes"]
["src"]
)
),
"ratio" => "16:9"
];
}
$out["news"][] = $data;
}
return $out;
}
public function image($get){
$search = $get["s"];
$country = $get["country"];
$nsfw = $get["nsfw"];
$out = [
"status" => "ok",
"npt" => null,
"image" => []
];
try{
$html =
$this->get(
"https://search.brave.com/images",
[
"q" => $search
],
$nsfw,
$country
);
}catch(Exception $error){
throw new Exception("Could not fetch search page");
}
/*
$handle = fopen("scraper/brave-image.html", "r");
$html = fread($handle, filesize("scraper/brave-image.html"));
fclose($handle);*/
preg_match(
'/const data = (\[{.*}\]);/',
$html,
$json
);
if(!isset($json[1])){
throw new Exception("Failed to get data object");
}
$json =
$this->fuckhtml
->parseJsObject(
$json[1]
);
foreach(
$json[1]
["data"]
["body"]
["response"]
["results"]
as $result
){
$out["image"][] = [
"title" => $result["title"],
"source" => [
[
"url" => $result["properties"]["url"],
"width" => null,
"height" => null
],
[
"url" => $result["thumbnail"]["src"],
"width" => null,
"height" => null
]
],
"url" => $result["url"]
];
}
return $out;
}
public function video($get){
$search = $get["s"];
$country = $get["country"];
$nsfw = $get["nsfw"];
$out = [
"status" => "ok",
"npt" => null,
"video" => [],
"author" => [],
"livestream" => [],
"playlist" => [],
"reel" => []
];
try{
$html =
$this->get(
"https://search.brave.com/videos",
[
"q" => $search
],
$nsfw,
$country
);
}catch(Exception $error){
throw new Exception("Could not fetch search page");
}
/*
$handle = fopen("scraper/brave-video.html", "r");
$html = fread($handle, filesize("scraper/brave-video.html"));
fclose($handle);*/
preg_match(
'/const data = (\[{.*}\]);/',
$html,
$json
);
if(!isset($json[1])){
throw new Exception("Failed to get data object");
}
$json =
$this->fuckhtml
->parseJsObject(
$json[1]
);
foreach(
$json
[1]
["data"]
["body"]
["response"]
["results"]
as $result
){
if($result["video"]["author"] != "null"){
$author = [
"name" => $result["video"]["author"]["name"] == "null" ? null : $result["video"]["author"]["name"],
"url" => $result["video"]["author"]["url"] == "null" ? null : $result["video"]["author"]["url"],
"avatar" => $result["video"]["author"]["img"] == "null" ? null : $result["video"]["author"]["img"]
];
}else{
$author = [
"name" => null,
"url" => null,
"avatar" => null
];
}
if($result["thumbnail"] != "null"){
$thumb = [
"url" => $result["thumbnail"]["original"],
"ratio" => "16:9"
];
}else{
$thumb = [
"url" => null,
"ratio" => null
];
}
$out["video"][] = [
"title" => $result["title"],
"description" => $result["description"] == "null" ? null : $this->titledots($result["description"]),
"author" => $author,
"date" => $result["age"] == "null" ? null : strtotime($result["age"]),
"duration" => $result["video"]["duration"] == "null" ? null : $this->hms2int($result["video"]["duration"]),
"views" => $result["video"]["views"] == "null" ? null : (int)$result["video"]["views"],
"thumb" => $thumb,
"url" => $result["url"]
];
}
return $out;
}
private function hms2int($time){
$parts = explode(":", $time, 3);
$time = 0;
if(count($parts) === 3){
// hours
$time = $time + ((int)$parts[0] * 3600);
array_shift($parts);
}
if(count($parts) === 2){
// minutes
$time = $time + ((int)$parts[0] * 60);
array_shift($parts);
}
// seconds
$time = $time + (int)$parts[0];
return $time;
}
private function appendtext($payload, &$text, &$index){
if(trim($payload) == ""){
return;
}
if(
$index !== 0 &&
$text[$index - 1]["type"] == "text"
){
$text[$index - 1]["value"] .= "\n\n" . preg_replace('/ $/', " ", $payload);
}else{
$text[] = [
"type" => "text",
"value" => preg_replace('/ $/', " ", $payload)
];
$index++;
}
}
private function tablesublink($html_collection, &$data){
foreach($html_collection as $html){
$html["innerHTML"] = preg_replace(
'/<style>[\S\s]*<\/style>/i',
"",
$html["innerHTML"]
);
$html =
explode(
":",
$this->fuckhtml->getTextContent($html),
2
);
if(count($html) === 1){
$html = ["Rating", $html[0]];
}
$data["table"][trim($html[0])] = trim($html[1]);
}
}
private function getimagelinkfromstyle($thumb){
$thumb =
$this->fuckhtml
->getElementsByClassName(
$thumb,
"div"
);
if(count($thumb) === 0){
return [
"url" => null,
"ratio" => null
];
}
$thumb = $thumb[0]["attributes"]["style"];
preg_match(
'/background-image: ?url\((\'[^\']+\'|"[^"]+"|[^\)]+)\)/',
$thumb,
$thumb
);
$url = $this->fuckhtml->getTextContent($this->unshiturl(trim($thumb[1], '"\' ')));
if(parse_url($url, PHP_URL_HOST) == "cdn.search.brave.com"){
return [
"url" => null,
"ratio" => null
];
}
return [
"url" => $url,
"ratio" => "16:9"
];
}
private function limitstrlen($text){
return explode("\n", wordwrap($text, 300, "\n"))[0];
}
private function limitwhitespace($text){
return
preg_replace(
'/[\s]+/',
" ",
$text
);
}
private function titledots($title){
$substr = substr($title, -3);
if(
$substr == "..." ||
$substr == ""
){
return trim(substr($title, 0, -3));
}
return trim($title);
}
private function unshiturl($url){
// https://imgs.search.brave.com/XFnbR8Sl7ge82MBDEH7ju0UHImRovMVmQ2qnDvgNTuA/rs:fit:844:225:1/g:ce/aHR0cHM6Ly90c2U0/Lm1tLmJpbmcubmV0/L3RoP2lkPU9JUC54/UWotQXU5N2ozVndT/RDJnNG9BNVhnSGFF/SyZwaWQ9QXBp.jpeg
$tmp = explode("aHR0", $url);
if(count($tmp) !== 2){
// nothing to do
return $url;
}
return
base64_decode(
"aHR0" .
str_replace(["/", "_"], ["", "/"],
explode(
".",
$tmp[1]
)[0]
)
);
}
}