startpage.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658
  1. <?php
  2. class startpage{
  3. public function __construct(){
  4. include "lib/backend.php";
  5. $this->backend = new backend("startpage");
  6. include "lib/fuckhtml.php";
  7. $this->fuckhtml = new fuckhtml();
  8. include "lib/anubis.php";
  9. $this->anubis = new anubis();
  10. $this->anubis_error = "all gucci";
  11. }
  12. public function getfilters($page){
  13. switch($page){
  14. case "web":
  15. return [
  16. "country" => [
  17. "display" => "Country",
  18. "option" => [
  19. "any" => "All Regions",
  20. "es_AR" => "Argentina",
  21. "en_AU" => "Australia",
  22. "de_AT" => "Austria",
  23. "ru_BY" => "Belarus",
  24. "fr_BE" => "Belgium (FR)",
  25. "nl_BE" => "Belgium (NL)",
  26. "bg_BG" => "Bulgaria",
  27. "en_CA" => "Canada (EN)",
  28. "fr_CA" => "Canada (FR)",
  29. "es_CL" => "Chile",
  30. "es_CO" => "Colombia",
  31. "cs_CZ" => "Czech Republic",
  32. "da_DK" => "Denmark",
  33. "ar_EG" => "Egypt",
  34. "et_EE" => "Estonia",
  35. "fi_FI" => "Finland",
  36. "fr_FR" => "France",
  37. "de_DE" => "Germany",
  38. "el_GR" => "Greece",
  39. "hu_HU" => "Hungary",
  40. "hi_IN" => "India (HI)",
  41. "en_IN" => "India (EN)",
  42. "id_ID" => "Indonesia (ID)",
  43. "en_ID" => "Indonesia (EN)",
  44. "en_IE" => "Ireland",
  45. "it_IT" => "Italy",
  46. "ja_JP" => "Japan",
  47. "ko_KR" => "Korea",
  48. "ms_MY" => "Malaysia (MS)",
  49. "en_MY" => "Malaysia (EN)",
  50. "es_MX" => "Mexico",
  51. "nl_NL" => "Netherlands",
  52. "en_NZ" => "New Zealand",
  53. "no_NO" => "Norway",
  54. "es_PE" => "Peru",
  55. "fil_PH" => "Philippines (FIL)",
  56. "en_PH" => "Philippines (EN)",
  57. "pl_PL" => "Poland",
  58. "pt_PT" => "Portugal",
  59. "ro_RO" => "Romania",
  60. "ru_RU" => "Russia",
  61. "ms_SG" => "Singapore (MS)",
  62. "en_SG" => "Singapore (EN)",
  63. "es_ES" => "Spain (ES)",
  64. "ca_ES" => "Spain (CA)",
  65. "sv_SE" => "Sweden",
  66. "de_CH" => "Switzerland (DE)",
  67. "fr_CH" => "Switzerland (FR)",
  68. "it_CH" => "Switzerland (IT)",
  69. "tr_TR" => "Turkey",
  70. "uk_UA" => "Ukraine",
  71. "en_US" => "US (EN)",
  72. "es_US" => "US (ES)",
  73. "es_UY" => "Uruguay",
  74. "es_VE" => "Venezuela",
  75. "vi_VN" => "Vietnam (VI)",
  76. "en_VN" => "Vietnam (EN)",
  77. "en_ZA" => "South Africa"
  78. ]
  79. ],
  80. "nsfw" => [ // qadf
  81. "display" => "NSFW",
  82. "option" => [
  83. "yes" => "Yes", // qadf=none
  84. "no" => "No" // qadf=heavy
  85. ]
  86. ],
  87. "time" => [ // with_date
  88. "display" => "Time posted",
  89. "option" => [
  90. "any" => "Any time",
  91. "d" => "Past 24 hours",
  92. "w" => "Past week",
  93. "m" => "Past month",
  94. "y" => "Past year",
  95. ]
  96. ],
  97. "extendedsearch" => [
  98. // undefined display, so it wont show in frontend
  99. "option" => [
  100. "yes" => "Yes",
  101. "no" => "No"
  102. ]
  103. ]
  104. ];
  105. break;
  106. case "images":
  107. return [
  108. "nsfw" => [ // qadf
  109. "display" => "NSFW",
  110. "option" => [
  111. "yes" => "Yes", // qadf=none
  112. "no" => "No" // qadf=heavy
  113. ]
  114. ],
  115. "size" => [ // flimgsize
  116. "display" => "Size",
  117. "option" => [
  118. "any" => "Any size",
  119. "Small" => "Small",
  120. "Medium" => "Medium",
  121. "Large" => "Large",
  122. "Wallpaper" => "Wallpaper",
  123. // from here, image-size-select, var prefix = isz:lt,islt:
  124. "qsvgs" => "Larger than 400x300",
  125. "vga" => "Larger than 640x480",
  126. "svga" => "Larger than 800x600",
  127. "xga" => "Larger than 1024x768",
  128. "qsvgs" => "Larger than 400x300",
  129. "2mp" => "Larger than 2 MP (1600x1200)",
  130. "4mp" => "Larger than 4 MP (2272x1704)",
  131. "6mp" => "Larger than 6 MP (2816x2112)",
  132. "8mp" => "Larger than 8 MP (3264x2448)",
  133. "10mp" => "Larger than 10 MP (3648x2736)",
  134. "12mp" => "Larger than 12 MP (4096x3072)",
  135. "15mp" => "Larger than 15 MP (4480x3360)",
  136. "20mp" => "Larger than 20 MP (5120x3840)",
  137. "40mp" => "Larger than 40 MP (7216x5412)",
  138. "70mp" => "Larger than 70 MP (9600x7200)"
  139. ]
  140. ],
  141. "color" => [ // flimgcolor
  142. "display" => "Color",
  143. "option" => [
  144. "any" => "Any color",
  145. // from here, var prefix = ic:
  146. "color" => "Color only",
  147. "bnw" => "Black & white", // set to "gray"
  148. // from here, var prefix = ic:specific,isc:
  149. "red" => "Red",
  150. "orange" => "Orange",
  151. "yellow" => "Yellow",
  152. "green" => "Green",
  153. "teal" => "Teal",
  154. "blue" => "Blue",
  155. "purple" => "Purple",
  156. "pink" => "Pink",
  157. "white" => "White",
  158. "gray" => "Gray",
  159. "black" => "Black",
  160. "brown" => "Brown"
  161. ]
  162. ],
  163. "type" => [ // flimgtype
  164. "display" => "Type",
  165. "option" => [
  166. "any" => "Any type",
  167. "AnimatedGif" => "Animated GIF",
  168. "Clipart" => "Clip Art",
  169. "Line" => "Line Drawing",
  170. "Photo" => "Photograph",
  171. "Transparent" => "Transparent Background"
  172. ]
  173. ],
  174. "license" => [ // flimglicense
  175. "display" => "License",
  176. "option" => [
  177. "any" => "Any license",
  178. "p" => "Public domain",
  179. "s" => "Free to share",
  180. "sc" => "Free to share commercially",
  181. "m" => "Free to modify",
  182. "mc" => "Free to modify commercially"
  183. ]
  184. ]
  185. ];
  186. break;
  187. case "videos":
  188. return [
  189. "nsfw" => [ // qadf
  190. "display" => "NSFW",
  191. "option" => [
  192. "yes" => "Yes", // qadf=none
  193. "no" => "No" // qadf=heavy
  194. ]
  195. ],
  196. "sort" => [
  197. "display" => "Sort by",
  198. "option" => [
  199. "relevance" => "Most relevant",
  200. "popular" => "Most popular",
  201. "recent" => "Most recent"
  202. ]
  203. ],
  204. "duration" => [ // with_duration
  205. "display" => "Duration",
  206. "option" => [
  207. "any" => "Any duration",
  208. "short" => "Short",
  209. "medium" => "Medium",
  210. "long" => "Long"
  211. ]
  212. ]
  213. ];
  214. break;
  215. case "news":
  216. return [
  217. "nsfw" => [ // qadf
  218. "display" => "NSFW",
  219. "option" => [
  220. "yes" => "Yes", // qadf=none
  221. "no" => "No" // qadf=heavy
  222. ]
  223. ],
  224. "time" => [ // with_date
  225. "display" => "Time posted",
  226. "option" => [
  227. "any" => "Any time",
  228. "d" => "Past 24 hours",
  229. "w" => "Past week",
  230. "m" => "Past month"
  231. ]
  232. ]
  233. ];
  234. break;
  235. //preferences=date_timeEEEworldN1Ndisable_family_filterEEE1N1Ndisable_open_in_new_windowEEE0N1Nenable_post_methodEEE1N1Nenable_proxy_safety_suggestEEE0N1Nenable_stay_controlEEE0N1Ninstant_answersEEE1N1Nlang_homepageEEEs%2Fdevice%2FenN1NlanguageEEEazerbaijaniN1Nlanguage_uiEEEenglishN1Nnum_of_resultsEEE20N1Nsearch_results_regionEEEallN1NsuggestionsEEE1N1Nwt_unitEEEcelsius; Domain=startpage.com; Expires=Mon, 28 Oct 2024 20:21:58 GMT; Secure; Path=/
  236. //preferences=date_timeEEEworldN1Ndisable_family_filterEEE1N1Ndisable_open_in_new_windowEEE0N1Nenable_post_methodEEE1N1Nenable_proxy_safety_suggestEEE0N1Nenable_stay_controlEEE0N1Ninstant_answersEEE1N1Nlang_homepageEEEs%2Fdevice%2FenN1NlanguageEEEenglishN1Nlanguage_uiEEEenglishN1Nnum_of_resultsEEE20N1Nsearch_results_regionEEEallN1NsuggestionsEEE1N1Nwt_unitEEEcelsius; Domain=startpage.com; Expires=Mon, 28 Oct 2024 20:22:52 GMT; Secure; Path=/
  237. }
  238. }
  239. private function get($proxy, $url, $get = [], $post = false, $is_xhr = false, $return_cookies = false, $passed_cookies = []){
  240. $curlproc = curl_init();
  241. if($post === true){
  242. curl_setopt($curlproc, CURLOPT_POST, true);
  243. curl_setopt($curlproc, CURLOPT_POSTFIELDS, $get);
  244. }elseif($get !== []){
  245. $get = http_build_query($get);
  246. $url .= "?" . $get;
  247. }
  248. if($return_cookies){
  249. $cookies = [];
  250. curl_setopt($curlproc, CURLOPT_HEADERFUNCTION, function($curlproc, $header) use (&$cookies){
  251. $length = strlen($header);
  252. $header = explode(":", $header, 2);
  253. if(trim(strtolower($header[0])) == "set-cookie"){
  254. $cookie_tmp = explode("=", trim($header[1]), 2);
  255. $cookies[trim($cookie_tmp[0])] =
  256. explode(";", $cookie_tmp[1], 2)[0];
  257. }
  258. return $length;
  259. });
  260. }
  261. curl_setopt($curlproc, CURLOPT_URL, $url);
  262. // http2 bypass
  263. curl_setopt($curlproc, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
  264. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  265. $passed_cookies["preferences"] = "date_timeEEEworldN1Ndisable_family_filterEEE1N1Ndisable_open_in_new_windowEEE0N1Nenable_post_methodEEE1N1Nenable_proxy_safety_suggestEEE0N1Nenable_stay_controlEEE0N1Ninstant_answersEEE1N1Nlang_homepageEEEs%2Fdevice%2FenN1NlanguageEEEenglishN1Nlanguage_uiEEEenglishN1Nnum_of_resultsEEE20N1Nsearch_results_regionEEEallN1NsuggestionsEEE1N1Nwt_unitEEEcelsius";
  266. foreach($passed_cookies as $name => $val){
  267. $header_cookies[] = "$name=$val";
  268. }
  269. $header_cookies = rtrim(implode("; ", $header_cookies));
  270. if($is_xhr === true){
  271. curl_setopt($curlproc, CURLOPT_HTTPHEADER,
  272. ["User-Agent: " . config::USER_AGENT,
  273. "Accept: application/json",
  274. "Accept-Language: en-US,en;q=0.5",
  275. "Accept-Encoding: gzip",
  276. "Referer: https://www.startpage.com/",
  277. "Content-Type: application/json",
  278. "Content-Length: " . strlen($get),
  279. "Origin: https://www.startpage.com/",
  280. "DNT: 1",
  281. "Connection: keep-alive",
  282. "Cookie: $header_cookies",
  283. "Sec-Fetch-Dest: empty",
  284. "Sec-Fetch-Mode: cors",
  285. "Sec-Fetch-Site: same-origin",
  286. "TE: trailers"]
  287. );
  288. }elseif($post === true){
  289. curl_setopt($curlproc, CURLOPT_HTTPHEADER,
  290. ["User-Agent: " . config::USER_AGENT,
  291. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
  292. "Accept-Language: en-US,en;q=0.5",
  293. "Accept-Encoding: gzip",
  294. "Referer: https://www.startpage.com/",
  295. "Content-Type: application/x-www-form-urlencoded",
  296. "Content-Length: " . strlen($get),
  297. "DNT: 1",
  298. "Connection: keep-alive",
  299. "Cookie: $header_cookies",
  300. "Upgrade-Insecure-Requests: 1",
  301. "Sec-Fetch-Dest: document",
  302. "Sec-Fetch-Mode: navigate",
  303. "Sec-Fetch-Site: none",
  304. "Sec-Fetch-User: ?1",
  305. "Priority: u=0, i",
  306. "TE: trailers"]
  307. );
  308. }else{
  309. curl_setopt($curlproc, CURLOPT_HTTPHEADER,
  310. ["User-Agent: " . config::USER_AGENT,
  311. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
  312. "Accept-Language: en-US,en;q=0.5",
  313. "Accept-Encoding: gzip",
  314. "DNT: 1",
  315. "Connection: keep-alive",
  316. "Cookie: $header_cookies",
  317. "Sec-Fetch-Dest: document",
  318. "Sec-Fetch-Mode: navigate",
  319. "Sec-Fetch-Site: none",
  320. "Sec-Fetch-User: ?1",
  321. "Priority: u=0, i",
  322. "TE: trailers"]
  323. );
  324. }
  325. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  326. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  327. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  328. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  329. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  330. $this->backend->assign_proxy($curlproc, $proxy);
  331. $data = curl_exec($curlproc);
  332. if(curl_errno($curlproc)){
  333. throw new Exception(curl_error($curlproc));
  334. }
  335. curl_close($curlproc);
  336. if($return_cookies){
  337. return [
  338. "cookies" => $cookies,
  339. "data" => $data
  340. ];
  341. }
  342. return $data;
  343. }
  344. public function web($get){
  345. if($get["npt"]){
  346. $this->anubis_error = "Not ran on pagination";
  347. [$post, $proxy] = $this->backend->get($get["npt"], "web");
  348. $post = json_decode($post, true);
  349. $query = $post["query"];
  350. $cookies = $post["cookies"];
  351. try{
  352. $html = $this->get(
  353. $proxy,
  354. "https://www.startpage.com/sp/search",
  355. $query,
  356. true,
  357. false,
  358. false,
  359. $cookies
  360. );
  361. }catch(Exception $error){
  362. throw new Exception("Failed to fetch search page");
  363. }
  364. $get_instant_answer = false;
  365. }else{
  366. $proxy = $this->backend->get_ip();
  367. $params = [
  368. "query" => $get["s"],
  369. "cat" => "web",
  370. "pl" => "opensearch"
  371. ];
  372. if($get["nsfw"] == "no"){
  373. $params["qadf"] = "heavy";
  374. $get_instant_answer = false;
  375. }else{
  376. $get_instant_answer = true;
  377. }
  378. if($get["country"] !== "any"){
  379. $params["qsr"] = $get["country"];
  380. }
  381. if($get["time"] !== "any"){
  382. $params["with_date"] = $get["time"];
  383. }
  384. try{
  385. $html = $this->get(
  386. $proxy,
  387. "https://www.startpage.com/sp/search",
  388. $params,
  389. false,
  390. false,
  391. true,
  392. []
  393. );
  394. }catch(Exception $error){
  395. throw new Exception("Failed to fetch search page");
  396. }
  397. //$html = file_get_contents("scraper/startpage.html");
  398. // detect captcha on that page
  399. $this->detect_captcha($html["data"]);
  400. // solve anubis if need be
  401. $cookies = $this->solve_anubis($proxy, $html);
  402. if($cookies === false){
  403. $cookies = [];
  404. $html = $html["data"];
  405. }else{
  406. try{
  407. $html = $this->get(
  408. $proxy,
  409. "https://www.startpage.com/sp/search",
  410. $params,
  411. false,
  412. false,
  413. false,
  414. $cookies
  415. );
  416. }catch(Exception $error){
  417. throw new Exception("Failed to re-fetch search page. Anubis error: {$this->anubis_error}");
  418. }
  419. }
  420. }
  421. // detect captcha even if we passed anubis challenge
  422. $this->detect_captcha($html);
  423. //echo htmlspecialchars($html);
  424. if(
  425. preg_match(
  426. '/React\.createElement\(UIStartpage\.AppSerpWeb, ?(.+)\),?$/m',
  427. $html,
  428. $matches
  429. ) === 0
  430. ){
  431. throw new Exception("Failed to grep JSON object. Anubis error: {$this->anubis_error}");
  432. }
  433. $json = json_decode($matches[1], true);
  434. if($json === null){
  435. throw new Exception("Failed to decode JSON. Anubis error: {$this->anubis_error}");
  436. }
  437. //print_r($json);
  438. $out = [
  439. "status" => "ok",
  440. "spelling" => [
  441. "type" => "no_correction",
  442. "using" => null,
  443. "correction" => null
  444. ],
  445. "npt" => null,
  446. "answer" => [],
  447. "web" => [],
  448. "image" => [],
  449. "video" => [],
  450. "news" => [],
  451. "related" => []
  452. ];
  453. // get npt
  454. $out["npt"] = $this->parse_npt($json, "web", $proxy, $cookies);
  455. foreach($json["render"]["presenter"]["regions"]["mainline"] as $category){
  456. if(!isset($category["display_type"])){
  457. continue;
  458. }
  459. switch($category["display_type"]){
  460. case "web-google":
  461. case "web-bing":
  462. foreach($category["results"] as $result){
  463. $sublinks = [];
  464. if(isset($result["siteLinks"])){
  465. foreach($result["siteLinks"] as $sublink){
  466. $sublinks[] = [
  467. "title" => $sublink["title"],
  468. "description" => null,
  469. "url" => $sublink["clickUrl"]
  470. ];
  471. }
  472. }
  473. $description =
  474. explode(
  475. "...",
  476. $this->titledots(
  477. html_entity_decode(
  478. $this->fuckhtml
  479. ->getTextContent(
  480. $result["description"]
  481. )
  482. )
  483. ),
  484. 2
  485. );
  486. $date = strtotime(trim($description[0]));
  487. if(
  488. $date === false ||
  489. count($description) !== 2 ||
  490. strlen($description[0]) > 14
  491. ){
  492. // no date found
  493. $description =
  494. implode(
  495. " ... ",
  496. $description
  497. );
  498. $date = null;
  499. }else{
  500. // date found
  501. $description = ltrim($description[1]);
  502. }
  503. $out["web"][] = [
  504. "title" =>
  505. $this->remove_penguins(
  506. $this->titledots(
  507. html_entity_decode(
  508. $this->fuckhtml
  509. ->getTextContent(
  510. $result["title"]
  511. )
  512. )
  513. )
  514. ),
  515. "description" => $this->remove_penguins($description),
  516. "url" => $result["clickUrl"],
  517. "date" => $date,
  518. "type" => "web",
  519. "thumb" => [
  520. "url" => null,
  521. "ratio" => null
  522. ],
  523. "sublink" => $sublinks,
  524. "table" => []
  525. ];
  526. }
  527. break;
  528. case "images-qi-top":
  529. case "images-bing-top":
  530. foreach($category["results"] as $result){
  531. if(isset($result["mdThumbnailWidth"])){
  532. $thumb_width = $result["mdThumbnailWidth"];
  533. }elseif(isset($result["thumbnailWidth"])){
  534. $thumb_width = $result["thumbnailWidth"];
  535. }else{
  536. $thumb_width = null;
  537. }
  538. if(isset($result["mdThumbnailHeight"])){
  539. $thumb_height = $result["mdThumbnailHeight"];
  540. }elseif(isset($result["thumbnailHeight"])){
  541. $thumb_height = $result["thumbnailHeight"];
  542. }else{
  543. $thumb_height = null;
  544. }
  545. if(isset($result["mdThumbnailUrl"])){
  546. $thumb_source = $result["mdThumbnailUrl"];
  547. }elseif(isset($result["thumbnailUrl"])){
  548. $thumb_source = $result["thumbnailUrl"];
  549. }else{
  550. // should not happen
  551. continue;
  552. }
  553. $out["image"][] = [
  554. "title" =>
  555. $this->titledots(
  556. html_entity_decode(
  557. $this->fuckhtml
  558. ->getTextContent(
  559. $result["title"]
  560. )
  561. )
  562. ),
  563. "source" => [
  564. [
  565. "url" => $result["rawImageUrl"],
  566. "width" => (int)$result["width"],
  567. "height" => (int)$result["height"]
  568. ],
  569. [
  570. "url" => $this->unshitimage($thumb_source),
  571. "width" => (int)$thumb_width,
  572. "height" => (int)$thumb_height
  573. ]
  574. ],
  575. "url" => $result["altClickUrl"]
  576. ];
  577. }
  578. break;
  579. case "spellsuggest-google":
  580. if(isset($category["results"][0]["query"])){
  581. $out["spelling"] =
  582. [
  583. "type" => "including",
  584. "using" => rawurldecode($json["render"]["query"]),
  585. "correction" => html_entity_decode($category["results"][0]["query"])
  586. ];
  587. }
  588. break;
  589. case "spelladjust-bing":
  590. if(isset($category["results"][0]["alterationDisplayQuery"])){
  591. $out["spelling"] =
  592. [
  593. "type" => "including",
  594. "using" => $this->remove_penguins($category["results"][0]["alterationDisplayQuery"]),
  595. "correction" => $category["results"][0]["originalQuery"]
  596. ];
  597. }
  598. break;
  599. case "aylf-bing-top":
  600. if(isset($category["results"][0]["query"])){
  601. foreach($category["results"] as $ddm){
  602. $out["related"][] = $ddm["query"];
  603. }
  604. }
  605. break;
  606. case "video-bing-top":
  607. if(isset($category["results"][0]["clickUrl"])){
  608. foreach($category["results"] as $video){
  609. if(isset($video["thumbnailUrl"])){
  610. $thumb = [
  611. "ratio" => "16:9",
  612. "url" => $this->unshitimage($video["thumbnailUrl"])
  613. ];
  614. }else{
  615. $thumb = [
  616. "ratio" => null,
  617. "url" => null
  618. ];
  619. }
  620. $out["video"][] = [
  621. "title" => $video["title"],
  622. "description" => $this->remove_penguins($video["description"]),
  623. "date" => isset($video["date"]) ? strtotime($video["date"]) : null,
  624. "duration" => (int)$video["duration"] !== 0 ? (int)$video["duration"] / 1000 : null,
  625. "views" => $video["viewCount"],
  626. "thumb" => $thumb,
  627. "url" => $video["clickUrl"]
  628. ];
  629. }
  630. }
  631. break;
  632. case "dictionary-qi":
  633. foreach($category["results"] as $result){
  634. $answer = [
  635. "title" => $result["word"],
  636. "description" => [],
  637. "url" => null,
  638. "thumb" => null,
  639. "table" => [],
  640. "sublink" => []
  641. ];
  642. foreach($result["lexical_categories"] as $lexic_type => $definitions){
  643. $answer["description"][] = [
  644. "type" => "title",
  645. "value" => $lexic_type
  646. ];
  647. $i = 0;
  648. foreach($definitions as $definition){
  649. $text_definition = trim($definition["definition"]);
  650. $text_example = trim($definition["example"]);
  651. $text_synonyms = implode(", ", $definition["synonyms"]);
  652. if($text_definition != ""){
  653. $i++;
  654. $c = count($answer["description"]) - 1;
  655. if(
  656. $c !== 0 &&
  657. $answer["description"][$c]["type"] == "text"
  658. ){
  659. $answer["description"][$c]["value"] .=
  660. "\n\n" . $i . ". " . $text_definition;
  661. }else{
  662. $answer["description"][] = [
  663. "type" => "text",
  664. "value" => $i . ". " . $text_definition
  665. ];
  666. }
  667. }
  668. if($text_example != ""){
  669. $answer["description"][] = [
  670. "type" => "quote",
  671. "value" => $text_example
  672. ];
  673. }
  674. if($text_synonyms != ""){
  675. $answer["description"][] = [
  676. "type" => "text",
  677. "value" => "Synonyms: " . $text_synonyms
  678. ];
  679. }
  680. }
  681. }
  682. $out["answer"][] = $answer;
  683. }
  684. break;
  685. case "news-bing-top":
  686. if(isset($category["results"][0]["clickUrl"])){
  687. foreach($category["results"] as $news){
  688. if(isset($news["thumbnailUrl"])){
  689. $thumb = [
  690. "ratio" => "16:9",
  691. "url" => $news["thumbnailUrl"]
  692. ];
  693. }else{
  694. $thumb = [
  695. "ratio" => null,
  696. "url" => null
  697. ];
  698. }
  699. $out["news"][] = [
  700. "title" => $this->remove_penguins($news["title"]),
  701. "description" => $news["description"],
  702. "date" => isset($news["date"]) ? strtotime($news["date"]) : null,
  703. "thumb" => $thumb,
  704. "url" => $news["clickUrl"],
  705. ];
  706. }
  707. }
  708. break;
  709. }
  710. }
  711. return $out;
  712. }
  713. public function image($get){
  714. if($get["npt"]){
  715. $this->anubis_error = "Not ran on pagination";
  716. [$post, $proxy] = $this->backend->get($get["npt"], "images");
  717. $post = json_decode($post, true);
  718. $query = $post["query"];
  719. $cookies = $post["cookies"];
  720. try{
  721. $html = $this->get(
  722. $proxy,
  723. "https://www.startpage.com/sp/search",
  724. $query,
  725. true,
  726. false,
  727. false,
  728. $cookies
  729. );
  730. }catch(Exception $error){
  731. throw new Exception("Failed to fetch search page");
  732. }
  733. }else{
  734. $search = $get["s"];
  735. if(strlen($search) === 0){
  736. throw new Exception("Search term is empty!");
  737. }
  738. $proxy = $this->backend->get_ip();
  739. $params = [
  740. "query" => $get["s"],
  741. "cat" => "images",
  742. "pl" => "opensearch"
  743. ];
  744. if($get["nsfw"] == "no"){
  745. $params["qadf"] = "heavy";
  746. }
  747. if($get["size"] != "any"){
  748. if(
  749. $get["size"] == "Small" ||
  750. $get["size"] == "Medium" ||
  751. $get["size"] == "Large" ||
  752. $get["size"] == "Wallpaper"
  753. ){
  754. $params["flimgsize"] = $get["size"];
  755. }else{
  756. $params["image-size-select"] = "isz:lt,islt:" . $get["size"];
  757. }
  758. }
  759. if($get["color"] != "any"){
  760. if($get["color"] == "color"){
  761. $params["flimgcolor"] = "ic:color";
  762. }elseif($get["color"] == "bnw"){
  763. $params["flimgcolor"] = "ic:gray";
  764. }else{
  765. $params["flimgcolor"] = "ic:specific,isc:" . $get["color"];
  766. }
  767. }
  768. if($get["type"] != "any"){
  769. $params["flimgtype"] = $get["type"];
  770. }
  771. if($get["license"] != "any"){
  772. $params["flimglicense"] = $get["license"];
  773. }
  774. try{
  775. $html = $this->get(
  776. $proxy,
  777. "https://www.startpage.com/sp/search",
  778. $params,
  779. false,
  780. false,
  781. true,
  782. []
  783. );
  784. }catch(Exception $error){
  785. throw new Exception("Failed to fetch search page");
  786. }
  787. //$html = file_get_contents("scraper/startpage.html");
  788. // detect captcha on that page
  789. $this->detect_captcha($html["data"]);
  790. // solve anubis if need be
  791. $cookies = $this->solve_anubis($proxy, $html);
  792. if($cookies === false){
  793. $cookies = [];
  794. $html = $html["data"];
  795. }else{
  796. try{
  797. $html = $this->get(
  798. $proxy,
  799. "https://www.startpage.com/sp/search",
  800. $params,
  801. false,
  802. false,
  803. false,
  804. $cookies
  805. );
  806. }catch(Exception $error){
  807. throw new Exception("Failed to re-fetch search page. Anubis error: {$this->anubis_error}");
  808. }
  809. }
  810. }
  811. $this->detect_captcha($html);
  812. $out = [
  813. "status" => "ok",
  814. "npt" => null,
  815. "image" => []
  816. ];
  817. if(
  818. preg_match(
  819. '/React\.createElement\(UIStartpage\.AppSerpImages, ?(.+)\),?$/m',
  820. $html,
  821. $matches
  822. ) === 0
  823. ){
  824. throw new Exception("Failed to grep JSON object. Anubis error: {$this->anubis_error}");
  825. }
  826. $json = json_decode($matches[1], true);
  827. if($json === null){
  828. throw new Exception("Failed to decode JSON object. Anubis error: {$this->anubis_error}");
  829. }
  830. // get npt
  831. $out["npt"] = $this->parse_npt($json, "images", $proxy, $cookies);
  832. // get images
  833. foreach($json["render"]["presenter"]["regions"]["mainline"] as $category){
  834. if($category["display_type"] != "images-bing"){
  835. // ignore ads and !! suggestions !! @todo
  836. continue;
  837. }
  838. foreach($category["results"] as $image){
  839. $out["image"][] = [
  840. "title" => $this->titledots($image["title"]),
  841. "source" => [
  842. [
  843. "url" => $this->unshitimage($image["clickUrl"]),
  844. "width" => (int)$image["width"],
  845. "height" => (int)$image["height"]
  846. ],
  847. [
  848. "url" => $this->unshitimage($image["thumbnailUrl"]),
  849. "width" => (int)$image["thumbnailWidth"],
  850. "height" => (int)$image["thumbnailHeight"]
  851. ]
  852. ],
  853. "url" => $image["altClickUrl"]
  854. ];
  855. }
  856. }
  857. return $out;
  858. }
  859. public function video($get){
  860. if($get["npt"]){
  861. $this->anubis_error = "Not ran on pagination";
  862. [$post, $proxy] = $this->backend->get($get["npt"], "videos");
  863. $post = json_decode($post, true);
  864. $query = $post["query"];
  865. $cookies = $post["cookies"];
  866. try{
  867. $html = $this->get(
  868. $proxy,
  869. "https://www.startpage.com/sp/search",
  870. $query,
  871. true,
  872. false,
  873. false,
  874. $cookies
  875. );
  876. }catch(Exception $error){
  877. throw new Exception("Failed to fetch search page");
  878. }
  879. }else{
  880. $search = $get["s"];
  881. if(strlen($search) === 0){
  882. throw new Exception("Search term is empty!");
  883. }
  884. $proxy = $this->backend->get_ip();
  885. $params = [
  886. "query" => $get["s"],
  887. "cat" => "video",
  888. "pl" => "opensearch"
  889. ];
  890. if($get["nsfw"] == "no"){
  891. $params["qadf"] = "heavy";
  892. }
  893. if($get["sort"] != "relevance"){
  894. $params["sort_by"] = $get["sort"];
  895. }
  896. if($get["duration"] != "any"){
  897. $params["with_duration"] = $get["duration"];
  898. }
  899. try{
  900. $html = $this->get(
  901. $proxy,
  902. "https://www.startpage.com/sp/search",
  903. $params,
  904. false,
  905. false,
  906. true,
  907. []
  908. );
  909. }catch(Exception $error){
  910. throw new Exception("Failed to fetch search page");
  911. }
  912. //$html = file_get_contents("scraper/startpage.html");
  913. // detect captcha on that page
  914. $this->detect_captcha($html["data"]);
  915. // solve anubis if need be
  916. $cookies = $this->solve_anubis($proxy, $html);
  917. if($cookies === false){
  918. $cookies = [];
  919. $html = $html["data"];
  920. }else{
  921. try{
  922. $html = $this->get(
  923. $proxy,
  924. "https://www.startpage.com/sp/search",
  925. $params,
  926. false,
  927. false,
  928. false,
  929. $cookies
  930. );
  931. }catch(Exception $error){
  932. throw new Exception("Failed to re-fetch search page. Anubis error: {$this->anubis_error}");
  933. }
  934. }
  935. }
  936. $this->detect_captcha($html);
  937. if(
  938. preg_match(
  939. '/React\.createElement\(UIStartpage\.AppSerpVideos, ?(.+)\),?$/m',
  940. $html,
  941. $matches
  942. ) === 0
  943. ){
  944. throw new Exception("Failed to get JSON object. Anubis error: {$this->anubis_error}");
  945. }
  946. $json = json_decode($matches[1], true);
  947. if($json === null){
  948. throw new Exception("Failed to decode JSON object. Anubis error: {$this->anubis_error}");
  949. }
  950. $out = [
  951. "status" => "ok",
  952. "npt" => null,
  953. "video" => [],
  954. "author" => [],
  955. "livestream" => [],
  956. "playlist" => [],
  957. "reel" => []
  958. ];
  959. // get npt
  960. $out["npt"] = $this->parse_npt($json, "video", $proxy, $cookies);
  961. // get results
  962. foreach($json["render"]["presenter"]["regions"]["mainline"] as $category){
  963. if(
  964. preg_match(
  965. '/^video-/i',
  966. $category["display_type"]
  967. )
  968. ){
  969. foreach($category["results"] as $video){
  970. if(
  971. isset($video["thumbnailUrl"]) &&
  972. $video["thumbnailUrl"] !== null
  973. ){
  974. $thumb = [
  975. "ratio" => "16:9",
  976. "url" => $this->unshitimage($video["thumbnailUrl"])
  977. ];
  978. }else{
  979. $thumb = [
  980. "ratio" => null,
  981. "url" => null
  982. ];
  983. }
  984. $out["video"][] = [
  985. "title" => str_replace(["", ""], "", $video["title"]),
  986. "description" => $this->limitstrlen($video["description"]),
  987. "author" => [
  988. "name" => $video["channelTitle"],
  989. "url" => null,
  990. "avatar" => null
  991. ],
  992. "date" => strtotime($video["publishDate"]),
  993. "duration" => $this->hms2int($category["display_type"] == "video-youtube" ? $video["duration"] : $video["duration"] / 1000),
  994. "views" => (int)$video["viewCount"],
  995. "thumb" => $thumb,
  996. "url" => $video["clickUrl"]
  997. ];
  998. }
  999. }
  1000. }
  1001. return $out;
  1002. }
  1003. public function news($get){
  1004. if($get["npt"]){
  1005. $this->anubis_error = "Not ran on pagination";
  1006. [$post, $proxy] = $this->backend->get($get["npt"], "news");
  1007. $post = json_decode($post, true);
  1008. $query = $post["query"];
  1009. $cookies = $post["cookies"];
  1010. try{
  1011. $html = $this->get(
  1012. $proxy,
  1013. "https://www.startpage.com/sp/search",
  1014. $query,
  1015. true,
  1016. false,
  1017. false,
  1018. $cookies
  1019. );
  1020. }catch(Exception $error){
  1021. throw new Exception("Failed to fetch search page");
  1022. }
  1023. }else{
  1024. $search = $get["s"];
  1025. if(strlen($search) === 0){
  1026. throw new Exception("Search term is empty!");
  1027. }
  1028. $proxy = $this->backend->get_ip();
  1029. $params = [
  1030. "query" => $get["s"],
  1031. "cat" => "news",
  1032. "pl" => "opensearch"
  1033. ];
  1034. if($get["nsfw"] == "no"){
  1035. $params["qadf"] = "heavy";
  1036. }
  1037. if($get["time"] != "any"){
  1038. $params["with_date"] = $get["time"];
  1039. }
  1040. try{
  1041. $html = $this->get(
  1042. $proxy,
  1043. "https://www.startpage.com/sp/search",
  1044. $params,
  1045. false,
  1046. false,
  1047. true,
  1048. []
  1049. );
  1050. }catch(Exception $error){
  1051. throw new Exception("Failed to fetch search page. Anubis error: {$this->anubis_error}");
  1052. }
  1053. //$html = file_get_contents("scraper/startpage.html");
  1054. // detect captcha on that page
  1055. $this->detect_captcha($html["data"]);
  1056. // solve anubis if need be
  1057. $cookies = $this->solve_anubis($proxy, $html);
  1058. if($cookies === false){
  1059. $cookies = [];
  1060. $html = $html["data"];
  1061. }else{
  1062. try{
  1063. $html = $this->get(
  1064. $proxy,
  1065. "https://www.startpage.com/sp/search",
  1066. $params,
  1067. false,
  1068. false,
  1069. false,
  1070. $cookies
  1071. );
  1072. }catch(Exception $error){
  1073. throw new Exception("Failed to re-fetch search page. Anubis error: {$this->anubis_error}");
  1074. }
  1075. }
  1076. }
  1077. $this->detect_captcha($html);
  1078. if(
  1079. preg_match(
  1080. '/React\.createElement\(UIStartpage\.AppSerpNews, ?(.+)\),?$/m',
  1081. $html,
  1082. $matches
  1083. ) === 0
  1084. ){
  1085. throw new Exception("Failed to get JSON object. Anubis error: {$this->anubis_error}");
  1086. }
  1087. $json = json_decode($matches[1], true);
  1088. if($json === null){
  1089. throw new Exception("Failed to decode JSON object. Anubis error: {$this->anubis_error}");
  1090. }
  1091. $out = [
  1092. "status" => "ok",
  1093. "npt" => null,
  1094. "news" => []
  1095. ];
  1096. // get npt
  1097. $out["npt"] = $this->parse_npt($json, "news", $proxy, $cookies);
  1098. //print_r($json);
  1099. foreach($json["render"]["presenter"]["regions"]["mainline"] as $category){
  1100. foreach($category["results"] as $news){
  1101. if(
  1102. isset($news["thumbnailUrl"]) &&
  1103. $news["thumbnailUrl"] !== null
  1104. ){
  1105. $thumb = [
  1106. "ratio" => "16:9",
  1107. "url" => $this->unshitimage($news["thumbnailUrl"])
  1108. ];
  1109. }else{
  1110. $thumb = [
  1111. "ratio" => null,
  1112. "url" => null
  1113. ];
  1114. }
  1115. $out["news"][] = [
  1116. "title" => $this->titledots($this->remove_penguins($news["title"])),
  1117. "author" => $news["source"],
  1118. "description" => isset($news["description"]) ? $this->titledots($this->remove_penguins($news["description"])) : null,
  1119. "date" => isset($news["date"]) ? strtotime($news["date"]) : null,
  1120. "thumb" => $thumb,
  1121. "url" => $this->remove_tracking_link($news["clickUrl"])
  1122. ];
  1123. }
  1124. }
  1125. return $out;
  1126. }
  1127. private function parse_npt($json, $pagetype, $proxy, $cookies){
  1128. foreach($json["render"]["presenter"]["pagination"]["pages"] as $page){
  1129. if($page["name"] == "Next"){
  1130. return
  1131. $this->backend->store(
  1132. json_encode([
  1133. "query" =>
  1134. http_build_query(
  1135. [
  1136. "lui" => "english",
  1137. "language" => "english",
  1138. "sc" => $json["render"]["search_sc"],
  1139. "t" => "device",
  1140. "cat" => $pagetype,
  1141. "segment" => "startpage.udog", // "original" sometimes
  1142. "abd" => "0",
  1143. "abe" => "0",
  1144. "query" => rawurldecode($json["render"]["query"]),
  1145. "page" => $page["number"],
  1146. "qsr" => "all",
  1147. "qadf" => "none", // @ todo fix (??)
  1148. ...(isset($page["token"]) ? ["page_token" => $page["token"]] : [])
  1149. ]
  1150. ),
  1151. "cookies" => $cookies
  1152. ]),
  1153. $pagetype,
  1154. $proxy
  1155. );
  1156. break;
  1157. }
  1158. }
  1159. return null;
  1160. }
  1161. private function remove_tracking_link($url){
  1162. // https://api.airfind.com/stats/adrequest/v1?clientid=51776&type=click&adCampaign=organicNewsArticle-prg-top&provider=prg&redirectURL=https%3A%2F%2Fvancouverisland.ctvnews.ca%2Fvideo%2F2026%2F11%2F02%2Ftrump-claims-concept-of-a-deal-with-canada%2F&brand=startpage.udog
  1163. $query = parse_url($url);
  1164. if(
  1165. !isset($query["host"]) ||
  1166. $query["host"] != "api.airfind.com" ||
  1167. !isset($query["query"])
  1168. ){
  1169. return $url;
  1170. }
  1171. parse_str($query["query"], $res);
  1172. if(isset($res["redirectURL"])){
  1173. return $res["redirectURL"];
  1174. }
  1175. return $url;
  1176. }
  1177. private function unshitimage($url){
  1178. $query = parse_url($url, PHP_URL_QUERY);
  1179. parse_str($query, $query);
  1180. if(isset($query["piurl"])){
  1181. if(strpos($query["piurl"], "gstatic.com/")){
  1182. return
  1183. explode(
  1184. "&",
  1185. $query["piurl"],
  1186. 2
  1187. )[0];
  1188. }
  1189. if(
  1190. strpos($query["piurl"], "bing.net/") ||
  1191. strpos($query["piurl"], "bing.com/")
  1192. ){
  1193. return
  1194. explode(
  1195. "&",
  1196. $query["piurl"],
  1197. 2
  1198. )[0];
  1199. }
  1200. return $query["piurl"];
  1201. }
  1202. return $url;
  1203. }
  1204. private function limitstrlen($text){
  1205. return
  1206. explode(
  1207. "\n",
  1208. wordwrap(
  1209. str_replace(
  1210. ["\n\r", "\r\n", "\n", "\r"],
  1211. " ",
  1212. $text
  1213. ),
  1214. 300,
  1215. "\n"
  1216. ),
  1217. 2
  1218. )[0];
  1219. }
  1220. private function titledots($title){
  1221. return trim($title, " .\t\n\r\0\x0B…");
  1222. }
  1223. private function hms2int($time){
  1224. $parts = explode(":", $time, 3);
  1225. $time = 0;
  1226. if(count($parts) === 3){
  1227. // hours
  1228. $time = $time + ((int)$parts[0] * 3600);
  1229. array_shift($parts);
  1230. }
  1231. if(count($parts) === 2){
  1232. // minutes
  1233. $time = $time + ((int)$parts[0] * 60);
  1234. array_shift($parts);
  1235. }
  1236. // seconds
  1237. $time = $time + (int)$parts[0];
  1238. return $time;
  1239. }
  1240. private function remove_penguins($text){
  1241. return str_replace(
  1242. ["", ""],
  1243. "",
  1244. $text
  1245. );
  1246. }
  1247. private function detect_captcha($html){
  1248. $this->fuckhtml->load($html);
  1249. $title =
  1250. $this->fuckhtml
  1251. ->getElementsByTagName(
  1252. "title"
  1253. );
  1254. if(
  1255. count($title) !== 0 &&
  1256. $title[0]["innerHTML"] == "Redirecting..."
  1257. ){
  1258. // check if it's a captcha
  1259. $as =
  1260. $this->fuckhtml
  1261. ->getElementsByTagName(
  1262. "a"
  1263. );
  1264. foreach($as as $a){
  1265. if(
  1266. strpos(
  1267. $this->fuckhtml
  1268. ->getTextContent(
  1269. $a["innerHTML"]
  1270. ),
  1271. "https://www.startpage.com/sp/captcha"
  1272. ) !== false
  1273. ){
  1274. throw new Exception("Startpage returned a captcha");
  1275. }
  1276. }
  1277. throw new Exception("Startpage redirected the scraper to an unhandled page");
  1278. }
  1279. }
  1280. private function solve_anubis($proxy, $html){
  1281. //
  1282. // solve anubis challenge, if theres one
  1283. //
  1284. $res = null;
  1285. $cookies = [];
  1286. try{
  1287. $res = $this->anubis->scrape($html["data"]);
  1288. }catch(Exception $error){
  1289. // do nothang
  1290. $this->anubis_error = $error->getMessage();
  1291. }
  1292. if($res !== null){
  1293. // https://www.startpage.com/.within.website/x/cmd/anubis/api/pass-challenge
  1294. // ?id=01a00241-da22-7d62-b64f-e7a6a0e8bda8
  1295. // &response=0000460826adc23e0363e97e713e4aa36ca46e1a7cd933184442772f3d6b994c
  1296. // &nonce=3098
  1297. // &redir=https%3A%2F%2Fwww.startpage.com%2Fsp%2Fsearch
  1298. // &elapsedTime=111
  1299. $anubis_response =
  1300. $this->get(
  1301. $proxy,
  1302. "https://www.startpage.com/.within.website/x/cmd/anubis/api/pass-challenge",
  1303. [
  1304. "id" => $res["id"],
  1305. "response" => $res["response"],
  1306. "nonce" => $res["nonce"],
  1307. "redir" => "https://www.startpage.com/sp/search",
  1308. "elapsedTime" => $res["elapsedTime"]
  1309. ],
  1310. false,
  1311. false,
  1312. true,
  1313. $html["cookies"]
  1314. );
  1315. // re-fetch search page AGAIN with the anubis bullshit
  1316. $cookies = array_merge($anubis_response["cookies"], $html["cookies"]);
  1317. return $cookies;
  1318. }
  1319. return false;
  1320. }
  1321. }