Python 如何包装所有BeautifulSoup现有的find/select方法以添加其他逻辑和参数?

Python 如何包装所有BeautifulSoup现有的find/select方法以添加其他逻辑和参数?,python,python-3.x,beautifulsoup,Python,Python 3.x,Beautifulsoup,对于大多数对BeautifulSoup对象的调用,我都有一个重复的健全性检查过程,其中我: 进行函数调用.find、.find_all、.select_one和.select moly 检查以确保找到元素 如果未找到,我将引发一个自定义MissingHTMLTagError,并在那里停止进程。 尝试使用.get或getattr从元素中检索属性 如果未找到,我将引发一个自定义missingHtmlatTributeTerror 返回一个: 字符串,当它是单个元素的单个属性时。查找并选择一个 字符串

对于大多数对BeautifulSoup对象的调用,我都有一个重复的健全性检查过程,其中我:

进行函数调用.find、.find_all、.select_one和.select moly 检查以确保找到元素 如果未找到,我将引发一个自定义MissingHTMLTagError,并在那里停止进程。 尝试使用.get或getattr从元素中检索属性 如果未找到,我将引发一个自定义missingHtmlatTributeTerror 返回一个: 字符串,当它是单个元素的单个属性时。查找并选择一个 字符串列表,当它是多个元素的单个属性时。查找所有元素并选择 当它是多个元素的两个属性键/值对时,dict。find_all and。select 我已经创建了下面的解决方案,它作为一个代理,对美化组方法来说并不那么优雅。 但是,我希望有一个更容易做到这一点。 基本上,我希望能够将所有BeautifulSoup方法修补为:

允许传递一个额外的参数,以便在一次调用中完成上述步骤 如果在不提供额外参数的情况下使用上述任何一种方法,我希望像正常情况一样返回BeautifulSoup对象,或者在返回值为None或空列表时引发MissingHTMLTagError。 大多数情况下,下面的函数与类变量self.\u soup一起使用,它只是最近requests.Response的一个BeautifulSoup对象

从bs4导入BeautifulSoup def get_html_valueself,元素,attribute=None,soup=None,func=find,**kwargs: 返回html元素属性的一步方法。 一个代理函数,用于处理将参数传递到对象实例的过程 在减少获取元素所需的样板代码量的同时,验证其存在性, 然后对该元素的属性执行相同的操作。同时管理为调试引发适当的异常。 **示例:** 使用BeautifulSoup.find从单个元素获取单个属性 >>self.get_html_valuea,href,attrs={class:report list} >>example.com/page 使用BeautifulSoup.find_all从多个元素中获取单个属性 >>self.get\u html\u valuea,href,func=find\u all,attrs={class:top nav link} >>[example.com/category1,example.com/category2,example.com/category3] 获取表示POST请求的隐藏输入字段的键/值对 来自仅包含表单控件的完整html页面登录表单的片段 >>self.get\u html\u valueinput,name,value,soup=login\u form,func=find\u all,attrs={type:hidden} >>{csrf_令牌:a1b23c456def,视图状态:wxyzqwerty} 使用func=select\u one查找基于其父元素之一的元素 >>account\u balance=self.get\u html\u valuedivaccount-details>section>h1,func=select\u one >>account\u balance.string >> $12,345.67 使用不带属性的func=select将返回BeautifulSoup对象 >>self.get\u html\u valuedivaccounts>div a,func=select >> [, ] 使用func=select with属性将返回值列表 >>self.get\u html\u valuedivaccounts>diva,attribute=href,func=select >>[example.com/account1,example.com/account2,example.com/account3] 如果没有[汤,自己._汤]: 未设置raise ValueErrorClass属性soup,未提供soup参数 艾利夫汤: 提供字符串和请求的解析。响应 如果isinstancesoup,str: soup=BeautifulSoupsoup,html.parser elif isinstancesoup,请求。响应: soup=BeautifulSoupsoup.text,html.parser 其他: 汤=self.\u汤 如果不是isinstanceattribute、str、tuple: raise TypeErrorattribute只能是字符串或元组 如果isinstanceattribute、元组和lenattribute!=2: raise ValueErrorattribute只能是一个字符串或两个字符串的元组键/值对 bs_func=getattrsoup,func 如果不是bs_func: 在BeautifulSoup包%func中找不到raise AttributeErrorMethod%s bs_元素=bs_funcelement,**kwargs如果kwargs其他bs_funcelement 如果不是bs_元素: raise MissingHtmlErrorself、元素、无、汤、func、kwargs 如果属性: 如果isinstanceattribute,则str: 处理汤。查找和汤。选择一个 如果isinstancebs_元素,请列出: 多个元素的单个属性 bs_属性=[] 对于bs_元件中的el: el_attribute=el.getattribute 如果不是el_属性: raise MissingHtmlErrorself、元素、属性、soup、func、kwargs bs_attributes.appendel_属性 返回bs_属性 其他: 单个元素的单个属性 bs_attribute=bs_element.getattribute 如果不是bs_属性: raise MissingHtmlErrorself、元素、属性、soup、func、kwargs 返回bs_属性 其他: 处理soup.find_all和soup.select 键,值=属性 如果isinstancebs_元素,请列出: 多个元素的属性对 bs_属性={} 对于bs_元件中的el: el_key=el.getkey 如果el_键为无: raise MissingHtmlErrorself、元素、属性、soup、func、kwargs bs_属性[el_键]=el.getvalue, 返回bs_属性 其他: 单个元素的属性对 el_key=bs_element.getkey 如果el_键为无: raise MissingHtmlErrorself、元素、属性、soup、func、kwargs 返回{el_key:bs_element.getvalue,} 没有提供属性,因此返回请求的元素 返回bs_元素
是否有任何方法可以包装BeautifulSoup的所有暴露的.find和.select类型方法,这样我仍然可以正常使用这些方法,例如:soup.find,而不必使用我的变通功能?

