#coding:utf8 from bs4 import BeautifulSoup import json import re import traceback import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) from BiddingKG.dl.interface.Preprocessing import tableToText from uuid import uuid4 def log(msg): ''' @summary:打印信息 ''' logger.info(msg) class DotDict(dict): def __getattr__(self,name): try: return self[name] except KeyError: raise AttributeError("No attribute '%s'" % name) def __setattr__(self,name,value): self[name] = value def get_tables(soup,dict_table = None): is_first = False if dict_table is None: dict_table = {"children":[]} is_first = True if soup and soup.name: childs = soup.contents else: childs = [] # tr+tbody _flag = False if len(childs)>=2: if childs[0].name=="tr" and childs[1].name=="tbody": childs[1].insert(0,copy.copy(childs[0])) childs[0].decompose() _flag = True childs_bak = childs # tbody+tbody _flag = False if soup and soup.name: childs = soup.find_all("tbody",recursive=False) if len(childs)>=2: if childs[0].name=="tbody" and childs[1].name=="tbody": child0_tr = childs[0].find_all("tr",recursive=False) has_td_count = 0 tr_line = None for tr in child0_tr: if len(tr.find_all(["td", "th"],recursive=False))>0: has_td_count += 1 tr_line = tr if has_td_count==1: childs[1].insert(0,copy.copy(tr_line)) childs[0].decompose() _flag = True childs = childs_bak for child in childs: _d = {"children":[]} if child.name in ("table","tbody"): if len(child.find_all("tr",recursive=False))>0: # _d["table"] = str(child) _d["table"] = child dict_table["children"].append(_d) child_dict_table = get_tables(child,_d) if is_first: if soup.name in ("table","tbody"): if not _flag: if len(soup.find_all("tr",recursive=False))>0: # dict_table["table"] = str(soup) dict_table["table"] = soup dict_table = squeeze_tables(dict_table) return dict_table def squeeze_tables(dict_table): _i = -1 new_children = [] for child in dict_table["children"]: _i += 1 child_table = squeeze_tables(child) if child_table is not None: new_children.append(child_table) if dict_table.get("table") is not None: if len(new_children)>0: dict_table["children"] = new_children else: del dict_table["children"] return dict_table if len(new_children)==1: return new_children[0] if len(new_children)>1: dict_table["children"] = new_children return dict_table return None def table_to_tree(soup,json_obj=None): if json_obj is None: json_obj = DotDict({"tag": "table","children":[]}) dict_table = get_tables(soup) if dict_table == None: # 20241226 修复报错 dict_table = {} children = dict_table.get("children",[]) for child in children: _d = DotDict({"tag": "table","children":[]}) json_obj["children"].append(_d) table = child.get("table") if table is not None: table_id = str(uuid4()) table_to_tree(table,_d) table = dict_table.get("table") if table is not None: table_id = str(uuid4()) json_obj["table_id"] = table_id soup, kv_list, text = tableToText(table,return_kv=True) _flag = False if soup and soup.name: if soup.contents: _flag = True soup.contents[0].insert_before(table_id) if not _flag: soup.insert_before(table_id) json_obj["text"] = text json_obj["kv"] = kv_list for _d in kv_list: _d["position"] = {"key_begin_sentence":0, "key_begin_sentence_start":_d.get("key_sen_index",0), "key_end_sentence":0, "key_end_sentence_end":_d.get("key_sen_index",0)+len(_d.get("key","")), "value_begin_sentence":0, "value_begin_sentence_start":_d.get("value_sen_index",0), "value_end_sentence":0, "value_end_sentence_end":_d.get("value_sen_index",0)+len(_d.get("value","")) } if "key_sen_index" in _d: _d.pop("key_sen_index") if "value_sen_index" in _d: _d.pop("value_sen_index") return json_obj def update_table_position(table,sentence_index): def get_table_idx_lengths(list_table_id,index): _length = 0 for _d in list_table_id: table_id = _d.get("table_id") idx = _d.get("idx",-1) if idx>=0 and _idx<=index: _length += len(table_id) return _length def get_sentence_index(list_sent_span,idx): list_sent_span.sort(key=lambda x:x[0]) for _i in range(len(list_sent_span)): if list_sent_span[_i][0]<=idx and idx<=list_sent_span[_i][1]: return _i return 0 def get_list_tables(table,list_table=[]): table_id = table.get("table_id") if table_id: list_table.append(table) childs = table.get("children",[]) for child in childs: get_list_tables(child,list_table) return list_table tables = get_list_tables(table) if tables: list_table_id = [] text = tables[0].get("text","") for table in tables: table_id = table.get("table_id") if table_id: _idx = text.find(table_id) list_table_id.append({"table_id":table_id,"idx":_idx}) if _idx>=0: kv_list = table.get("kv",[]) for _d in kv_list: _d["position"]["key_begin_sentence_start"] += _idx _d["position"]["key_end_sentence_end"] += _idx _d["position"]["value_begin_sentence_start"] += _idx _d["position"]["value_end_sentence_end"] += _idx # remove table_id for table in tables: table_id = table.get("table_id") if table_id: kv_list = table.get("kv",[]) for _d in kv_list: _length = get_table_idx_lengths(list_table_id,_d["position"]["key_begin_sentence_start"]) _d["position"]["key_begin_sentence_start"] -= _length _length = get_table_idx_lengths(list_table_id,_d["position"]["key_end_sentence_end"]) _d["position"]["key_end_sentence_end"] -= _length _length = get_table_idx_lengths(list_table_id,_d["position"]["value_begin_sentence_start"]) _d["position"]["value_begin_sentence_start"] -= _length _length = get_table_idx_lengths(list_table_id,_d["position"]["value_end_sentence_end"]) _d["position"]["value_end_sentence_end"] -= _length for table in tables: if table.get("table_id"): text = table.get("text","") for _d in list_table_id: table_id = _d.get("table_id") text = text.replace(table_id,"") table["text"] = text # split sentence text = tables[0].get("text","") list_sentence = str(text).split("。") list_sent_span = [] _begin = 0 for _i in range(len(list_sentence)): list_sentence[_i] += "。" _end = _begin+len(list_sentence[_i]) list_sent_span.append([_begin,_end]) _begin = _end tables[0]["sentences"] = list_sentence for table in tables: kv_list = table.get("kv",[]) for _d in kv_list: key_begin_sentence = get_sentence_index(list_sent_span,_d["position"]["key_begin_sentence_start"]) _d["position"]["key_begin_sentence"] = key_begin_sentence+sentence_index key_end_sentence = get_sentence_index(list_sent_span,_d["position"]["key_end_sentence_end"]) _d["position"]["key_end_sentence"] = key_end_sentence+sentence_index value_begin_sentence = get_sentence_index(list_sent_span,_d["position"]["value_begin_sentence_start"]) _d["position"]["value_begin_sentence"] = value_begin_sentence+sentence_index value_end_sentence = get_sentence_index(list_sent_span,_d["position"]["value_end_sentence_end"]) _d["position"]["value_end_sentence"] = value_end_sentence+sentence_index return sentence_index + len(list_sentence) return sentence_index def tree_reposition(tree,sentence_index=None): if sentence_index is None: sentence_index = 0 wordOffset_begin = 0 wordOffset_end = 0 for obj in tree: is_table = True if obj.get("tag","")=="table" else False if not is_table: sentence_index += 1 obj["sentence_index"] = sentence_index obj["sentences"] = [obj.get("text","")] for _t in obj["sentences"]: wordOffset_end += len(_t) obj["wordOffset_begin"] = wordOffset_begin obj["wordOffset_end"] = wordOffset_end wordOffset_begin = wordOffset_end list_kv = obj.get("kv",[]) for _d in list_kv: _d["position"]["key_begin_sentence"] = sentence_index _d["position"]["key_end_sentence"] = sentence_index _d["position"]["value_begin_sentence"] = sentence_index _d["position"]["value_end_sentence"] = sentence_index else: sentence_index += 1 obj["sentence_index"] = sentence_index obj["sentence_index_start"] = sentence_index obj["sentences"] = [obj.get("text","")] sentence_index_end = update_table_position(obj,sentence_index) obj["sentence_index_end"] = sentence_index_end sentence_index = sentence_index_end for _t in obj["sentences"]: wordOffset_end += len(_t) obj["wordOffset_begin"] = wordOffset_begin obj["wordOffset_end"] = wordOffset_end wordOffset_begin = wordOffset_end # 递归地将 DOM 转换为 JSON def dom_to_tree(node): if node.name: # 如果是标签节点 json_obj = DotDict({"tag": node.name}) if node.attrs: json_obj["attributes"] = node.attrs is_table = False if node.name in ("table","tbody"): json_obj = table_to_tree(node) is_table = True if not is_table: children = [] for child in node.contents: _child = dom_to_tree(child) if _child is not None: children.append(_child) if children: json_obj["children"] = children json_obj["name"] = json_obj.get("tag") return json_obj elif node.string and node.string.strip(): # 如果是纯文本节点 _text = node.string.strip() _text = re.sub('\xa0','',_text) list_text = re.split("\s",_text) _text = "" for _t in list_text: if len(_t)<3: if len(_t)>0: _text += _t else: _text += _t+" " _text = _text.strip() return DotDict({"tag":"text","name":"text","text": _text}) return None # 忽略空白字符 def tree_pop_parent(tree): if isinstance(tree,list): for child in tree: tree_pop_parent(child) if isinstance(tree,dict): if "parent" in tree: del tree["parent"] for child in tree.get("children",[]): tree_pop_parent(child) def html_to_tree(html_content): # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(html_content, "lxml") dom_tree = dom_to_tree(soup) extract_kv_from_tree(dom_tree) list_objs = get_outobjs_from_tree(dom_tree) tree_reposition(list_objs) return dom_tree def print_tree(dom_tree): # 转换为 JSON 格式 tree_pop_parent(dom_tree) json_output = json.dumps(dom_tree,ensure_ascii=False, indent=2) # kv_pattern = "\s*(?P.{,10})[::]\s*(?P[^::。,()]+?)(\s+|$|;|;)(?![\u4e00-\u9fa5]+:)" kv_pattern = r"(?P[\u4e00-\u9fa5]+):\s*(?P[^\s,。();;]+)" def get_kv_pattern(): import re text = """ name: John age: 30 note: invalid; """ # 正则模式 kv_pattern = r"(?P[a-zA-Z]+)[::](?P.+(?!.*[::]))" # 提取匹配 matches = re.findall(kv_pattern, text) # 打印结果 for match in matches: key, value = match print("{%s}: {%s}"%(key,value)) def extract_kv_from_sentence(sentence): list_kv = [] _iter = re.finditer("[::]", sentence) if _iter: list_span = [] for iter in _iter: list_span.append(iter.span()) if len(list_span)==1: _begin,_end = list_span[0] if _begin<20 and _end1: _begin,_end = list_span[0] if _begin<20 and len(sentence)>100: _d = DotDict({"key":sentence[0:_begin],"value":sentence[_end:]}) _d["position"] = {"key_begin_sentence":0, "key_begin_sentence_start":0, "key_end_sentence":0, "key_end_sentence_end":_begin, "value_begin_sentence":0, "value_begin_sentence_start":_end, "value_end_sentence":0, "value_end_sentence_end":len(sentence) } list_kv.append(_d) else: _begin = 0 for _i in range(len(list_span)-1): _end = list_span[_i+1][0] iter = re.search(kv_pattern,sentence[_begin:_end]) _begin = list_span[_i][1] if iter is not None: _d = DotDict({}) _d["key"] = iter.group("key") _d["value"] = iter.group("value") _d["position"] = {"key_begin_sentence":0, "key_begin_sentence_start":iter.span("key")[0], "key_end_sentence":0, "key_end_sentence_end":iter.span("key")[0]+len(_d.get("key","")), "value_begin_sentence":0, "value_begin_sentence_start":iter.span("value")[0], "value_end_sentence":0, "value_end_sentence_end":iter.span("value")[0]+len(_d.get("value","")) } list_kv.append(_d) _begin = list_span[-2][1] _end = len(sentence) iter = re.search(kv_pattern,sentence[_begin:_end]) if iter is not None: _d = DotDict({}) _d["key"] = iter.group("key") _d["value"] = iter.group("value") _d["position"] = {"key_begin_sentence":0, "key_begin_sentence_start":iter.span("key")[0], "key_end_sentence":0, "key_end_sentence_end":iter.span("key")[0]+len(_d.get("key","")), "value_begin_sentence":0, "value_begin_sentence_start":iter.span("value")[0], "value_end_sentence":0, "value_end_sentence_end":iter.span("value")[0]+len(_d.get("value","")) } list_kv.append(_d) # for iter in _iter: # _d = DotDict({}) # _d["key"] = iter.group("key") # _d["value"] = iter.group("value") # _d["key_span"] = iter.span("key") # _d["value_span"] = iter.span("value") # list_kv.append(_d) return list_kv def extract_kv_from_node(node): list_kv = [] list_text = [] childs = node.get("children",[]) _text = "" has_br = False if childs: for child in childs: node_name = child.get("tag","") child_text = child.get("text") if node_name=="br": list_text.append([]) has_br = True if child_text: if len(list_text)==0: list_text.append([]) list_text[-1].append(child) node["kv"] = [] if has_br: new_children = [] for texts in list_text: if texts: _text = "".join([a.get("text") for a in texts]) tag = texts[0] list_kv = extract_kv_from_sentence(_text) _n = DotDict({"tag":tag,"name":tag,"text":_text,"children":[],"kv":list_kv}) new_children.append(_n) node["children"] = new_children else: for texts in list_text: _text = "".join([a.get("text") for a in texts]) if _text: list_kv = extract_kv_from_sentence(_text) node["kv"].extend(list_kv) else: _text = node.get("text") if _text: list_kv = extract_kv_from_sentence(_text) node["kv"] = list_kv return list_kv def get_child_text(node): _text = node.get("text","") for child in node.get("children",[]): _text += get_child_text(child) return _text def extract_kv_from_tree(tree): if isinstance(tree,list): _count = 0 has_table = False for child in tree: _c,_t = extract_kv_from_tree(child) _count += _c if _t: has_table = _t return _count,has_table if isinstance(tree,dict): if tree.get("tag","")!="table": childs = tree.get("children",[]) if len(childs)>0: _count = 0 has_table = False child_has_p_div = False child_has_br = False for child in childs: _c,_t = extract_kv_from_tree(child) _count += _c if _t: has_table = _t if child.get("tag","") in ("p","div","li"): child_has_p_div = True if child.get("tag","")=="br": child_has_br = True if _count==0: if not has_table and not child_has_p_div and not child_has_br: _text = get_child_text(tree) if "children" in tree: del tree["children"] tree["text"] = _text list_kv = extract_kv_from_node(tree) _count = len(list_kv) return _count,has_table if tree.get("tag","") in ("p","div","li") and not has_table and not child_has_p_div: if not child_has_br: _text = get_child_text(tree) tree["text"] = _text if "children" in tree: del tree["children"] p_list_kv = extract_kv_from_node(tree) return len(p_list_kv),has_table return _count,has_table else: list_kv = extract_kv_from_node(tree) return len(list_kv),False else: return len(tree.get("kv",[])),True return 0,False def update_kv_span(list_kv,append_length): for _d in list_kv: _d["position"] = {"key_begin_sentence":0, "key_begin_sentence_start":_d.get("key_sen_index",0), "key_end_sentence":0, "key_end_sentence_end":_d.get("key_sen_index",0)+len(_d.get("key","")), "value_begin_sentence":0, "value_begin_sentence_start":_d.get("value_sen_index",0), "value_end_sentence":0, "value_end_sentence_end":_d.get("value_sen_index",0)+len(_d.get("value","")) } _d["position"]["key_begin_sentence_start"] += append_length _d["position"]["key_end_sentence_end"] += append_length _d["position"]["value_begin_sentence_start"] += append_length _d["position"]["value_end_sentence_end"] += append_length def get_outobjs_from_tree(tree,list_outobjs=None): is_first = False if list_outobjs is None: list_outobjs = [] is_first = True if isinstance(tree,list): for child in tree: get_outobjs_from_tree(child,list_outobjs) if isinstance(tree,dict): childs = tree.get("children",[]) _text = tree.get("text","") is_table = True if tree.get("tag","")=="table" else False if is_table: if _text!="" and _text is not None: tree.name = tree.tag list_outobjs.append(tree) else: for child in childs: get_outobjs_from_tree(child,list_outobjs) else: if _text!="" and _text is not None: tree.name = tree.tag tree.text = _text list_outobjs.append(tree) for child in childs: get_outobjs_from_tree(child,list_outobjs) return list_outobjs def standard_title_context(_title_context): return _title_context.replace("(","(").replace(")",")").replace(":",":").replace(":",";").replace(",",".").replace(",",".").replace("、",".").replace(".",".") def standard_product(sentence): return sentence.replace("(","(").replace(")",")") import Levenshtein import copy def jaccard_score(source,target): source_set = set([s for s in source]) target_set = set([s for s in target]) if len(source_set)==0 or len(target_set)==0: return 0 return max(len(source_set&target_set)/len(source_set),len(source_set&target_set)/len(target_set)) def judge_pur_chinese(keyword): """ 中文字符的编码范围为: u'\u4e00' -- u'\u9fff:只要在此范围内就可以判断为中文字符串 @param keyword: @return: """ # 定义一个需要删除的标点符号字符串列表 remove_chars = '[·’!"\#$%&\'()#!()*+,-./:;<=>?\@,:?¥★、….>【】[]《》?“”‘’\[\\]^_`{|}~]+' # 利用re.sub来删除中文字符串中的标点符号 strings = re.sub(remove_chars, "", keyword) # 将keyword中文字符串中remove_chars中包含的标点符号替换为空字符串 for ch in strings: if u'\u4e00' <= ch <= u'\u9fff': pass else: return False return True def is_similar(source,target,_radio=None): source = str(source).lower() target = str(target).lower() max_len = max(len(source),len(target)) min_len = min(len(source),len(target)) min_ratio = 90 if min_len>=3: min_ratio = 87 if min_len>=5: min_ratio = 85 if _radio is not None: min_ratio = _radio # dis_len = abs(len(source)-len(target)) # min_dis = min(max_len*0.2,4) if min_len==0 and max_len>0: return False if max_len<=2: if source==target: return True if min_len<2: return False #判断相似度 similar = Levenshtein.ratio(source,target)*100 if similar>=min_ratio: log("%s and %s similar_jaro %d"%(source,target,similar)) return True similar_jaro = Levenshtein.jaro(source,target) if similar_jaro*100>=min_ratio: log("%s and %s similar_jaro %d"%(source,target,similar_jaro*100)) return True similar_jarow = Levenshtein.jaro_winkler(source,target) if similar_jarow*100>=min_ratio: log("%s and %s similar_jaro %d"%(source,target,similar_jarow*100)) return True if min_len>=5: if len(source)==max_len and str(source).find(target)>=0: return True elif len(target)==max_len and target.find(source)>=0: return True elif jaccard_score(source, target)==1 and judge_pur_chinese(source) and judge_pur_chinese(target): return True return False end_pattern = "商务要求|评分标准|商务条件|商务条件" _param_pattern = "(产品|技术|清单|配置|参数|具体|明细|项目|招标|货物|服务|规格|工作|具体)[及和与]?(指标|配置|条件|要求|参数|需求|规格|条款|名称及要求)|配置清单|(质量|技术).{,10}要求|验收标准|^(参数|功能)$" 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大中小]+码|净重|颜色|[红橙黄绿青蓝紫]色|不锈钢|输入|输出|噪声|认证|配置" not_meter_pattern = "投标报价|中标金额|商务部分|公章|分值构成|业绩|详见|联系人|联系电话|合同价|金额|采购预算|资金来源|费用|质疑|评审因素|评审标准|商务资信|商务评分|专家论证意见|评标方法|代理服务费|售后服务|评分类型|评分项目|预算金额|得\d+分|项目金额|详见招标文件|乙方" def getTrs(tbody): #获取所有的tr trs = [] if tbody.name=="table": body = tbody.find("tbody",recursive=False) if body is not None: tbody = body objs = tbody.find_all(recursive=False) for obj in objs: if obj.name=="tr": trs.append(obj) if obj.name=="tbody" or obj.name=="table": for tr in obj.find_all("tr",recursive=False): trs.append(tr) return trs def fixSpan(tbody): # 处理colspan, rowspan信息补全问题 #trs = tbody.findChildren('tr', recursive=False) trs = getTrs(tbody) ths_len = 0 ths = list() trs_set = set() #修改为先进行列补全再进行行补全,否则可能会出现表格解析混乱 # 遍历每一个tr for indtr, tr in enumerate(trs): ths_tmp = tr.findChildren('th', recursive=False) #不补全含有表格的tr if len(tr.findChildren('table'))>0: continue if len(ths_tmp) > 0: ths_len = ths_len + len(ths_tmp) for th in ths_tmp: ths.append(th) trs_set.add(tr) # 遍历每行中的element tds = tr.findChildren(recursive=False) for indtd, td in enumerate(tds): # 若有colspan 则补全同一行下一个位置 if 'colspan' in td.attrs: if str(re.sub("[^0-9]","",str(td['colspan'])))!="": col = int(re.sub("[^0-9]","",str(td['colspan']))) if col<100 and len(td.get_text())<1000: td['colspan'] = 1 for i in range(1, col, 1): td.insert_after(copy.copy(td)) for indtr, tr in enumerate(trs): ths_tmp = tr.findChildren('th', recursive=False) #不补全含有表格的tr if len(tr.findChildren('table'))>0: continue if len(ths_tmp) > 0: ths_len = ths_len + len(ths_tmp) for th in ths_tmp: ths.append(th) trs_set.add(tr) # 遍历每行中的element tds = tr.findChildren(recursive=False) for indtd, td in enumerate(tds): # 若有rowspan 则补全下一行同样位置 if 'rowspan' in td.attrs: if str(re.sub("[^0-9]","",str(td['rowspan'])))!="": row = int(re.sub("[^0-9]","",str(td['rowspan']))) td['rowspan'] = 1 for i in range(1, row, 1): # 获取下一行的所有td, 在对应的位置插入 if indtr+i= (indtd) and len(tds1)>0: if indtd > 0: tds1[indtd - 1].insert_after(copy.copy(td)) else: tds1[0].insert_before(copy.copy(td)) elif indtd-2>0 and len(tds1) > 0 and len(tds1) == indtd - 1: # 修正某些表格最后一列没补全 tds1[indtd-2].insert_after(copy.copy(td)) def getTable(tbody): #trs = tbody.findChildren('tr', recursive=False) fixSpan(tbody) trs = getTrs(tbody) inner_table = [] for tr in trs: tr_line = [] tds = tr.findChildren(['td','th'], recursive=False) if len(tds)==0: tr_line.append([re.sub('\xa0','',tr.get_text()),0]) # 2021/12/21 修复部分表格没有td 造成数据丢失 for td in tds: tr_line.append([re.sub('\xa0','',td.get_text()),0]) #tr_line.append([td.get_text(),0]) inner_table.append(tr_line) return inner_table def extract_products(list_data,_product,_param_pattern = "产品名称|设备材料|采购内存|标的名称|采购内容|(标的|维修|系统|报价构成|商品|产品|物料|物资|货物|设备|采购品|采购条目|物品|材料|印刷品?|采购|物装|配件|资产|耗材|清单|器材|仪器|器械|备件|拍卖物|标的物|物件|药品|药材|药械|货品|食品|食材|品目|^品名|气体|标项|分项|项目|计划|包组|标段|[分子]?包|子目|服务|招标|中标|成交|工程|招标内容)[\))的]?([、\w]{,4}名称|内容|描述)|标的|标项|项目$|商品|产品|物料|物资|货物|设备|采购品|采购条目|物品|材料|印刷品|物装|配件|资产|招标内容|耗材|清单|器材|仪器|器械|备件|拍卖物|标的物|物件|药品|药材|药械|货品|食品|食材|菜名|^品目$|^品名$|^名称|^内容$"): _product = standard_product(_product) list_result = [] list_table_products = [] for _data_i in range(len(list_data)): _data = list_data[_data_i] _type = _data["type"] _text = _data["text"] if _type=="table": list_table = _data["list_table"] if list_table is None: continue _check = True max_length = max([len(a) for a in list_table]) min_length = min([len(a) for a in list_table]) if min_length=len(line): continue cell = line[cell_i] cell_text = cell[0] head_cell_text += cell_text # print("===head_cell_text",head_cell_text) if re.search("招标人|采购人|项目编号|项目名称|金额|^\d+$",head_cell_text) is not None: list_head_index = [] for line in list_table: line_text = ",".join([cell[0] for cell in line]) for cell_i in range(len(line)): cell = line[cell_i] cell_text = cell[0] if cell_text is not None and _product is not None and len(cell_text)=0 and re.search("单价|数量|总价|规格|品牌|型号|用途|要求|采购量",line_text) is not None: list_head_index.append(cell_i) list_head_index = list(set(list_head_index)) if len(list_head_index)>0: has_number = False for cell_i in list_head_index: table_products = [] for line_i in range(_begin_index,len(list_table)): line = list_table[line_i] for _i in range(len(line)): cell = line[_i] cell_text = cell[0] if re.search("^\d+$",cell_text) is not None: has_number = True if cell_i>=len(line): continue cell = line[cell_i] cell_text = cell[0] if re.search(_param_pattern,cell_text) is None or has_number: if re.search("^[\da-zA-Z]+$",cell_text) is None: table_products.append(cell_text) if len(table_products)>0: logger.debug("table products %s"%(str(table_products))) if min([len(x) for x in table_products])>0 and max([len(x) for x in table_products])<=30: if re.search("招标人|代理人|预算|数量|交货期|品牌|产地","".join(table_products)) is None: list_table_products.append(table_products) _find = False for table_products in list_table_products: for _p in table_products: if is_similar(_product,_p,90): _find = True logger.debug("similar table_products %s"%(str(table_products))) list_result = list(set([a for a in table_products if len(a)>1 and len(a)<20 and re.search("费用|预算|合计|金额|万元|运费|^其他$",a) is None])) break if not _find: for table_products in list_table_products: list_result.extend(table_products) list_result = list(set([a for a in list_result if len(a)>1 and len(a)<30 and re.search("费用|预算|合计|金额|万元|运费",a) is None])) return list_result def get_childs(childs, max_depth=None): list_data = [] for _child in childs: list_data.append(_child) childs2 = _child.get("child_title",[]) if len(childs2)>0 and (max_depth==None or max_depth>0): for _child2 in childs2: if max_depth != None: list_data.extend(get_childs([_child2], max_depth-1)) else: list_data.extend(get_childs([_child2], None)) return list_data class Html2KVTree(): def __init__(self,_html,auto_merge_table=True,list_obj = []): if _html is None: _html = "" self.html = _html self.auto_merge_table = auto_merge_table if list_obj: self.list_obj = list_obj else: _tree = html_to_tree(_html) self.list_obj = get_outobjs_from_tree(_tree) # for obj in self.list_obj: # print("obj",obj.get_text()[:20]) self.tree = self.buildParsetree(self.list_obj,[],auto_merge_table) # #识别目录树 # self.print_tree(self.tree,"-|") def get_soup_objs(self,soup,list_obj=None): if list_obj is None: list_obj = [] childs = soup.find_all(recursive=False) for _obj in childs: childs1 = _obj.find_all(recursive=False) if len(childs1)==0 or len(_obj.get_text())<40 or _obj.name=="table": list_obj.append(_obj) elif _obj.name=="p": list_obj.append(_obj) else: self.get_soup_objs(_obj,list_obj) return list_obj def fix_tree(self,_product): products = extract_products(self.tree,_product) if len(products)>0: self.tree = self.buildParsetree(self.list_obj,products,self.auto_merge_table) def print_tree(self,tree,append="",set_tree_id=None): if set_tree_id is None: set_tree_id = set() if append=="": for t in tree: 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"])) for t in tree: _id = id(t) if _id in set_tree_id: continue set_tree_id.add(_id) logger.info("%s text:%s title:%s title_text:%s before:%s after%s product:%s kv:%s"%(append,t["text"][:50],t["sentence_title"],t["sentence_title_text"],t["title_before"],t["title_after"],t["has_product"],str(t["kv"]))) childs = t["child_title"] self.print_tree(childs,append=append+"-|",set_tree_id=set_tree_id) def is_title_first(self,title): if title in ("一","1","Ⅰ","a","A"): return True return False def find_title_by_pattern(self,_text,_pattern="(^|★|▲|:|:|\s+)(?P(?P第?)(?P[一二三四五六七八九十ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]+)(?P[、章册包标部.::]+))|" \ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?)(?P[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]+)(?P[、章册包标部.::]+))|" \ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?第?)(?P[一二三四五六七八九十]+)(?P[节章册部\.::、、]+))|" \ "([\s★▲\*]*)(?P(?P^)(?P[一二三四五六七八九十]+)(?P)[^一二三四五六七八九十节章册部\.::、])|" \ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-])(?P\d{1,2})(?P[\..、\s\-]?))|"\ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-])(?P\d{1,2})(?P[\..、\s\-]?))|" \ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..、\s\-]\d{1,2}[\..、\s\-])(?P\d{1,2})(?P[\..、\s\-]?))|" \ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?\d{1,2}[\..\s\-])(?P\d{1,2})(?P[\..包标::、\s\-]*))|" \ "(^[\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?包?)(?P\d{1,2})(?P[\..、\s\-包标]*))|" \ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?[((]?)(?P\d{1,2})(?P[))包标\..::、]+))|" \ "([\s★▲\*]+)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?[((]?)(?P[a-zA-Z]+)(?P[))包标\..::、]+))|" \ "([\s★▲\*]*)(?P(?P[^一二三四五六七八九十\dⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]{,3}?[((]?)(?P[一二三四五六七八九十ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]+)(?P[))]))" ): _se = re.search(_pattern,_text) groups = [] if _se is not None: e = _se.end() if re.search('(时间|日期|编号|账号|号码|手机|价格|\w价|人民币|金额|得分|分值|总分|满分|最高得|扣|减|数量|评委)[::]?\d', _se.group(0)) or (re.search('\d[.::]?$', _se.group(0)) and re.search('^[\d年月日万元天个分秒台条A-Za-z]|^(小时)', _text[e:])): return None elif re.match('[二三四五六七八九十]\w{1,2}[市区县]|五金|四川|八疆|九龙|[一二三四五六七八九十][层天标包]', _text) and re.match('[一二三四五六七八九十]', _se.group(0)): # 289765335 排除三明市等开头作为大纲 return None elif re.search('^[\u4e00-\u9fa5]+[::]', _text[:e]): return None _gd = _se.groupdict() for k,v in _gd.items(): if v is not None: groups.append((k,v)) if len(groups): groups.sort(key=lambda x:x[0]) return groups return None def make_increase(self,_sort,_title,_add=1): if len(_title)==0 and _add==0: return "" if len(_title)==0 and _add==1: return _sort[0] _index = _sort.index(_title[-1]) next_index = (_index+_add)%len(_sort) next_chr = _sort[next_index] if _index==len(_sort)-1: _add = 1 else: _add = 0 return next_chr+self.make_increase(_sort,_title[:-1],_add) def get_next_title(self,_title): if re.search("^\d+$",_title) is not None: return str(int(_title)+1) if re.search("^[一二三四五六七八九十百]+$",_title) is not None: if _title[-1]=="十": return _title+"一" if _title[-1]=="百": return _title+"零一" if _title[-1]=="九": if len(_title)==1: return "十" if len(_title)==2: if _title[0]=="十": return "二十" if len(_title)==3: if _title[0]=="九": return "一百" else: _next_title = self.make_increase(['一','二','三','四','五','六','七','八','九','十'],re.sub("[十百]",'',_title[0])) return _next_title+"十" _next_title = self.make_increase(['一','二','三','四','五','六','七','八','九','十'],re.sub("[十百]",'',_title)) _next_title = list(_next_title) _next_title.reverse() if _next_title[-1]!="十": if len(_next_title)>=2: _next_title.insert(-1,'十') if len(_next_title)>=4: _next_title.insert(-3,'百') if _title[0]=="十": if _next_title=="十": _next_title = ["二","十"] _next_title.insert(0,"十") _next_title = "".join(_next_title) return _next_title if re.search("^[a-z]+$",_title) is not None: _next_title = self.make_increase([chr(i+ord('a')) for i in range(26)],_title) _next_title = list(_next_title) _next_title.reverse() return "".join(_next_title) if re.search("^[A-Z]+$",_title) is not None: _next_title = self.make_increase([chr(i+ord('A')) for i in range(26)],_title) _next_title = list(_next_title) _next_title.reverse() return "".join(_next_title) if re.search("^[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]$",_title) is not None: _sort = ["Ⅰ","Ⅱ","Ⅲ","Ⅳ","Ⅴ","Ⅵ","Ⅶ","Ⅷ","Ⅸ","Ⅹ","Ⅺ","Ⅻ"] _index = _sort.index(_title) if _index10 and len(_text)<100: if _text not in dict_sentence_count: dict_sentence_count[_text] = 0 dict_sentence_count[_text] += 1 if re.search("\d+页",_text) is not None: illegal_sentence.add(_text) elif len(_text)<10: if re.search("第\d+页",_text) is not None: illegal_sentence.add(_text) sentence_groups = self.find_title_by_pattern(_text[:10]) if sentence_groups: # c062f53cf83401e671822003d63c1828print("sentence_groups",sentence_groups) sentence_title = sentence_groups[0][0] sentence_title_text = sentence_groups[0][1] title_index = sentence_groups[-2][1] title_before = sentence_groups[1][1].replace("(","(").replace(":",":").replace(":",";").replace(",",".").replace(",",".").replace("、",".") title_after = sentence_groups[-1][1].replace(")",")").replace(":",":").replace(":",";").replace(",",".").replace(",",".").replace("、",".") next_index = self.get_next_title(title_index) if title_before not in dict_before: dict_before[title_before] = 0 dict_before[title_before] += 1 for k,v in dict_sentence_count.items(): if v>10: illegal_sentence.add(k) return dict_before,illegal_sentence def is_page_no(self,sentence): if len(sentence)<10: if re.search("\d+页|^\-\d+\-$",sentence) is not None: return True def block_tree(self,childs): for child in childs: if not child["block"]: child["block"] = True childs2 = child["child_title"] self.block_tree(childs2) def buildParsetree(self,list_obj,products=[],auto_merge_table=True,auto_append=False): self.parseTree = None trees = [] list_length = [] for obj in list_obj[:200]: if obj.name!="table": list_length.append(len(obj.text)) if len(list_length)>0: max_length = max(list_length) else: max_length = 40 max_length = min(max_length,40) logger.debug("%s:%d"%("max_length",max_length)) list_data = [] last_table_index = None last_table_columns = None last_table = None dict_before,illegal_sentence = self.count_title_before(list_obj) for obj_i in range(len(list_obj)): obj = list_obj[obj_i] # logger.debug("==obj %s"%obj.text[:20]) _type = "sentence" _text = standard_product(obj.text) if obj.name=="table": _type = "table" _text = standard_product(str(obj)) _append = False sentence_title = None sentence_title_text = None sentence_groups = None title_index = None next_index = None parent_title = None title_before = None title_after = None title_next = None childs = [] # new sentence_index = obj.sentence_index wordOffset_begin = obj.wordOffset_begin wordOffset_end = obj.wordOffset_end sentences = obj.sentences list_kv = obj.get("kv",[]) table_id = obj.get("table_id") list_table = None block = False has_product = False position = obj.get("position",{}) if _type=="sentence": if _text in illegal_sentence: continue sentence_groups = self.find_title_by_pattern(_text[:10]) if sentence_groups: title_before = standard_title_context(sentence_groups[1][1]) title_after = sentence_groups[-1][1] sentence_title_text = sentence_groups[0][1] other_text = _text.replace(sentence_title_text,"") if (title_before in dict_before and dict_before[title_before]>1) or title_after!="": sentence_title = sentence_groups[0][0] title_index = sentence_groups[-2][1] next_index = self.get_next_title(title_index) other_text = _text.replace(sentence_title_text,"") for p in products: if other_text.strip()==p.strip(): has_product = True else: _fix = False for p in products: if other_text.strip()==p.strip(): title_before = "=产品" sentence_title = "title_0" sentence_title_text = p title_index = "0" title_after = "产品=" next_index = "0" _fix = True has_product = True break if not _fix: title_before = None title_after = None sentence_title_text = None else: if len(_text)<40 and re.search(_param_pattern,_text) is not None: for p in products: if _text.find(p)>=0: title_before = "=产品" sentence_title = "title_0" sentence_title_text = p title_index = "0" title_after = "产品=" next_index = "0" _fix = True has_product = True break # 合并两个非标题句子 20241106 注销,由于 485441521 招标内容结束位置不对 if auto_append: if _type=="sentence": 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: list_data[-1]["text"] += _text list_data[-1]["line_width"] = len(_text) update_kv_span(list_kv,len(_text)) list_data[-1]["kv"].extend(list_kv) list_data[-1]["sentences"].extend(sentences) _append = True elif sentence_title is None and len(list_data)>0 and _type==list_data[-1]["type"]: if list_data[-1]["line_width"]>=max_length*0.7: list_data[-1]["text"] += _text list_data[-1]["line_width"] = len(_text) update_kv_span(list_kv,len(_text)) list_data[-1]["kv"].extend(list_kv) list_data[-1]["sentences"].extend(sentences) _append = True if not _append: _data = {"type":_type,"tag":obj.get("tag"),"table_id":table_id, "text":_text,"sentences":sentences,"list_table":list_table, "line_width":len(_text),"sentence_title":sentence_title,"title_index":title_index, "sentence_title_text":sentence_title_text,"sentence_groups":sentence_groups,"parent_title":parent_title, "child_title":childs,"title_before":title_before,"title_after":title_after,"title_next":title_next,"next_index":next_index, "block":block,"has_product":has_product, "sentence_index":sentence_index,"wordOffset_begin":wordOffset_begin,"wordOffset_end":wordOffset_end, "kv":list_kv,"position":position } if sentence_title is not None: if len(list_data)>0: if self.is_title_first(title_index): for i in range(1,len(list_data)+1): _d = list_data[-i] if _d["sentence_title"] is not None: _data["parent_title"] = _d _d["child_title"].append(_data) break else: _find = False for i in range(1,len(list_data)+1): if _find: break _d = list_data[-i] if _d.get("sentence_title")==sentence_title and title_before==_d["title_before"] and title_after==_d["title_after"]: if _d["next_index"]==title_index and _d["title_next"] is None and not _d["block"]: _data["parent_title"] = _d["parent_title"] _d["title_next"] = _data if len(_d["child_title"])>0: _d["child_title"][-1]["title_next"] = "" self.block_tree(_d["child_title"]) if _d["parent_title"] is not None: _d["parent_title"]["child_title"].append(_data) _find = True break for i in range(1,len(list_data)+1): if _find: break _d = list_data[-i] 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"]: _data["parent_title"] = _d["parent_title"] _d["title_next"] = _data if len(_d["child_title"])>0: _d["child_title"][-1]["title_next"] = "" self.block_tree(_d["child_title"]) if _d["parent_title"] is not None: _d["parent_title"]["child_title"].append(_data) _find = True break title_before = standard_title_context(title_before) title_after = standard_title_context(title_after) for i in range(1,len(list_data)+1): if _find: break _d = list_data[-i] 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"]): if _d["next_index"]==title_index and _d["title_next"] is None and not _d["block"]: _data["parent_title"] = _d["parent_title"] _d["title_next"] = _data if len(_d["child_title"])>0: _d["child_title"][-1]["title_next"] = "" self.block_tree(_d["child_title"]) if _d["parent_title"] is not None: _d["parent_title"]["child_title"].append(_data) _find = True break for i in range(1,len(list_data)+1): if _find: break _d = list_data[-i] 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"]): _data["parent_title"] = _d["parent_title"] _d["title_next"] = _data if len(_d["child_title"])>0: _d["child_title"][-1]["title_next"] = "" # self.block_tree(_d["child_title"]) if _d["parent_title"] is not None: _d["parent_title"]["child_title"].append(_data) _find = True break for i in range(1,min(len(list_data)+1,20)): if _find: break _d = list_data[-i] if not _d["block"] and _d.get("sentence_title")==sentence_title and title_before==standard_title_context(_d["title_before"]): _data["parent_title"] = _d["parent_title"] _d["title_next"] = _data if len(_d["child_title"])>0: _d["child_title"][-1]["title_next"] = "" # self.block_tree(_d["child_title"]) if _d["parent_title"] is not None: _d["parent_title"]["child_title"].append(_data) _find = True break if not _find: if len(list_data)>0: for i in range(1,len(list_data)+1): _d = list_data[-i] if _d.get("sentence_title") is not None: _data["parent_title"] = _d _d["child_title"].append(_data) break else: if len(list_data)>0: for i in range(1,len(list_data)+1): _d = list_data[-i] if _d.get("sentence_title") is not None: _data["parent_title"] = _d _d["child_title"].append(_data) break list_data.append(_data) for _data in list_data: childs = _data["child_title"] for c_i in range(len(childs)): cdata = childs[c_i] if cdata["has_product"]: continue else: if c_i>0: last_cdata = childs[c_i-1] 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"]: cdata["has_product"] = True if c_i0: last_cdata = childs[c_i-1] 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"]: cdata["has_product"] = True if c_i
张家港励晟塑胶制品有限公司塑料制品迁建项目竣工环境保护验收公示

