htmlparser.py 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. #coding:utf8
  2. import re
  3. from BaseDataMaintenance.maintenance.product.productUtils import is_similar
  4. import logging
  5. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  6. logger = logging.getLogger(__name__)
  7. logger.setLevel(logging.DEBUG)
  8. from bs4 import BeautifulSoup
  9. import copy
  10. end_pattern = "商务要求|评分标准|商务条件|商务条件"
  11. _param_pattern = "(产品|技术|清单|配置|参数|具体|明细|项目|招标|货物|服务|规格|工作|具体)[及和与]?(指标|配置|条件|要求|参数|需求|规格|条款|名称及要求)|配置清单|(质量|技术).{,10}要求|验收标准|^(参数|功能)$"
  12. meter_pattern = "[><≤≥±]\d+|\d+(?:[μucmkK微毫千]?[米升LlgGmMΩ]|摄氏度|英寸|度|天|VA|dB|bpm|rpm|kPa|mol|cmH20|%|°|Mpa|Hz|K?HZ|℃|W|min|[*×xX])|[*×xX]\d+|/min|\ds[^a-zA-Z]|GB.{,20}标准|PVC|PP|角度|容积|色彩|自动|流量|外径|轴位|折射率|帧率|柱镜|振幅|磁场|镜片|防漏|强度|允差|心率|倍数|瞳距|底座|色泽|噪音|间距|材质|材料|表面|频率|阻抗|浓度|兼容|防尘|防水|内径|实时|一次性|误差|性能|距离|精确|温度|超温|范围|跟踪|对比度|亮度|[横纵]向|均压|负压|正压|可调|设定值|功能|检测|高度|厚度|宽度|深度|[单双多]通道|效果|指数|模式|尺寸|重量|峰值|谷值|容量|寿命|稳定性|高温|信号|电源|电流|转换率|效率|释放量|转速|离心力|向心力|弯曲|电压|功率|气量|国标|标准协议|灵敏度|最大值|最小值|耐磨|波形|高压|性强|工艺|光源|低压|压力|压强|速度|湿度|重量|毛重|[MLX大中小]+码|净重|颜色|[红橙黄绿青蓝紫]色|不锈钢|输入|输出|噪声|认证|配置"
  13. not_meter_pattern = "投标报价|中标金额|商务部分|公章|分值构成|业绩|详见|联系人|联系电话|合同价|金额|采购预算|资金来源|费用|质疑|评审因素|评审标准|商务资信|商务评分|专家论证意见|评标方法|代理服务费|售后服务|评分类型|评分项目|预算金额|得\d+分|项目金额|详见招标文件|乙方"
  14. def getTrs(tbody):
  15. #获取所有的tr
  16. trs = []
  17. if tbody.name=="table":
  18. body = tbody.find("tbody",recursive=False)
  19. if body is not None:
  20. tbody = body
  21. objs = tbody.find_all(recursive=False)
  22. for obj in objs:
  23. if obj.name=="tr":
  24. trs.append(obj)
  25. if obj.name=="tbody" or obj.name=="table":
  26. for tr in obj.find_all("tr",recursive=False):
  27. trs.append(tr)
  28. return trs
  29. def fixSpan(tbody):
  30. # 处理colspan, rowspan信息补全问题
  31. #trs = tbody.findChildren('tr', recursive=False)
  32. trs = getTrs(tbody)
  33. ths_len = 0
  34. ths = list()
  35. trs_set = set()
  36. #修改为先进行列补全再进行行补全,否则可能会出现表格解析混乱
  37. # 遍历每一个tr
  38. for indtr, tr in enumerate(trs):
  39. ths_tmp = tr.findChildren('th', recursive=False)
  40. #不补全含有表格的tr
  41. if len(tr.findChildren('table'))>0:
  42. continue
  43. if len(ths_tmp) > 0:
  44. ths_len = ths_len + len(ths_tmp)
  45. for th in ths_tmp:
  46. ths.append(th)
  47. trs_set.add(tr)
  48. # 遍历每行中的element
  49. tds = tr.findChildren(recursive=False)
  50. for indtd, td in enumerate(tds):
  51. # 若有colspan 则补全同一行下一个位置
  52. if 'colspan' in td.attrs:
  53. if str(re.sub("[^0-9]","",str(td['colspan'])))!="":
  54. col = int(re.sub("[^0-9]","",str(td['colspan'])))
  55. if col<100 and len(td.get_text())<1000:
  56. td['colspan'] = 1
  57. for i in range(1, col, 1):
  58. td.insert_after(copy.copy(td))
  59. for indtr, tr in enumerate(trs):
  60. ths_tmp = tr.findChildren('th', recursive=False)
  61. #不补全含有表格的tr
  62. if len(tr.findChildren('table'))>0:
  63. continue
  64. if len(ths_tmp) > 0:
  65. ths_len = ths_len + len(ths_tmp)
  66. for th in ths_tmp:
  67. ths.append(th)
  68. trs_set.add(tr)
  69. # 遍历每行中的element
  70. tds = tr.findChildren(recursive=False)
  71. for indtd, td in enumerate(tds):
  72. # 若有rowspan 则补全下一行同样位置
  73. if 'rowspan' in td.attrs:
  74. if str(re.sub("[^0-9]","",str(td['rowspan'])))!="":
  75. row = int(re.sub("[^0-9]","",str(td['rowspan'])))
  76. td['rowspan'] = 1
  77. for i in range(1, row, 1):
  78. # 获取下一行的所有td, 在对应的位置插入
  79. if indtr+i<len(trs):
  80. tds1 = trs[indtr + i].findChildren(['td','th'], recursive=False)
  81. if len(tds1) >= (indtd) and len(tds1)>0:
  82. if indtd > 0:
  83. tds1[indtd - 1].insert_after(copy.copy(td))
  84. else:
  85. tds1[0].insert_before(copy.copy(td))
  86. elif indtd-2>0 and len(tds1) > 0 and len(tds1) == indtd - 1: # 修正某些表格最后一列没补全
  87. tds1[indtd-2].insert_after(copy.copy(td))
  88. def getTable(tbody):
  89. #trs = tbody.findChildren('tr', recursive=False)
  90. fixSpan(tbody)
  91. trs = getTrs(tbody)
  92. inner_table = []
  93. for tr in trs:
  94. tr_line = []
  95. tds = tr.findChildren(['td','th'], recursive=False)
  96. if len(tds)==0:
  97. tr_line.append([re.sub('\xa0','',tr.get_text()),0]) # 2021/12/21 修复部分表格没有td 造成数据丢失
  98. for td in tds:
  99. tr_line.append([re.sub('\xa0','',td.get_text()),0])
  100. #tr_line.append([td.get_text(),0])
  101. inner_table.append(tr_line)
  102. return inner_table
  103. class ParseDocument():
  104. def __init__(self,_html,auto_merge_table=True):
  105. if _html is None:
  106. _html = ""
  107. self.html = _html
  108. # self.soup = BeautifulSoup(self.html,"lxml")
  109. # self.soup = BeautifulSoup(self.html,"html.parser")
  110. self.auto_merge_table = auto_merge_table
  111. self.soup = BeautifulSoup(self.html,"html5lib")
  112. _body = self.soup.find("body")
  113. if _body is not None:
  114. self.soup = _body
  115. self.list_obj = self.get_soup_objs(self.soup)
  116. # for obj in self.list_obj:
  117. # print("obj",obj.get_text()[:20])
  118. self.tree = self.buildParsetree(self.list_obj,[],auto_merge_table)
  119. # #识别目录树
  120. # if self.parseTree:
  121. # self.parseTree.printParseTree()
  122. def get_soup_objs(self,soup,list_obj=None):
  123. if list_obj is None:
  124. list_obj = []
  125. childs = soup.find_all(recursive=False)
  126. for _obj in childs:
  127. childs1 = _obj.find_all(recursive=False)
  128. if len(childs1)==0 or len(_obj.get_text())<40 or _obj.name=="table":
  129. list_obj.append(_obj)
  130. else:
  131. self.get_soup_objs(_obj,list_obj)
  132. return list_obj
  133. def fix_tree(self,_product):
  134. products = extract_products(self.tree,_product)
  135. if len(products)>0:
  136. self.tree = self.buildParsetree(self.list_obj,products,self.auto_merge_table)
  137. def print_tree(self,tree,append=""):
  138. if append=="":
  139. self.set_tree_id = set()
  140. # for t in tree:
  141. # logger.debug("%s text:%s title:%s title_text:%s before:%s after%s product:%s"%("==>",t["text"][:50],t["sentence_title"],t["sentence_title_text"],t["title_before"],t["title_after"],t["has_product"]))
  142. for t in tree:
  143. _id = id(t)
  144. if _id in self.set_tree_id:
  145. continue
  146. self.set_tree_id.add(_id)
  147. logger.debug("%s text:%s title:%s title_text:%s before:%s after%s product:%s"%(append,t["text"][:50],t["sentence_title"],t["sentence_title_text"],t["title_before"],t["title_after"],t["has_product"]))
  148. childs = t["child_title"]
  149. self.print_tree(childs,append=append+"-|")
  150. def is_title_first(self,title):
  151. if title in ("一","1","Ⅰ","a","A"):
  152. return True
  153. return False
  154. def find_title_by_pattern(self,_text,_pattern="(^|★|▲|:|:|\s+)(?P<title_1>(?P<title_1_index_0_0>第?)(?P<title_1_index_1_1>[一二三四五六七八九十ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]+)(?P<title_1_index_2_0>[、章册包标部.::、、]+))|" \
  155. "([\s★▲\*]*)(?P<title_3>(?P<title_3_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?)(?P<title_3_index_0_1>[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]+)(?P<title_3_index_0_2>[、章册包标部.::、、]+))|" \
  156. "([\s★▲\*]*)(?P<title_4>(?P<title_4_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?第?)(?P<title_4_index_1_1>[一二三四五六七八九十]+)(?P<title_4_index_2_0>[节章册部\.::、、]+))|" \
  157. "([\s★▲\*]*)(?P<title_5>(?P<title_5_index_0_0>^)(?P<title_5_index_1_1>[一二三四五六七八九十]+)(?P<title_5_index_2_0>)[^一二三四五六七八九十节章册部\.::、、])|" \
  158. "([\s★▲\*]*)(?P<title_12>(?P<title_12_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-])(?P<title_12_index_1_1>\d{1,2})(?P<title_12_index_2_0>[\..、\s\-]?))|"\
  159. "([\s★▲\*]*)(?P<title_11>(?P<title_11_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-])(?P<title_11_index_1_1>\d{1,2})(?P<title_11_index_2_0>[\..、\s\-]?))|" \
  160. "([\s★▲\*]*)(?P<title_10>(?P<title_10_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-])(?P<title_10_index_1_1>\d{1,2})(?P<title_10_index_2_0>[\..、\s\-]?))|" \
  161. "([\s★▲\*]*)(?P<title_7>(?P<title_7_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..\s\-])(?P<title_7_index_1_1>\d{1,2})(?P<title_7_index_2_0>[\..包标::、\s\-]*))|" \
  162. "(^[\s★▲\*]*)(?P<title_6>(?P<title_6_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?包?)(?P<title_6_index_0_1>\d{1,2})(?P<title_6_index_2_0>[\..、\s\-包标]*))|" \
  163. "([\s★▲\*]*)(?P<title_15>(?P<title_15_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?[((]?)(?P<title_15_index_1_1>\d{1,2})(?P<title_15_index_2_0>[))包标\..::、]+))|" \
  164. "([\s★▲\*]*)(?P<title_17>(?P<title_17_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?[((]?)(?P<title_17_index_1_1>[a-zA-Z]+)(?P<title_17_index_2_0>[))包标\..::、]+))|" \
  165. "([\s★▲\*]*)(?P<title_19>(?P<title_19_index_0_0>[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?[((]?)(?P<title_19_index_1_1>[一二三四五六七八九十ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]+)(?P<title_19_index_2_0>[))]))"
  166. ):
  167. _se = re.search(_pattern,_text)
  168. groups = []
  169. if _se is not None:
  170. _gd = _se.groupdict()
  171. for k,v in _gd.items():
  172. if v is not None:
  173. groups.append((k,v))
  174. if len(groups):
  175. # groups.sort(key=lambda x:x[0])
  176. return groups
  177. return None
  178. def make_increase(self,_sort,_title,_add=1):
  179. if len(_title)==0 and _add==0:
  180. return ""
  181. if len(_title)==0 and _add==1:
  182. return _sort[0]
  183. _index = _sort.index(_title[-1])
  184. next_index = (_index+_add)%len(_sort)
  185. next_chr = _sort[next_index]
  186. if _index==len(_sort)-1:
  187. _add = 1
  188. else:
  189. _add = 0
  190. return next_chr+self.make_increase(_sort,_title[:-1],_add)
  191. def get_next_title(self,_title):
  192. if re.search("^\d+$",_title) is not None:
  193. return str(int(_title)+1)
  194. if re.search("^[一二三四五六七八九十百]+$",_title) is not None:
  195. if _title[-1]=="十":
  196. return _title+"一"
  197. if _title[-1]=="百":
  198. return _title+"零一"
  199. if _title[-1]=="九":
  200. if len(_title)==1:
  201. return "十"
  202. if len(_title)==2:
  203. if _title[0]=="十":
  204. return "二十"
  205. if len(_title)==3:
  206. if _title[0]=="九":
  207. return "一百"
  208. else:
  209. _next_title = self.make_increase(['一','二','三','四','五','六','七','八','九','十'],re.sub("[十百]",'',_title[0]))
  210. return _next_title+"十"
  211. _next_title = self.make_increase(['一','二','三','四','五','六','七','八','九','十'],re.sub("[十百]",'',_title))
  212. _next_title = list(_next_title)
  213. _next_title.reverse()
  214. if _next_title[-1]!="十":
  215. if len(_next_title)>=2:
  216. _next_title.insert(-1,'十')
  217. if len(_next_title)>=4:
  218. _next_title.insert(-3,'百')
  219. if _title[0]=="十":
  220. if _next_title=="十":
  221. _next_title = ["二","十"]
  222. _next_title.insert(0,"十")
  223. _next_title = "".join(_next_title)
  224. return _next_title
  225. if re.search("^[a-z]+$",_title) is not None:
  226. _next_title = self.make_increase([chr(i+ord('a')) for i in range(26)],_title)
  227. _next_title = list(_next_title)
  228. _next_title.reverse()
  229. return "".join(_next_title)
  230. if re.search("^[A-Z]+$",_title) is not None:
  231. _next_title = self.make_increase([chr(i+ord('A')) for i in range(26)],_title)
  232. _next_title = list(_next_title)
  233. _next_title.reverse()
  234. return "".join(_next_title)
  235. if re.search("^[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]$",_title) is not None:
  236. _sort = ["Ⅰ","Ⅱ","Ⅲ","Ⅳ","Ⅴ","Ⅵ","Ⅶ","Ⅷ","Ⅸ","Ⅹ","Ⅺ","Ⅻ"]
  237. _index = _sort.index(_title)
  238. if _index<len(_sort)-1:
  239. return _sort[_index+1]
  240. return None
  241. def count_title_before(self,list_obj):
  242. dict_before = {}
  243. dict_sentence_count = {}
  244. illegal_sentence = set()
  245. for obj_i in range(len(list_obj)):
  246. obj = list_obj[obj_i]
  247. _type = "sentence"
  248. _text = obj.text.strip()
  249. if obj.name=="table":
  250. _type = "table"
  251. _text = str(obj)
  252. _append = False
  253. if _type=="sentence":
  254. if len(_text)>10 and len(_text)<100:
  255. if _text not in dict_sentence_count:
  256. dict_sentence_count[_text] = 0
  257. dict_sentence_count[_text] += 1
  258. if re.search("\d+页",_text) is not None:
  259. illegal_sentence.add(_text)
  260. elif len(_text)<10:
  261. if re.search("第\d+页",_text) is not None:
  262. illegal_sentence.add(_text)
  263. sentence_groups = self.find_title_by_pattern(_text[:10])
  264. if sentence_groups:
  265. # c062f53cf83401e671822003d63c1828print("sentence_groups",sentence_groups)
  266. sentence_title = sentence_groups[0][0]
  267. sentence_title_text = sentence_groups[0][1]
  268. title_index = sentence_groups[-2][1]
  269. title_before = sentence_groups[1][1].replace("(","(").replace(":",":").replace(":",";").replace(",",".").replace(",",".").replace("、",".")
  270. title_after = sentence_groups[-1][1].replace(")",")").replace(":",":").replace(":",";").replace(",",".").replace(",",".").replace("、",".")
  271. next_index = self.get_next_title(title_index)
  272. if title_before not in dict_before:
  273. dict_before[title_before] = 0
  274. dict_before[title_before] += 1
  275. for k,v in dict_sentence_count.items():
  276. if v>10:
  277. illegal_sentence.add(k)
  278. return dict_before,illegal_sentence
  279. def is_page_no(self,sentence):
  280. if len(sentence)<10:
  281. if re.search("\d+页|^\-\d+\-$",sentence) is not None:
  282. return True
  283. def block_tree(self,childs):
  284. for child in childs:
  285. if not child["block"]:
  286. child["block"] = True
  287. childs2 = child["child_title"]
  288. self.block_tree(childs2)
  289. def buildParsetree(self,list_obj,products=[],auto_merge_table=True):
  290. self.parseTree = None
  291. trees = []
  292. list_length = []
  293. for obj in list_obj[:200]:
  294. if obj.name!="table":
  295. list_length.append(len(obj.get_text()))
  296. if len(list_length)>0:
  297. max_length = max(list_length)
  298. else:
  299. max_length = 40
  300. max_length = min(max_length,40)
  301. logger.debug("%s:%d"%("max_length",max_length))
  302. list_data = []
  303. last_table_index = None
  304. last_table_columns = None
  305. last_table = None
  306. dict_before,illegal_sentence = self.count_title_before(list_obj)
  307. for obj_i in range(len(list_obj)):
  308. obj = list_obj[obj_i]
  309. # logger.debug("==obj %s"%obj.text[:20])
  310. _type = "sentence"
  311. _text = standard_product(obj.text)
  312. if obj.name=="table":
  313. _type = "table"
  314. _text = standard_product(str(obj))
  315. _append = False
  316. sentence_title = None
  317. sentence_title_text = None
  318. sentence_groups = None
  319. title_index = None
  320. next_index = None
  321. parent_title = None
  322. title_before = None
  323. title_after = None
  324. title_next = None
  325. childs = []
  326. list_table = None
  327. block = False
  328. has_product = False
  329. if _type=="sentence":
  330. if _text in illegal_sentence:
  331. continue
  332. sentence_groups = self.find_title_by_pattern(_text[:10])
  333. if sentence_groups:
  334. title_before = standard_title_context(sentence_groups[1][1])
  335. title_after = sentence_groups[-1][1]
  336. sentence_title_text = sentence_groups[0][1]
  337. other_text = _text.replace(sentence_title_text,"")
  338. if (title_before in dict_before and dict_before[title_before]>1) or title_after!="":
  339. sentence_title = sentence_groups[0][0]
  340. title_index = sentence_groups[-2][1]
  341. next_index = self.get_next_title(title_index)
  342. other_text = _text.replace(sentence_title_text,"")
  343. for p in products:
  344. if other_text.strip()==p.strip():
  345. has_product = True
  346. else:
  347. _fix = False
  348. for p in products:
  349. if other_text.strip()==p.strip():
  350. title_before = "=产品"
  351. sentence_title = "title_0"
  352. sentence_title_text = p
  353. title_index = "0"
  354. title_after = "产品="
  355. next_index = "0"
  356. _fix = True
  357. has_product = True
  358. break
  359. if not _fix:
  360. title_before = None
  361. title_after = None
  362. sentence_title_text = None
  363. else:
  364. if len(_text)<40 and re.search(_param_pattern,_text) is not None:
  365. for p in products:
  366. if _text.find(p)>=0:
  367. title_before = "=产品"
  368. sentence_title = "title_0"
  369. sentence_title_text = p
  370. title_index = "0"
  371. title_after = "产品="
  372. next_index = "0"
  373. _fix = True
  374. has_product = True
  375. break
  376. if _type=="sentence":
  377. if sentence_title is None and len(list_data)>0 and list_data[-1]["sentence_title"] is not None and list_data[-1]["line_width"]>=max_length*0.6:
  378. list_data[-1]["text"] += _text
  379. list_data[-1]["line_width"] = len(_text)
  380. _append = True
  381. elif sentence_title is None and len(list_data)>0 and _type==list_data[-1]["type"]:
  382. if list_data[-1]["line_width"]>=max_length*0.7:
  383. list_data[-1]["text"] += _text
  384. list_data[-1]["line_width"] = len(_text)
  385. _append = True
  386. if _type=="table":
  387. _soup = BeautifulSoup(_text,"lxml")
  388. _table = _soup.find("table")
  389. if _table is not None:
  390. list_table = getTable(_table)
  391. table_columns = len(list_table[0])
  392. if auto_merge_table:
  393. if last_table_index is not None and abs(obj_i-last_table_index)<=2 and last_table_columns is not None and last_table_columns==table_columns:
  394. if last_table is not None:
  395. trs = getTrs(_table)
  396. last_tbody = BeautifulSoup(last_table["text"],"lxml")
  397. _table = last_tbody.find("table")
  398. last_trs = getTrs(_table)
  399. _append = True
  400. for _line in list_table:
  401. last_table["list_table"].append(_line)
  402. if len(last_trs)>0:
  403. for _tr in trs:
  404. last_trs[-1].insert_after(copy.copy(_tr))
  405. last_table["text"] = re.sub("</?html>|</?body>","",str(last_tbody))
  406. last_table_index = obj_i
  407. last_table_columns = len(list_table[-1])
  408. if not _append:
  409. _data = {"type":_type, "text":_text,"list_table":list_table,"line_width":len(_text),"sentence_title":sentence_title,"title_index":title_index,
  410. "sentence_title_text":sentence_title_text,"sentence_groups":sentence_groups,"parent_title":parent_title,
  411. "child_title":childs,"title_before":title_before,"title_after":title_after,"title_next":title_next,"next_index":next_index,
  412. "block":block,"has_product":has_product}
  413. if _type=="table":
  414. last_table = _data
  415. last_table_index = obj_i
  416. if list_table:
  417. last_table_columns = last_table_columns = len(list_table[-1])
  418. if sentence_title is not None:
  419. if len(list_data)>0:
  420. if self.is_title_first(title_index):
  421. for i in range(1,len(list_data)+1):
  422. _d = list_data[-i]
  423. if _d["sentence_title"] is not None:
  424. _data["parent_title"] = _d
  425. _d["child_title"].append(_data)
  426. break
  427. else:
  428. _find = False
  429. for i in range(1,len(list_data)+1):
  430. if _find:
  431. break
  432. _d = list_data[-i]
  433. if _d.get("sentence_title")==sentence_title and title_before==_d["title_before"] and title_after==_d["title_after"]:
  434. if _d["next_index"]==title_index and _d["title_next"] is None and not _d["block"]:
  435. _data["parent_title"] = _d["parent_title"]
  436. _d["title_next"] = _data
  437. if len(_d["child_title"])>0:
  438. _d["child_title"][-1]["title_next"] = ""
  439. self.block_tree(_d["child_title"])
  440. if _d["parent_title"] is not None:
  441. _d["parent_title"]["child_title"].append(_data)
  442. _find = True
  443. break
  444. for i in range(1,len(list_data)+1):
  445. if _find:
  446. break
  447. _d = list_data[-i]
  448. if i==1 and not _d["block"] and _d.get("sentence_title")==sentence_title and title_before==_d["title_before"] and title_after==_d["title_after"]:
  449. _data["parent_title"] = _d["parent_title"]
  450. _d["title_next"] = _data
  451. if len(_d["child_title"])>0:
  452. _d["child_title"][-1]["title_next"] = ""
  453. self.block_tree(_d["child_title"])
  454. if _d["parent_title"] is not None:
  455. _d["parent_title"]["child_title"].append(_data)
  456. _find = True
  457. break
  458. title_before = standard_title_context(title_before)
  459. title_after = standard_title_context(title_after)
  460. for i in range(1,len(list_data)+1):
  461. if _find:
  462. break
  463. _d = list_data[-i]
  464. if _d.get("sentence_title")==sentence_title and title_before==standard_title_context(_d["title_before"]) and title_after==standard_title_context(_d["title_after"]):
  465. if _d["next_index"]==title_index and _d["title_next"] is None and not _d["block"]:
  466. _data["parent_title"] = _d["parent_title"]
  467. _d["title_next"] = _data
  468. if len(_d["child_title"])>0:
  469. _d["child_title"][-1]["title_next"] = ""
  470. self.block_tree(_d["child_title"])
  471. if _d["parent_title"] is not None:
  472. _d["parent_title"]["child_title"].append(_data)
  473. _find = True
  474. break
  475. for i in range(1,len(list_data)+1):
  476. if _find:
  477. break
  478. _d = list_data[-i]
  479. if not _d["block"] and _d.get("sentence_title")==sentence_title and title_before==standard_title_context(_d["title_before"]) and title_after==standard_title_context(_d["title_after"]):
  480. _data["parent_title"] = _d["parent_title"]
  481. _d["title_next"] = _data
  482. if len(_d["child_title"])>0:
  483. _d["child_title"][-1]["title_next"] = ""
  484. # self.block_tree(_d["child_title"])
  485. if _d["parent_title"] is not None:
  486. _d["parent_title"]["child_title"].append(_data)
  487. _find = True
  488. break
  489. for i in range(1,min(len(list_data)+1,20)):
  490. if _find:
  491. break
  492. _d = list_data[-i]
  493. if not _d["block"] and _d.get("sentence_title")==sentence_title and title_before==standard_title_context(_d["title_before"]):
  494. _data["parent_title"] = _d["parent_title"]
  495. _d["title_next"] = _data
  496. if len(_d["child_title"])>0:
  497. _d["child_title"][-1]["title_next"] = ""
  498. # self.block_tree(_d["child_title"])
  499. if _d["parent_title"] is not None:
  500. _d["parent_title"]["child_title"].append(_data)
  501. _find = True
  502. break
  503. if not _find:
  504. if len(list_data)>0:
  505. for i in range(1,len(list_data)+1):
  506. _d = list_data[-i]
  507. if _d.get("sentence_title") is not None:
  508. _data["parent_title"] = _d
  509. _d["child_title"].append(_data)
  510. break
  511. else:
  512. if len(list_data)>0:
  513. for i in range(1,len(list_data)+1):
  514. _d = list_data[-i]
  515. if _d.get("sentence_title") is not None:
  516. _data["parent_title"] = _d
  517. _d["child_title"].append(_data)
  518. break
  519. list_data.append(_data)
  520. for _data in list_data:
  521. childs = _data["child_title"]
  522. for c_i in range(len(childs)):
  523. cdata = childs[c_i]
  524. if cdata["has_product"]:
  525. continue
  526. else:
  527. if c_i>0:
  528. last_cdata = childs[c_i-1]
  529. if cdata["sentence_title"] is not None and last_cdata["sentence_title"] is not None and last_cdata["title_before"]==cdata["title_before"] and last_cdata["title_after"]==cdata["title_after"] and last_cdata["has_product"]:
  530. cdata["has_product"] = True
  531. if c_i<len(childs)-1:
  532. last_cdata = childs[c_i+1]
  533. if cdata["sentence_title"] is not None and last_cdata["sentence_title"] is not None and last_cdata["title_before"]==cdata["title_before"] and last_cdata["title_after"]==cdata["title_after"] and last_cdata["has_product"]:
  534. cdata["has_product"] = True
  535. for c_i in range(len(childs)):
  536. cdata = childs[len(childs)-1-c_i]
  537. if cdata["has_product"]:
  538. continue
  539. else:
  540. if c_i>0:
  541. last_cdata = childs[c_i-1]
  542. if cdata["sentence_title"] is not None and last_cdata["sentence_title"] is not None and last_cdata["title_before"]==cdata["title_before"] and last_cdata["title_after"]==cdata["title_after"] and last_cdata["has_product"]:
  543. cdata["has_product"] = True
  544. if c_i<len(childs)-1:
  545. last_cdata = childs[c_i+1]
  546. if cdata["sentence_title"] is not None and last_cdata["sentence_title"] is not None and last_cdata["title_before"]==cdata["title_before"] and last_cdata["title_after"]==cdata["title_after"] and last_cdata["has_product"]:
  547. cdata["has_product"] = True
  548. return list_data
  549. def standard_title_context(_title_context):
  550. return _title_context.replace("(","(").replace(")",")").replace(":",":").replace(":",";").replace(",",".").replace(",",".").replace("、",".").replace(".",".")
  551. def standard_product(sentence):
  552. return sentence.replace("(","(").replace(")",")")
  553. def extract_products(list_data,_product,_param_pattern = "产品名称|设备材料|采购内存|标的名称|采购内容|(标的|维修|系统|报价构成|商品|产品|物料|物资|货物|设备|采购品|采购条目|物品|材料|印刷品?|采购|物装|配件|资产|耗材|清单|器材|仪器|器械|备件|拍卖物|标的物|物件|药品|药材|药械|货品|食品|食材|品目|^品名|气体|标项|分项|项目|计划|包组|标段|[分子]?包|子目|服务|招标|中标|成交|工程|招标内容)[\))的]?([、\w]{,4}名称|内容|描述)|标的|标项|项目$|商品|产品|物料|物资|货物|设备|采购品|采购条目|物品|材料|印刷品|物装|配件|资产|招标内容|耗材|清单|器材|仪器|器械|备件|拍卖物|标的物|物件|药品|药材|药械|货品|食品|食材|菜名|^品目$|^品名$|^名称|^内容$"):
  554. _product = standard_product(_product)
  555. list_result = []
  556. list_table_products = []
  557. for _data_i in range(len(list_data)):
  558. _data = list_data[_data_i]
  559. _type = _data["type"]
  560. _text = _data["text"]
  561. if _type=="table":
  562. list_table = _data["list_table"]
  563. if list_table is None:
  564. continue
  565. _check = True
  566. max_length = max([len(a) for a in list_table])
  567. min_length = min([len(a) for a in list_table])
  568. if min_length<max_length/2:
  569. continue
  570. list_head_index = []
  571. _begin_index = 0
  572. head_cell_text = ""
  573. for line_i in range(len(list_table[:2])):
  574. line = list_table[line_i]
  575. line_text = ",".join([cell[0] for cell in line])
  576. for cell_i in range(len(line)):
  577. cell = line[cell_i]
  578. cell_text = cell[0]
  579. if len(cell_text)<10 and re.search(_param_pattern,cell_text) is not None and re.search("单价|数量|预算|限价|总价|品牌|规格|型号|用途|要求|采购量",line_text) is not None:
  580. _begin_index = line_i+1
  581. list_head_index.append(cell_i)
  582. for line_i in range(len(list_table)):
  583. line = list_table[line_i]
  584. for cell_i in list_head_index:
  585. if cell_i>=len(line):
  586. continue
  587. cell = line[cell_i]
  588. cell_text = cell[0]
  589. head_cell_text += cell_text
  590. # print("===head_cell_text",head_cell_text)
  591. if re.search("招标人|采购人|项目编号|项目名称|金额|^\d+$",head_cell_text) is not None:
  592. list_head_index = []
  593. for line in list_table:
  594. line_text = ",".join([cell[0] for cell in line])
  595. for cell_i in range(len(line)):
  596. cell = line[cell_i]
  597. cell_text = cell[0]
  598. if cell_text is not None and _product is not None and len(cell_text)<len(_product)*10 and cell_text.find(_product)>=0 and re.search("单价|数量|总价|规格|品牌|型号|用途|要求|采购量",line_text) is not None:
  599. list_head_index.append(cell_i)
  600. list_head_index = list(set(list_head_index))
  601. if len(list_head_index)>0:
  602. has_number = False
  603. for cell_i in list_head_index:
  604. table_products = []
  605. for line_i in range(_begin_index,len(list_table)):
  606. line = list_table[line_i]
  607. for _i in range(len(line)):
  608. cell = line[_i]
  609. cell_text = cell[0]
  610. if re.search("^\d+$",cell_text) is not None:
  611. has_number = True
  612. if cell_i>=len(line):
  613. continue
  614. cell = line[cell_i]
  615. cell_text = cell[0]
  616. if re.search(_param_pattern,cell_text) is None or has_number:
  617. if re.search("^[\da-zA-Z]+$",cell_text) is None:
  618. table_products.append(cell_text)
  619. if len(table_products)>0:
  620. logger.debug("table products %s"%(str(table_products)))
  621. if min([len(x) for x in table_products])>0 and max([len(x) for x in table_products])<=30:
  622. if re.search("招标人|代理人|预算|数量|交货期|品牌|产地","".join(table_products)) is None:
  623. list_table_products.append(table_products)
  624. _find = False
  625. for table_products in list_table_products:
  626. for _p in table_products:
  627. if is_similar(_product,_p,90):
  628. _find = True
  629. logger.debug("similar table_products %s"%(str(table_products)))
  630. list_result = list(set([a for a in table_products if len(a)>1 and len(a)<20 and re.search("费用|预算|合计|金额|万元|运费|^其他$",a) is None]))
  631. break
  632. if not _find:
  633. for table_products in list_table_products:
  634. list_result.extend(table_products)
  635. list_result = list(set([a for a in list_result if len(a)>1 and len(a)<30 and re.search("费用|预算|合计|金额|万元|运费",a) is None]))
  636. return list_result
  637. def get_childs(childs):
  638. list_data = []
  639. for _child in childs:
  640. list_data.append(_child)
  641. childs2 = _child.get("child_title",[])
  642. if len(childs2)>0:
  643. for _child2 in childs2:
  644. list_data.extend(get_childs([_child2]))
  645. return list_data
  646. def get_range_data_by_childs(list_data,childs):
  647. range_data = []
  648. list_child = get_childs(childs)
  649. list_index = []
  650. set_child = set([id(x) for x in list_child])
  651. for _data_i in range(len(list_data)):
  652. _data = list_data[_data_i]
  653. _id = id(_data)
  654. if _id in set_child:
  655. list_index.append(_data_i)
  656. if len(list_index)>0:
  657. range_data = list_data[min(list_index):max(list_index)+1]
  658. return range_data
  659. def get_correct_product(product,products):
  660. list_data = []
  661. for p in products:
  662. is_sim = is_similar(product,p)
  663. _d = {"product":p,"distance":abs(len(product)-len(p)),"is_sim":is_sim}
  664. list_data.append(_d)
  665. list_data.sort(key=lambda x:x["distance"])
  666. for _d in list_data:
  667. is_sim = _d["is_sim"]
  668. if is_sim:
  669. if len(_d["product"])>len(product) and _d["product"].find(product)>=0:
  670. return product
  671. return _d["product"]
  672. return product
  673. def get_childs_text(childs,_product,products,is_begin=False,is_end=False):
  674. _text = ""
  675. end_next = False
  676. for _child in childs:
  677. child_text = _child.get("text")
  678. if child_text.find(_product)>=0:
  679. if not is_begin:
  680. is_begin = True
  681. if not end_next:
  682. if _child["sentence_title"] is not None and isinstance(_child["title_next"],dict) and _child["title_next"]["sentence_title"] is not None:
  683. end_next = True
  684. end_title = _child["title_next"]
  685. logger.debug("end_title %s "%end_title["text"])
  686. logger.debug("%s-%s-%s"%("get_childs_text",child_text[:10],str(is_begin)))
  687. for p in products:
  688. if child_text.find(p)>=0 and is_similar(_product,p,90):
  689. is_begin = True
  690. if child_text.find(_product)<0 and not is_similar(_product,p,80) and (child_text.find(p)>=0 or _child["has_product"]):
  691. if is_begin:
  692. is_end = True
  693. logger.debug("%s-%s-%s"%("get_childs_text end",child_text[:10],p))
  694. break
  695. if re.search(end_pattern,child_text) is not None:
  696. if is_begin:
  697. is_end = True
  698. logger.debug("%s-%s-%s"%("get_childs_text end",child_text[:10],str(is_end)))
  699. if is_begin and is_end:
  700. break
  701. if is_begin:
  702. _text += _child.get("text")+"\r\n"
  703. childs2 = _child.get("child_title",[])
  704. if len(childs2)>0:
  705. for _child2 in childs2:
  706. child_text,is_begin,is_end = get_childs_text([_child2],_product,products,is_begin)
  707. if is_begin:
  708. _text += child_text
  709. if is_end:
  710. break
  711. if end_next:
  712. is_end = True
  713. # logger.debug("%s-%s-%s"%("get_childs_text1",_text,str(is_begin)))
  714. # logger.debug("%s-%s-%s"%("get_childs_text2",_text,str(is_begin)))
  715. return _text,is_begin,is_end
  716. def extract_parameters_by_tree(_product,products,list_data,_data_i,parent_title,list_result,):
  717. _data = list_data[_data_i]
  718. childs = _data.get("child_title",[])
  719. if len(childs)>0:
  720. child_text,_,_ = get_childs_text([_data],_product,products)
  721. if len(child_text)>0:
  722. logger.info("extract_type by_tree child_text:%s"%child_text)
  723. list_result.append(child_text)
  724. if parent_title is not None:
  725. child_text,_,_ = get_childs_text([parent_title],_product,products)
  726. if len(child_text)>0:
  727. logger.info("extract_type by_tree child_text:%s"%child_text)
  728. list_result.append(child_text)
  729. childs = parent_title.get("child_title",[])
  730. if len(childs)>0:
  731. range_data = get_range_data_by_childs(list_data[_data_i:],childs)
  732. p_text = ""
  733. _find = False
  734. end_id = id(_data["title_next"]) if isinstance(_data["sentence_title"],dict) and _data["title_next"] is not None and _data["title_next"]["sentence_title"] is not None else None
  735. for pdata in range_data:
  736. ptext = pdata["text"]
  737. for p in products:
  738. if ptext.find(_product)<0 and (ptext.find(p)>=0 or pdata["has_product"]):
  739. _find = True
  740. break
  741. if re.search(end_pattern,ptext) is not None:
  742. _find = True
  743. if _find:
  744. break
  745. if id(pdata)==end_id:
  746. break
  747. p_text += ptext+"\r\n"
  748. if len(p_text)>0:
  749. logger.debug("extract_type by parent range_text:%s"%p_text)
  750. list_result.append(p_text)
  751. return True
  752. return False
  753. def get_table_pieces(_text,_product,products,list_result,_find):
  754. _soup = BeautifulSoup(_text,"html5lib")
  755. _table = _soup.find("table")
  756. if _table is not None:
  757. trs = getTrs(_table)
  758. list_trs = []
  759. for tr in trs:
  760. tr_text = tr.get_text()
  761. if tr_text.find(_product)>=0:
  762. _find = True
  763. logger.debug("%s-%s"%("table_html_tr",tr_text))
  764. for p in products:
  765. if _find and p!=_product and tr_text.find(p)>=0:
  766. _find = False
  767. break
  768. if re.search(end_pattern,tr_text) is not None:
  769. _find = False
  770. break
  771. if _find:
  772. list_trs.append(tr)
  773. if len(list_trs)>0:
  774. table_html = "<table>%s</table>"%("\r\n".join([str(a) for a in list_trs]))
  775. logger.debug("extract_type table slices %s"%(table_html))
  776. list_result.append(table_html)
  777. def extract_parameters_by_table(_product,products,_param_pattern,list_data,_data_i,list_result):
  778. _data = list_data[_data_i]
  779. _text = _data["text"]
  780. list_table = _data["list_table"]
  781. parent_title = _data["parent_title"]
  782. if list_table is not None:
  783. _check = True
  784. max_length = max([len(a) for a in list_table])
  785. min_length = min([len(a) for a in list_table])
  786. text_line_first = ",".join(a[0] for a in list_table[0])
  787. if max_length>10:
  788. if min_length<max_length/2:
  789. return
  790. last_data = list_data[_data_i-1]
  791. _flag = False
  792. if last_data["type"]=="sentence" and last_data["text"].find(_product)>=0:
  793. logger.debug("last sentence find product %s-%s"%(_product,last_data["text"]))
  794. _flag = True
  795. # print(text_line_first,"text_line_first",re.search(_param_pattern,text_line_first) is not None and text_line_first.find(_product)>=0)
  796. if re.search(_param_pattern,text_line_first) is not None and text_line_first.find(_product)>=0:
  797. _flag = True
  798. if _flag:
  799. if len(products)==0:
  800. logger.debug("extract_type whole table by param and product %s"%(_text))
  801. list_result.append(_text)
  802. else:
  803. for p in products:
  804. if p!=_product and _text.find(p)>=0:
  805. logger.debug("extract_type add all table failed %s-%s"%(_product,p))
  806. _flag = False
  807. break
  808. if _flag:
  809. logger.debug("extract_type add all table succeed")
  810. get_table_pieces(_text,_product,products,list_result,True)
  811. else:
  812. list_head_index = []
  813. for line in list_table[:2]:
  814. for cell_i in range(len(line)):
  815. cell = line[cell_i]
  816. cell_text = cell[0]
  817. if len(cell_text)<20 and re.search(_param_pattern,cell_text) is not None:
  818. list_head_index.append(cell_i)
  819. list_head_index = list(set(list_head_index))
  820. for line in list_table:
  821. for cell in line:
  822. cell_text = cell[0]
  823. if len(cell_text)>50 and len(re.findall(meter_pattern,cell_text))>5 and cell_text.find(_product)>=0:
  824. _f = True
  825. for cell in line:
  826. if not _f:
  827. break
  828. cell_text = cell[0]
  829. for p in products:
  830. if cell_text.find(p)>=0 and p!=_product:
  831. _f = False
  832. break
  833. if _f:
  834. logger.debug("extract_type param column %s"%(cell_text))
  835. list_result.append(cell_text)
  836. if len(cell_text)<len(_product)*10 and str(cell_text).find(_product)>=0:
  837. for _index in list_head_index:
  838. if _index>=len(line):
  839. continue
  840. _cell = line[_index]
  841. if len(cell[0])>0:
  842. logger.info("%s-%s"%("extract_type add on table text:",_cell[0]))
  843. list_result.append(_cell[0])
  844. if not _flag and (re.search(_param_pattern,_text) is not None or (parent_title is not None and re.search(_param_pattern,parent_title["text"]) is not None)) and _text.find(_product)>=0:
  845. get_table_pieces(_text,_product,products,list_result,False)
  846. def extract_parameters_by_sentence(list_data,_data,_data_i,_product,products,list_result,is_project):
  847. _text = _data["text"]
  848. if _text.find(_product)>=0:
  849. parent_title = _data.get("parent_title")
  850. parent_text = ""
  851. parent_parent_title = None
  852. parent_parent_text = ""
  853. parent_title_index = None
  854. parent_parent_title_index = None
  855. childs = get_childs([_data])
  856. child_find = False
  857. for c in childs:
  858. if re.search(_param_pattern,c["text"]) is not None and len(c["text"])<30:
  859. logger.debug("child text %s"%(c["text"]))
  860. child_find = True
  861. break
  862. extract_text,_,_ = get_childs_text([_data],_product,products)
  863. logger.debug("childs found extract_text %s %s"%(str(child_find),extract_text))
  864. if child_find:
  865. if len(extract_text)>0:
  866. list_result.append(extract_text)
  867. else:
  868. limit_nums = len(_product)*2+5
  869. if len(_product)<=3:
  870. limit_nums += 6
  871. if _text.find("数量")>=0:
  872. limit_nums += 6
  873. if len(_text)<=limit_nums and _data["sentence_title"] is not None:
  874. if re.search(meter_pattern,extract_text) is not None:
  875. list_result.append(extract_text)
  876. elif len(re.findall(meter_pattern,extract_text))>2:
  877. list_result.append(extract_text)
  878. if parent_title is not None:
  879. parent_text = parent_title.get("text","")
  880. parent_parent_title = parent_title.get("parent_title")
  881. parent_title_index = parent_title["title_index"]
  882. if parent_parent_title is not None:
  883. parent_parent_text = parent_parent_title.get("text","")
  884. parent_parent_title_index = parent_parent_title["title_index"]
  885. _suit = False
  886. if re.search(_param_pattern,_text) is not None and len(_text)<50:
  887. _suit = True
  888. if re.search(_param_pattern,parent_text) is not None and len(parent_text)<50:
  889. _suit = True
  890. if re.search(_param_pattern,parent_parent_text) is not None and len(parent_parent_text)<50:
  891. _suit = True
  892. if _suit:
  893. logger.debug("extract_type sentence %s"%("extract_parameters_by_tree"))
  894. if not extract_parameters_by_tree(_product,products,list_data,_data_i,parent_title,list_result):
  895. logger.debug("extract_type sentence %s"%("extract_parameters_by_tree"))
  896. extract_parameters_by_tree(_product,products,list_data,_data_i,parent_parent_title,list_result)
  897. if re.search(_param_pattern,_text) is not None and len(_text)<50:
  898. childs = _data["child_title"]
  899. if len(childs)>0:
  900. extract_text,_,_ = get_childs_text([_data],_product,products)
  901. if len(extract_text)>0:
  902. logger.debug("extract_type param-product %s"%(extract_text))
  903. list_result.append(extract_text)
  904. elif is_project:
  905. extract_text,_,_ = get_childs_text([_data],_product,products,is_begin=True)
  906. if len(extract_text)>0 and re.search(meter_pattern,extract_text) is not None:
  907. logger.debug("extract_type sentence is_project param-product is product %s"%(extract_text))
  908. list_result.append(extract_text)
  909. def getBestProductText(list_result,_product,products):
  910. list_result.sort(key=lambda x:len(re.findall(meter_pattern+"|"+'[::;;]|\d+[%A-Za-z]+',BeautifulSoup(x,"html5lib").get_text())), reverse=True)
  911. logger.debug("+++++++++++++++++++++")
  912. for i in range(len(list_result)):
  913. logger.debug("result%d %s"%(i,list_result[i]))
  914. logger.debug("+++++++++++++++++++++")
  915. for i in range(len(list_result)):
  916. _result = list_result[i]
  917. _check = True
  918. _result_text = BeautifulSoup(_result,"html5lib").get_text()
  919. _search = re.search("项目编号[::]|项目名称[::]|联合体投标|开户银行",_result)
  920. if _search is not None:
  921. logger.debug("result%d error illegal text %s"%(i,str(_search)))
  922. _check = False
  923. if not (len(_result_text)<1000 and _result[:6]!="<table"):
  924. for p in products:
  925. if _result_text.find(p)>0 and not (is_similar(_product,p,80) or p.find(_product)>=0 or _product.find(p)>=0):
  926. logger.debug("result%d error product scoss %s"%(i,p))
  927. _check = False
  928. if len(_result_text)<100:
  929. if re.search(meter_pattern,_result_text) is None:
  930. logger.debug("result%d error text min count"%(i))
  931. _check = False
  932. if len(_result_text)>5000:
  933. if len(_result_text)>10000:
  934. logger.debug("result%d error text max count"%(i))
  935. _check = False
  936. elif len(re.findall(meter_pattern,_result_text))<10:
  937. logger.debug("result%d error text max count less meter"%(i))
  938. _check = False
  939. list_find = list(set(re.findall(meter_pattern,_result_text)))
  940. not_list_find = list(set(re.findall(not_meter_pattern,_result_text)))
  941. _count = len(list_find)-len(not_list_find)
  942. has_num = False
  943. for _find in list_find:
  944. if re.search('[0-9a-zA-Z]',_find) is not None:
  945. has_num = True
  946. break
  947. if not(_count>=2 and has_num or _count>=5):
  948. logger.debug("result%d error match not enough"%(i))
  949. _check = False
  950. if _check:
  951. return _result
  952. def extract_product_parameters(list_data,_product):
  953. list_result = []
  954. _product = standard_product(_product.strip())
  955. products = extract_products(list_data,_product)
  956. _product = get_correct_product(_product,products)
  957. logger.debug("all products %s-%s"%(_product,str(products)))
  958. is_project = False
  959. if re.search("项目名称|采购项目",_product) is not None:
  960. is_project = True
  961. if len(products)==1 and is_similar(products[0],_product,90):
  962. is_project = True
  963. _find_count = 0
  964. for _data_i in range(len(list_data)):
  965. _data = list_data[_data_i]
  966. _type = _data["type"]
  967. _text = _data["text"]
  968. if _type=="sentence":
  969. if _text.find(_product)>=0:
  970. _find_count += 1
  971. if re.search("项目名称|采购项目",_text) is not None and re.search("等",_text) is not None:
  972. is_project = True
  973. extract_parameters_by_sentence(list_data,_data,_data_i,_product,products,list_result,is_project)
  974. elif _type=="table":
  975. if _text.find(_product)>=0:
  976. _find_count += 1
  977. extract_parameters_by_table(_product,products,_param_pattern,list_data,_data_i,list_result)
  978. _text = getBestProductText(list_result,_product,products)
  979. return _text,_find_count
  980. if __name__ == '__main__':
  981. filepath = "download/4597dcc128bfabc7584d10590ae50656.html"
  982. _product = "彩色多普勒超声诊断仪"
  983. _html = open(filepath, "r", encoding="utf8").read()
  984. pd = ParseDocument(_html,False)
  985. pd.fix_tree(_product)
  986. list_data = pd.tree
  987. pd.print_tree(list_data)
  988. _text,_count = extract_product_parameters(list_data,_product)
  989. logger.info("find count:%d"%(_count))
  990. logger.info("extract_parameter_text::%s"%(_text))