我相信我已经找到了一种简洁合理的方法,可以用以下包装器完成我想要的功能:

从bs4导入BeautifulSoup 从functools导入包装 导入请求 进口检验 进口abc 类HtmlParserErrorException: 通过 类丢失HtmlTagerRorException: 通过 类丢失HtmlAttributeErrorException: 通过 类MyClassmetaclass=abc.ABCMeta: 定义初始自我: self.\u sess=requests.Session self.\u sess.hook[response].appendself.\u session\u hook self.\u resp=无 self.\u汤=无 def_session_hookself,response,*args,**kwargs: 隐式设置私有实例变量以实现无缝状态跟踪和更少的样板代码 self.\u resp=响应 如果html在self.\u resp.header[内容类型]中: 自备汤 def_包装的汤,汤: def汤_包装fn: @wrapsfn def包裹汤*args,**kwargs: extract=kwargs.popextract,无 如果不是isinstanceextract、str、tuple、typeNone: raise TypeErrorextract只能是None、str或tuple类型 elif isinstanceextract、元组和lenextract!=2: raise TypeErrorextract元组只能包含两个值;键/值对 元素=fn*args,**kwargs 如果不是元素: 提升缺失HtmlTagerRor elif非摘录: 返回元素 elif isinstanceelements,列表: 处理'soup.find_all'和'soup.select` 如果isinstanceextract,str: 多个元素的单个属性 attribs=列表 对于el in元件: 包括元素属性以及诸如“.string”之类的属性` el_attrib=el.getextract或getattrel,extract 如果不是el_attrib: 提升缺失HTMLATTRIBUTEERROR attribs.appendel_attrib 返回属性 其他: 多个元素的属性对 attribs=dict 键,值=提取 对于el in元件: el_key=el.getkey 如果el_键为无: 提升缺失HTMLATTRIBUTEERROR attribs[el_key]=el.getvalue, 返回属性 其他: 如果isinstanceextract,str: 单个元素的单个属性 包括元素属性以及诸如“.string”之类的属性` attrib=elements.getextract或getattrel,extract 如果不是attrib: 提升缺失HTMLATTRIBUTEERROR 返回属性 其他: 单个元素的属性对 键,值=提取 el_key=elements.getkey 如果el_键为无: 提升缺失HTMLATTRIBUTEERROR 返回{el_key:elements.getvalue,} 还包汤 包装以“查找”或“选择”开头的所有方法 适用函数=[ 在汤里用f代替f 如果f.startswithfind 或f.startswithselect ] 对于适用函数中的函数: setattrsoup,func,sou p_wrappergetattrsoup,func 返汤 def get_soupself: 尝试: self.\u soup=self.\u wrapped\u soupBeautifulSoupself.\u resp.text,html.parser 除HTMLPasserError外: 因为这是隐含的,我们需要优雅地失败 self.logger.warning无法分析其内容类型标题设置为text/html的响应 通过 cls=MyClass cls.\u sess。gethttps://www.example.com test=cls.\u soup.finda,extract=href 打印测试:,测试