yanshoubaogao.pdf


张家港励晟塑胶制品有限公司塑料制品迁建
项目竣工环境保护验收监测报告
建设单位:张家港励晟塑胶制品有限公司
编制单位:张家港励晟塑胶制品有限公司
2023年06月,
目录
1项目概况
2验收依据
3项目建设情况
3.1地理位置及平面布置
3.2建设内容
3.3主要原辅材料
3.4生产工艺
3.5项目变动情况
4环境保护设施
4.1污染物治理/处置设施
5建设项目环评报告表主要结论及审批意见的要求
5.1建设项目环评报告表的主要结论
6验收执行标准
6.1废气评价标准
6.2噪声评价标准
6.3固废处置标准
7验收监测内容
7.1废水
7.2废气
7.3噪声
8质量保证及质量控制
8.1监测分析方法
8.2监测仪器
8.3人员能力
8.4监测分析过程中的质量保证和质量控制
9验收监测工况及要求
10验收监测结果及分析评价
10.1废气监测结果及分析评价
10.2噪声监测结果及分析评价
10.3污染物排放总量核算
11环评及审批意见执行情况
12验收监测结论和建议
12.1验收监测结论
12.2建议
1项目概况
张家港励晟塑胶制品有限公司原厂位于凤凰镇杏市村,现搬迁至凤凰镇西参
村,主要从事塑料制品的生产制造。项目于2019年12月26日在张家港市凤凰镇人
民政府备案,备案文号:张凤申备[2019]134号。本项目于2020年7月委托江苏绿
源工程设计研究有限公司编制完成了《张家港励晟塑胶制品有限公司塑料制品迁
建项目环境影响报告表》,并于2020年10月27日通过苏州市行政审批局审批(文
号:苏环审环评[2020]10268号),该项目于2020年11月建成。
本项目环评设计建设新增吹膜机、手套机、造粒机、流延机等,年产塑料制
品20亿件。本次验收范围为张家港励晟塑胶制品有限公司塑料制品迁建项目。
本项目概况见表1-1。
表1-1项目概况表
建设项目塑料制品迁建项目塑料制品迁建项目塑料制品迁建项目
建设单位张家港励晟塑胶制品有限公司张家港励晟塑胶制品有限公司张家港励晟塑胶制品有限公司
建设项目性质新建扩建技改迁建√行业类别C2929塑料零件及其他塑料制品制造
建设地点张家港市凤凰镇西参村张家港市凤凰镇西参村张家港市凤凰镇西参村
环评编制单位江苏绿源工程设计研究有限公司环评编制时间2020年7月
环评审批单位苏州市行政审批局环评审批时间2020年10月27日
开工时间2020年11月调试时间2020年11月
排污许可登记编号91320582689615143D001W有效期2023年4月28日至2028年4月27日
环评设计能力年产塑料制品20亿件年产塑料制品20亿件年产塑料制品20亿件
实际建设能力年产塑料制品20亿件年产塑料制品20亿件年产塑料制品20亿件
现场验收监测时间2022年10月24日至2022年10月25日2022年10月24日至2022年10月25日2022年10月24日至2022年10月25日
张家港励晟塑胶制品有限公司,第1页共20页
2验收依据
2.1建设项目环境保护相关法律、法规和规章制度
(1)《中华人民共和国环境保护法》(2015年1月1日起施行)
(2)《中华人民共和国水污染防治法》(2018年1月1日起施行);
(3)《中华人民共和国大气污染防治法》(2018年10月26日施行);
(4)《中华人民共和国环境噪声污染防治法》(2022年6月5日起施行);
(5)《中华人民共和国固体废物污染环境防治法》(2020年9日1日起施行);
(6)《建设项目环境保护管理条例》(中华人民共和国国务院令第682号,2017年
7月16日)。
2.2建设项目竣工环境保护验收技术规范
(1)《建设项目竣工环境保护验收管理办法》(原国家环境保护总局令第13
号,2001年12月27日)
(2)《关于规范建设单位自主开展建设项目竣工环境保护验收的通知(征求
意见稿)》意见的通知(环办环评函[2017]1235号,2017年8月3日);
(3)《建设项目竣工环境保护验收技术指南污染影响类》意见的通知(生
态环境部2018年第9号公告,2018年5月15日);
(4)关于印发《污染影响类建设项目重大变动清单(试行)》的通知(环办
环评函[2020]688号);
(5)《省生态环境厅关于加强涉变动项目环评与排污许可管理衔接的通知》
(苏环办[2021]122号);
(6)《排污单位自行监测技术指南总则》(HJ819-2017)。
2.3建设项目环境影响报告书(表)及其审批部门审批决定:
(1)《张家港励晟塑胶制品有限公司塑料制品迁建项目环境影响报告表》
(江苏绿源工程设计研究有限公司,2020年7月);
(2)苏州市行政审批局关于对张家港励晟塑胶制品有限公司塑料制品迁建项
目环境影响报告表的审批意见(2020年10月27日)。
2.4其他相关文件。
(1)《排污许可管理条例》(中华人民共和国国务院令第736号,2021年1月
24日);
张家港励晟塑胶制品有限公司,第2页共20页
(2)《国家危险废物名录(2021年版)》(部令第15号,2020年11月25日);
(3)《一般工业固体废物贮存和填埋污染控制标准》(GB18599-2020)。
(4)《危险废物贮存污染控制标准》(GB18597-2023)。
3项目建设情况
3.1地理位置及平面布置
本项目位于张家港市凤凰镇西参村。项目厂界西侧为天源塑业,南侧为张家
港东旭针织服饰有限公司,北侧为西塘公路,隔路为张家港市佳暖纺织有限公司。
本项目地理位置、周边环境概况、平面布置及监测点位见下图(噪声测点点
位见附件检测报告)。,
张家港励晟塑胶制品有限公司,第3页共20页
张家港行政图
双山备山
本项目位置常代
业地国
图3.1-1地理位置图
西社区居民约40户
阳百社区
以生产车间为边界向外
设置50m卫生防护距离
港东旭针
西公路
家堂居民10户
以造粒车间为边界向外图例
设置100m卫生防护距离
本项目位置
L=300米
比例尺:
0259075100me-5
污水管网
图3.1-2周边环境图
张家港励晟塑胶制品有限公司,第4页共20页
图3.1-3平面布置及监测点位图
张家港励晟塑胶制品有限公司,第5页共20页
3.2建设内容
(1)原有项目回顾
与本项目有关的企业原有已批已建的项目情况如下:
表3.2-1与本项目有关的原有项目情况
建设地点项目名称建设内容环评批复及时间验收批复及时间备注
杏市村“塑料制品、橡胶制品项目”环境影响登记表年产塑料制品1000吨2009.04.10通过张家港市环境保护局审批意见-已搬迁
杏市村“年产塑料制品1000吨项目”自查评估报告年产塑料制品1000吨2016.11.08通过张家港市环境保护局审批意见-已搬迁
(2)本项目情况
本项目建设情况见表3.2-2,产品方案见表3.2-3,主要生产设备见表3.2-4,
公辅工程见表3.2-5。
表3.2-2本项目建设情况
类型环评/审批项目内容实际建设情况
地理位置本项目位于张家港市凤凰镇西参村。与环评一致
总投资总投资500万元人民币,其中环保投资50万元。与环评一致
定员与生产制度本项目不新增员工,全厂职工40人,年工作300天,实行两班制,每班10h,全年工作6000小时。与环评不一致。全厂职工40人,年工作300天,实行两班制,每班8h,全年工作4800h。
占地面积占地面积2500平方米,建筑面积4000平方米。与环评一致
表3.2-3本项目产品方案一览表
序号名称本项目产能(亿件)本项目产能(亿件)储存方式及地点实际建设
序号名称环评设计实际建设储存方式及地点实际建设
1塑料制品2020仓库与环评一致
表3.2-4本项目主要生产设备及数量
序号设备名称设备规格(型号)设备数量(台)设备数量(台)备注
序号设备名称设备规格(型号)环评设计实际建设备注
1混料机/610新增4台
2吹膜机/1818与环评一致
3手套机/3230减少2台
4造粒机/44与环评一致
5冲床/13新增2台
6流延机/32减少1台
7空压机/33与环评一致
表3.2-5本项目公辅工程
类别建设名称建设名称设计能力环评设计实际建设
主体工程生产车间生产车间2500m2从事生产活动与环评一致
主体工程办公区办公区200m2位于生产车间内,从事办公活动位于车间二楼,面积不变
储运工程仓库仓库100m2位于生产车间内,用于成品堆放与环评一致
公用工程供水生活用水720t/a经化粪池预处理后接管至张家港市给排水有限公司塘桥片区污水处理厂处理由于本项目厂区生活污水管网尚未接通,项目生活污水只能汇入村里集中化粪池,由村里负责委托污水厂定期拖运
公用工程供水冷却循环水50t/a循环回用,不外排与环评一致
公用工程供电供电14万kwh/a当地电网与环评一致
环保工程废气处理废气处理干式过滤+活性炭吸附1套与环评一致
环保工程生活废水生活废水化粪池经化粪池预处理后接管至张家港市给排水有限公司塘桥片区污水处理厂处理与环评一致
环保工程噪声处理噪声处理隔声降噪措施隔声量≥25dB(A)与环评一致
环保工程固废处理固废处理一般固废堆场10m2与环评一致
环保工程固废处理固废处理危废仓库10m2与环评一致
3.3主要原辅材料
表3.2-6本项目原辅材料用量表
序号名称使用量(t/a)使用量(t/a)储存方式包装规格实际建设
序号名称环评设计实际建设储存方式包装规格实际建设
1PE塑料粒子10001000仓库存储袋装与环评一致
2色母粒1010仓库存储袋装与环评一致
张家港励晟塑胶制品有限公司,第7页共20页
3.4生产工艺
3.4.1生产工艺流程图
图3.4-1本项目工艺流程图
工艺流程简述:
混料:根据生产需要PE粒子和色母粒按比例投入料斗混合,此工序产生集线
器噪声N1;
吹膜:塑料粒子加热融化,工艺的加热温度为130℃~160℃,将塑料挤成薄管,
然后趁热将塑料吹胀,此工序产生机器噪声N2以及有机废气G1;
流延:通过三层共挤流延成薄膜,流延机加热温度200℃,此工序产生一定的
机器噪声N3以及有机废气G2;
剪切、冲压:根据产品需求对薄膜进行剪切,其中用于制作手套的薄膜需进
行冲压处理,此工序产生机器噪声N4;
封口、包装:部分薄膜经手套制作(电熔、烫口等)后封口包装入库,其他
产品直接封口包装入库,此工序产生机器噪声N5以及少量得有机废气G3;
造粒:剪切、冲压产生的边角料重新进行造粒后回用。造粒使用边角料,造
张家港励晟塑胶制品有限公司,第8页共20页
G3下风向第一次0.390.400.1590.141
G3下风向第二次0.410.360.1240.176
G3下风向第三次0.400.380.2130.159
G4下风向第一次0.400.360.1410.106
G4下风向第二次0.400.340.1950.123
G4下风向第三次0.400.470.1600.177
最大值最大值0.410.470.2130.212
标准值标准值4.04.01.01.0
达标情况达标情况达标达标达标达标
表10.1-3厂区内无组织排放废气监测结果统计表单位:mg/m³
监测点位频次非甲烷总烃非甲烷总烃
监测点位频次10月24日10月25日
G5厂区车间门外1m处第一次0.430.48
G5厂区车间门外1m处第二次0.430.40
G5厂区车间门外1m处第三次0.400.39
G5厂区车间门外1m处均值0.420.42
G5厂区车间门外1m处最大值0.430.48
G5厂区车间门外1m处标准值66
G5厂区车间门外1m处达标情况达标达标
10.1.3结果评价
监测结果表明:验收监测期间,本项目废气排放浓度最大值排放浓度均达到
《合成树脂工业污染物排放标准》(GB31572-2015)、《大气污染物综合排放标
准》(GB16297-1996)以及江苏省《大气污染物综合排放标准》(DB32/4041-
2021)限值要求。
10.2噪声监测结果及分析评价
10.2.1本项目噪声监测结果见表10.2-1。监测点位见附图。
表10.2-1厂界环境噪声监测结果汇总表LeqdB(A)
测点编号测点名称监测时间监测时间监测时间监测时间
测点编号测点名称2022.10.242022.10.242022.10.252022.10.25
测点编号测点名称昼间夜间昼间夜间
N1项目东侧厂界外1米53.748.454.048.5
N2项目南侧厂界外1米54.549.054.849.3
N3项目西侧厂界外1米52.847.352.747.3
N4项目北侧厂界外1米53.347.853.548.0
标准限值标准限值60506050
达标情况达标情况达标达标达标达标
10.2.2结果评价
监测结果表明:验收监测期间,本项目厂界环境噪声测点昼夜间等效声级值
均满足《工业企业厂界环境噪声排放标准》(GB12348-2008)2类标准限值要求。
10.3污染物排放总量核算
10.3.1废水污染物排放总量
由于本项目用水简单,用水仅为员工生活用水,无工业废水外排,因为本项
目不对废水情况进行检测分析。具体见表10.3-1。
表10.3-1废水污染物排放总量与控制指标对照
污染物排放口污染物排放口废水量化学需氧量悬浮物氨氮总磷
生活污水排放口S1排放浓度(mg/L)/////
生活污水排放口S1排放量(t/a)/////
环评批复接管总量(t/a)环评批复接管总量(t/a)5760.230.1150.020.002
备注:由于本项目用水简单,全厂员工40人,用水仅为员工生活用水,无工业废水外排,因此本项目不对废水情况进行检测分析。备注:由于本项目用水简单,全厂员工40人,用水仅为员工生活用水,无工业废水外排,因此本项目不对废水情况进行检测分析。备注:由于本项目用水简单,全厂员工40人,用水仅为员工生活用水,无工业废水外排,因此本项目不对废水情况进行检测分析。备注:由于本项目用水简单,全厂员工40人,用水仅为员工生活用水,无工业废水外排,因此本项目不对废水情况进行检测分析。备注:由于本项目用水简单,全厂员工40人,用水仅为员工生活用水,无工业废水外排,因此本项目不对废水情况进行检测分析。备注:由于本项目用水简单,全厂员工40人,用水仅为员工生活用水,无工业废水外排,因此本项目不对废水情况进行检测分析。备注:由于本项目用水简单,全厂员工40人,用水仅为员工生活用水,无工业废水外排,因此本项目不对废水情况进行检测分析。
10.3.2废气污染物排放总量
本项目废气主要为吹膜、流延、封口和造粒工序产生的有机废气以及粉尘,
项目废气经1套干式过滤(过滤棉)+二级活性炭吸附装置处理后通过1根15m高排
气筒排放。根据本次验收监测结果计算废气污染物排放总量,本项目废气总量满
足环评批复要求。具体见表10.3-2。
表10.3-2废气污染物排放总量与控制指标对照
污染物排放口污染物排放口非甲烷总烃颗粒物
有组织废气排放口P1排放浓度(mg/m³)0.381.5
有组织废气排放口P1排放速率(kg/h)0.00470.013
有组织废气排放口P1排放量(t/a)0.01130.0039(造粒年工作时间约300h)
环评批复总量(t/a)环评批复总量(t/a)0.03780.0045
备注:/备注:/备注:/备注:/
11环评及审批意见执行情况
张家港励晟塑胶制品有限公司塑料制品迁建项目环境影响报告表及审批意见
张家港励晟塑胶制品有限公司,第17页共20页
执行情况见表11.1-1。
表11.1-1环评报告表及其审批意见执行情况一览表
序号环评审批意见执行情况
1项目位于凤凰镇西参村,搬迁项目投资500万元,新增相应生产设备进行生产,项目搬迁投产后,全厂年产PE手套等各类塑料制品20亿件。本项目建设地址、投资额、成品情况与环评相一致。设备较环评新增4台混料机、2台手套机、2台冲床,减少1台流延机。
2本项目生活污水接管至张家港市给排水公司塘桥片区污水处理厂集中处理,达标排放。本项目厂区生活污水外管网尚未接通,项目生活污水只能汇入村里集中化粪池,由村里负责委托污水厂定期拖运。
3本项目吹膜、流延、封口、造粒产生的有机废气和颗粒物经集气罩收集后经1套干式过滤(过滤棉)+活性炭吸附装置处理后通过一根15米高排气筒排放,以上废气执行相应项目有机废气和颗粒物经集气罩收集后经1套干式过滤(过滤棉)+二级活性炭吸附装置处理后通过一根15米高排气筒达标排放。
4制定有效措施控制项目运营期的噪声,厂界噪声排放执行《工业企业厂界环境噪声排放标准》(GB12348-2008)2类。本项目合理布局厂区,采用低噪声设备,高噪音设备采取了相应的减振、隔声等降噪措施。根据监测结果表明:验收监测期间,该公司厂界环境噪声测点昼、夜间等效声级值均满足《工业企业厂界环境噪声排放标准》(GB12348-2008)2类标准限值要求。
5制定和落实固体废物的厂内收集和贮存、综合利用、安全处置的实施方案,实现“零排放”。本项目在厂区内设置一个危废仓库,一般固废收集后外卖,危险固废委托有资质单位处理,实现“零排放”。
6该项目实施后,建设单位应落实环评文件提出的以生产车间为边界向外设置50米卫生防护距离,以造粒车间为边界向外设置100米卫生防护距离的要求。本项目卫生防护距离范围内无居民等环境敏感目标。
7严格落实环境风险的防范措施,避免风险事故。建设单位应强化环境风险意识,从技术、工艺、管理等方面加强落实防范措施。公司在后续经营过程中会不断强化环境风险意识,从技术、工艺、管理等方面加强落实防范措施。
8该项目在设计、施工建设和生产中总平面布局以及主要工艺设备、储运设施、公辅工程、污染防治设施安装、使用中涉及安全生产的应遵守设计使用规范和相关主管部门要求;建设单位应对环境治理设施开展安全风险辨识管控,要健全内部污染防治设施稳定运行和管理责任制度,严格依据标准规范建设环境治理设施,确保环境治理设施安全、稳定、有效运行。公司在建设过程中严格按照设计使用规范和相关主管部门要求开展。建立内部污染防治设施稳定运行和管理责任制度,严格依据标准规范建设环境治理设施。
9按《江苏省排污口设置及规范化整治管理办法》(苏环控(1997)122号)的要求完善各类排污口和标志设置。本项目排放口均按照要求完善标志设置。
10严格落实《报告表》提出的监测计划。根据《固定污染源排污许可分类管理名录(2019年版)》本项目属于二十四、橡胶和塑料制品业29-62塑料制品业-其他,实行登记管理,建设单位已完成排污登记并生成排污编号。
11控制设备调试期间的噪声污染,应尽量采用低噪声的器械,避免夜间进行高噪声污染,减轻对厂界周围声环境的影响。本项目均采用低噪声设备,夜间生产噪声污染较小,对厂界周围声环境影响不大。
12你公司应当按照《排污许可管理条例》规定,及时申请排污许可证;未取得排污许可证的,不得排放污染物。按照《建设项目竣工环境保护验收暂行办法》办理环保设施竣工验收手续。需要配套建设的环境保护设施未建成,未经验收或者经验收不合格,建设项目已投入生产或者使用的,生态环境部门将依法进行查处。公司已取得排污许可证,配套建设的环境保护设施经验收合格后投入生产。
13建设单位是该建设项目环境信息公开的主体,须自收到我局批复后及时将项目报告表的最终版本予以公开。同时应按照《建设项目环境影响评价信息公开机制方案》(环发[2015]162号)做好建设项目开工前、施工期和建成后的信息公开工作。建设严格落实环评批复内容,公开项目建设情况。
14如该项目所涉及污染物排放标准发生变化,应执行最新的排放标准。验收监测期间本项目执行标准未发生变化,后续将严格按照标准限值要求执行。若标准发生变化,将严格按照最新标准执行。
15该项目在建设过程中若项目的性质、规模、地点,采用的生产工艺或者防治污染、防止生态破坏的措施、设施发生重大变动的,应当重新报批项目的环境影响评价文件。自批准之日起,如超过5年方决定工程开工建设的,环境影响评价文件须报重新审核。本项目于2020年10月27日取得苏州市行政审批局批复意见,并于2022年11月设备进厂调试,建设内容基本与环评相一致,无重大变动。
12验收监测结论和建议
12.1验收监测结论
验收监测期间(2022年10月24日-25日)本项目生产正常,各项环保治理设施
均运转正常,满足验收监测要求。
验收监测期间,本项目废气排放均满足《合成树脂工业污染物排放标准》
(GB31572-2015)标准限值。
验收监测期间,本项目厂界环境噪声测点昼、夜间等效声级值均满足《工业
企业厂界环境噪声排放标准》(GB12348-2008)2类标准限值要求。
验收监测期间,本项目危险固废均得到有效处置,实现零排放。
12.2建议
1、进一步加强危废仓库的管理,做好台账记录;
2、做好高噪声设备的降噪措施,尽量减少对周边环境的影响;
3、加强管理,进一步提高公司员工的环境意识,倡导清洁生产,并加强各种
原料的储存、运送管理,制定严格的规章制度。
附件:
1、张家港励晟塑胶制品有限公司塑料制品迁建项目环境影响报告表的审批意见;
2、张家港励晟塑胶制品有限公司排水许可证;
3、张家港励晟塑胶制品有限公司生活垃圾处理协议;
4、张家港励晟塑胶制品有限公司塑料制品迁建项目验收委托检测报告;
5、苏州捷盈环境检测有限公司检验检测机构资质认定证书。
张家港励晟塑胶制品有限公司,第20页共20页
张家港励晟塑胶制品有限公司塑料制品迁建项目竣工环境保护验收
验收组人员名单
38电市风国续西叁社时间:2023.7.2
地点:
姓名单位名称职务/职称联系电话签名签名
陕到国张家港的晟至级制项关经理136062264132222
上怡苏洲提盈环境检有限司技术员5950947777
北卷市约里261名了前2司投求台13061661423WIY
史提13805561786
张家港励晟塑胶制品有限公司塑料制品迁建项目竣工环境保护验收
意见
根据《建设项目环境保护管理条例》规定,2023年7月27日,张家港
励晟塑胶制品有限公司组织验收报告编制单位(张家港市维恩环境咨询
有限公司)、验收监测单位(苏州捷盈环境检测有限公司)的代表以及专
家组成验收工作组,对公司“塑料制品迁建项目”进行竣工环境保护验
收。验收工作组听取了建设单位对项目环境保护执行情况的汇报和验收
监测单位对项目竣工环境保护验收监测情况的汇报,踏勘了建设项目现
场,审阅并核实了有关资料,经认真讨论,形成竣工环保验收意见如下:
一、工程建设基本情况
(一)建设地点、规模、主要建设内容
建设地点:位于张家港市凤凰镇西参村。
建设规模及主要建设内容:本项目原厂位于凤凰镇杏市村,现搬迁
至凤凰镇西参村,项目投资500万元,将原厂的生产设备搬入新厂房内,
从事塑料制品的生产制造。搬迁后年产量不变,年产塑料制品20亿件,
主要为一次性塑料用品。全厂员工40人,实行两班16小时工作制,年有
效工作日为300天,年运行时数4800小时。
(二)建设过程及环保审批情况
项目于2020年7月委托江苏绿源工程设计研究有限公司编制了《塑料
制品迁建项目环境影响报告表》,于2020年10月27日通过苏州市生态环
境局审批(苏行审环评[2020]10268号)。2020年11月建成。
(三)投资情况
项目总投资500万元人民币,其中环保投资50万元,占总投资比例
10%。
(四)验收范围
本次对张家港励晟塑胶制品有限公司塑料制品迁建项目(苏行审环评
第1页共4页
[2020]10268号)进行验收。
二、工程变动情况
依据环评报告表、环评批复等材料,对项目实际建设相关内容进行
梳理,项目实际生产设备较环评增加4台混料机、2台冲床,减少2台手
套机、1台流延机。设备增加或减少均不导致废气、废水污染物增加,因
此不属于重大变动。
三、环境保护设施建设情况
(一)废水
本项目生活污水汇入村里集中化粪池,由村里负责集中托运;本项
目造粒工段使用的冷却水通过冷却塔循环回用,不外排。
(二)废气
本项目吹膜、流延、封口、造粒产生的有机废气和颗粒物经集气罩
收集后经1套干式过滤(过滤棉)+活性炭吸附装置处理后通过一根15米
高排气筒排放。
(三)噪声
本项目噪声主要为生产设备运行时产生的噪声,采取的噪声治理措
施由设施布置于室内,厂区四周墙体采用实体墙,噪声源强相对较高的
设备加装消声减振器或者隔声屏障,日常生产时加强科学管理。
(四)固体废物
本项目产生的固废主要包括:边角料、废活性炭、废过滤棉。
1、一般工业固废中部分边角料回用于生产,其余全部外售综合利用。
2、危险固废:委托有资质单位处理。
3、生活垃圾委托环卫部门处置。
四、环境保护设施调试效果
苏州捷盈环境检测有限公司于2022年10月24日-25日对项目进行了现
场验收监测。验收监测期间生产正常,满足规范化监测的要求。
第2页共4页
(一)污染物排放情况
1、废气
本项目废气排放均满足《合成树脂工业污染物排放标准》
(GB31572-2015)、江苏省《大气污染物综合排放标准》(DB32/4041-
2021)标准限值要求。
2、厂界噪声
本项目厂界环境噪声各测点昼、夜间等效声级值均满足《工业企业
厂界环境噪声排放标准》(GB12348-2008)2类标准限值要求。
3、固废
本项目固体废物均得到有效处置,实现“零排放”。
4、卫生防护距离
本项目以造粒车间为界设置的100m、以生产车间为界设置的50m卫生
防护距离内无环境敏感目标。
5、排污相关
本项目排放的污染物总量满足环评批复的要求。排污许可本项目已于
2023年4月28日取得了排污许可。
五、验收结论
项目执行了环保“三同时”制度,落实了环评及批复要求的污染防治
措施,环保设施运行正常,主要污染物达标排放。对照《建设项目竣工
环境保护验收暂行办法》,验收工作组认为:本项目废水、废气、噪声、
固废等污染设施措施满足环评及批复的要求,同意通过本项目竣工环境
保护“三同时”验收。
六、后续要求
1、进一步加强固物的规范化管理,确保每批次可追溯;
2、进一步加强废气废水治理设施的管理,确保持续稳定正常的运行;
3、进一步加强规范化的监测,确保排放的污染物稳定达标;
第3页共4页
4、进一步强化公司的环境安全应急预案,杜绝因意外事故诱发的环
境(安全)二次污染。
七、验收人员信息
验收人员名单附后。
张家港励晟塑胶制品有限公司
2023年7月27日
第4页共4页
""" _tree = html_to_tree(html_content) _pd = Html2KVTree(html_content) _pd.print_tree(_pd.tree,"-|") list_kv = _pd.extract_kv("建设单位") print(list_kv) #获取预处理后的所有句子,该句子与kv值对应 print(_pd.get_tree_sentence()) # soup = BeautifulSoup(html_content,"lxml") # table_tree = table_to_tree(soup) # print(json.dumps(table_tree,ensure_ascii=False))