Source code for langchain_community.utilities.serpapi
"""Chain that calls SerpAPI.Heavily borrowed from https://github.com/ofirpress/self-ask"""importosimportsysfromtypingimportAny,Dict,Optional,Tupleimportaiohttpfromlangchain_core.utilsimportget_from_dict_or_envfrompydanticimportBaseModel,ConfigDict,Field,model_validator
[docs]classHiddenPrints:"""Context manager to hide prints."""def__enter__(self)->None:"""Open file to pipe stdout to."""self._original_stdout=sys.stdoutsys.stdout=open(os.devnull,"w")def__exit__(self,*_:Any)->None:"""Close file that stdout was piped to."""sys.stdout.close()sys.stdout=self._original_stdout
[docs]classSerpAPIWrapper(BaseModel):"""Wrapper around SerpAPI. To use, you should have the ``google-search-results`` python package installed, and the environment variable ``SERPAPI_API_KEY`` set with your API key, or pass `serpapi_api_key` as a named parameter to the constructor. Example: .. code-block:: python from langchain_community.utilities import SerpAPIWrapper serpapi = SerpAPIWrapper() """search_engine:Any=None#: :meta private:params:dict=Field(default={"engine":"google","google_domain":"google.com","gl":"us","hl":"en",})serpapi_api_key:Optional[str]=Noneaiosession:Optional[aiohttp.ClientSession]=Nonemodel_config=ConfigDict(arbitrary_types_allowed=True,extra="forbid",)@model_validator(mode="before")@classmethoddefvalidate_environment(cls,values:Dict)->Any:"""Validate that api key and python package exists in environment."""serpapi_api_key=get_from_dict_or_env(values,"serpapi_api_key","SERPAPI_API_KEY")values["serpapi_api_key"]=serpapi_api_keytry:fromserpapiimportGoogleSearchvalues["search_engine"]=GoogleSearchexceptImportError:raiseImportError("Could not import serpapi python package. ""Please install it with `pip install google-search-results`.")returnvalues
[docs]asyncdefarun(self,query:str,**kwargs:Any)->str:"""Run query through SerpAPI and parse result async."""returnself._process_response(awaitself.aresults(query))
[docs]defrun(self,query:str,**kwargs:Any)->str:"""Run query through SerpAPI and parse result."""returnself._process_response(self.results(query))
[docs]defresults(self,query:str)->dict:"""Run query through SerpAPI and return the raw result."""params=self.get_params(query)withHiddenPrints():search=self.search_engine(params)res=search.get_dict()returnres
[docs]asyncdefaresults(self,query:str)->dict:"""Use aiohttp to run query through SerpAPI and return the results async."""defconstruct_url_and_params()->Tuple[str,Dict[str,str]]:params=self.get_params(query)params["source"]="python"ifself.serpapi_api_key:params["serp_api_key"]=self.serpapi_api_keyparams["output"]="json"url="https://serpapi.com/search"returnurl,paramsurl,params=construct_url_and_params()ifnotself.aiosession:asyncwithaiohttp.ClientSession()assession:asyncwithsession.get(url,params=params)asresponse:res=awaitresponse.json()else:asyncwithself.aiosession.get(url,params=params)asresponse:res=awaitresponse.json()returnres
[docs]defget_params(self,query:str)->Dict[str,str]:"""Get parameters for SerpAPI."""_params={"api_key":self.serpapi_api_key,"q":query,}params={**self.params,**_params}returnparams
@staticmethoddef_process_response(res:dict)->str:"""Process response from SerpAPI."""if"error"inres.keys():raiseValueError(f"Got error from SerpAPI: {res['error']}")if"answer_box_list"inres.keys():res["answer_box"]=res["answer_box_list"]if"answer_box"inres.keys():answer_box=res["answer_box"]ifisinstance(answer_box,list):answer_box=answer_box[0]if"result"inanswer_box.keys():returnanswer_box["result"]elif"answer"inanswer_box.keys():returnanswer_box["answer"]elif"snippet"inanswer_box.keys():returnanswer_box["snippet"]elif"snippet_highlighted_words"inanswer_box.keys():returnanswer_box["snippet_highlighted_words"]else:answer={}forkey,valueinanswer_box.items():ifnotisinstance(value,(list,dict))andnot(isinstance(value,str)andvalue.startswith("http")):answer[key]=valuereturnstr(answer)elif"events_results"inres.keys():returnres["events_results"][:10]elif"sports_results"inres.keys():returnres["sports_results"]elif"top_stories"inres.keys():returnres["top_stories"]elif"news_results"inres.keys():returnres["news_results"]elif"jobs_results"inres.keys()and"jobs"inres["jobs_results"].keys():returnres["jobs_results"]["jobs"]elif("shopping_results"inres.keys()and"title"inres["shopping_results"][0].keys()):returnres["shopping_results"][:3]elif"questions_and_answers"inres.keys():returnres["questions_and_answers"]elif("popular_destinations"inres.keys()and"destinations"inres["popular_destinations"].keys()):returnres["popular_destinations"]["destinations"]elif"top_sights"inres.keys()and"sights"inres["top_sights"].keys():returnres["top_sights"]["sights"]elif("images_results"inres.keys()and"thumbnail"inres["images_results"][0].keys()):returnstr([item["thumbnail"]foriteminres["images_results"][:10]])snippets=[]if"knowledge_graph"inres.keys():knowledge_graph=res["knowledge_graph"]title=knowledge_graph["title"]if"title"inknowledge_graphelse""if"description"inknowledge_graph.keys():snippets.append(knowledge_graph["description"])forkey,valueinknowledge_graph.items():if(isinstance(key,str)andisinstance(value,str)andkeynotin["title","description"]andnotkey.endswith("_stick")andnotkey.endswith("_link")andnotvalue.startswith("http")):snippets.append(f"{title}{key}: {value}.")fororganic_resultinres.get("organic_results",[]):if"snippet"inorganic_result.keys():snippets.append(organic_result["snippet"])elif"snippet_highlighted_words"inorganic_result.keys():snippets.append(organic_result["snippet_highlighted_words"])elif"rich_snippet"inorganic_result.keys():snippets.append(organic_result["rich_snippet"])elif"rich_snippet_table"inorganic_result.keys():snippets.append(organic_result["rich_snippet_table"])elif"link"inorganic_result.keys():snippets.append(organic_result["link"])if"buying_guide"inres.keys():snippets.append(res["buying_guide"])if"local_results"inresandisinstance(res["local_results"],list):snippets+=res["local_results"]if("local_results"inres.keys()andisinstance(res["local_results"],dict)and"places"inres["local_results"].keys()):snippets.append(res["local_results"]["places"])iflen(snippets)>0:returnstr(snippets)else:return"No good search result found"