html_again_parse.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. # -*- coding: utf-8 -*-
  2. import re
  3. from pprint import pprint
  4. from pyquery import PyQuery as pq
  5. #
  6. # pattern = re.compile(r"\[来源.*?\]|www\..*?com")
  7. #
  8. # filter_words = ["学科网", "高考资源网", "Ziyuanku.com", "WWW.ziyuanku.com",
  9. # "高☆考♂资♀源€网", "w.w.w.k.s.5.u.c.o.m", "本资料由《七彩教育网》www.7caiedu.cn 提供!",
  10. # "本资料来源于《七彩教育网》http://www.7caiedu.cn", "本资料由《七彩教育网》www.7caiedu.cn 提",
  11. # "高考试题来源:"]
  12. #
  13. #
  14. # def filter_word(txt_list):
  15. # new_txt_list = []
  16. # for word in txt_list:
  17. # if not word.strip():
  18. # continue
  19. # new_word = re.sub(pattern, "", word)
  20. # for keys in filter_words:
  21. # if keys in new_word:
  22. # new_word = new_word.replace(keys, "")
  23. # new_txt_list.append(new_word)
  24. # return new_txt_list
  25. def filter_data(x):
  26. if not str(x).replace(" ", "").strip():
  27. pass
  28. else:
  29. return str(x)
  30. def replace_k(con):
  31. # con = str(con).replace(" ", "+")
  32. # con = str(con).replace(" ", "+")
  33. con = re.sub(r'\s(?!(src="http|_src="http|class="tiankong"|data-num=))', "#+#", str(con))
  34. return pq(con, parser="html")
  35. def css_label_wash(content):
  36. # todo add 9-4
  37. """
  38. 清洗文本中带有的css标签
  39. :param content:
  40. :return:
  41. """
  42. # temp_con = re.sub('</?p(\s*|\s+style=.*?")?>', "", str(content))
  43. if re.search('</?(span|font|article|ul|ol|div)(\s*|\s+style=.*?")>|text\s*-\s*decoration: underline|border\s*-\s*bottom:', str(content)) is None:
  44. # content = re.sub('</p\s*>', "\n", content).strip().replace("\n\n", "\n").replace("\n", "<br/>")
  45. def subp(s):
  46. cons = s.group(1)
  47. if '<p style="text-indent:' in cons or '<p style="text-align:' in cons:
  48. return cons
  49. else:
  50. return "<p>"
  51. content = re.sub('(<p(\s*|\s+style=.*?")>)', subp, content)
  52. content = re.sub('</p><p>', "</p>\n<p>", content)
  53. return content
  54. else:
  55. content = re.sub(r'<font\s+style="color: red">', "", str(content))
  56. content = re.sub(r'<span\s+style="color: red">', "", str(content))
  57. content = re.sub(r'<span\s+style="color: blue">', "", str(content))
  58. content = re.sub(r'<font\s+style="color: blue">', "", str(content))
  59. content = content.replace("</font >", "").replace("</font>", "")
  60. # content = content.replace("</span >", "").replace("</span>", "")
  61. content = content.replace('<p style="height: 0;">&nbsp;</p>', "\n").replace('<p><br/></p>', "\n")
  62. content = content.replace("</p>", "</p>\n")
  63. # content = re.sub('<p( style=.*?")?>', "", content)
  64. # content = re.sub('<br\s*/?>', "\n", content)
  65. # parm = False
  66. # if "<article>" not in str(content):
  67. # parm = True
  68. subs2img = {}
  69. if re.search('<img.*? src="', content) or "</table>" in content:
  70. all_imgs = re.findall('<img.*? src=.*?[\s/"]>|<table.*?>.*?</table>',
  71. content, flags=re.S)
  72. for k, img in enumerate(all_imgs):
  73. content = content.replace(img, "&{}&".format(k))
  74. subs2img["&{}&".format(k)] = img
  75. content = re.sub(r"<(su[bp])>(.*?)</(su[bp])>", r"【\1】\2【/\3】", content)
  76. html = pq(content, parser="html")
  77. a = []
  78. if html.children():
  79. # temph = [str(i) for i in html.children().items()]
  80. for line in html.children().items(): # <p>.*?</p>里面的内容可能会被过滤掉
  81. test = str(line) # line.text()
  82. # 保留下划线及着重符标签 <span style="text-decoration: underline;">
  83. # 波浪线:<span style="text-decoration: underline wavy;">
  84. # pq会将多个空格换成一个
  85. # print(str(line))
  86. if '<span style="text-decoration: underline' in str(line) or '<span class="dots"' in str(line)\
  87. or re.search('<(p|div)( class="[a-z\-]+")? style="text-indent:'
  88. '|<(p|div)( class="[a-z\-]+")? style="text-align:|<strong>|<em>', str(line)):
  89. line = re.sub(r'<span style="text-decoration: underline(.*?">.+?)</span>', r"【1#\1##】", str(line))
  90. line = re.sub(r'<span class="dots">(.+?)</span>', r"【2#\1##】", str(line))
  91. line = re.sub(r'<(p style="text-(indent|align):.*?">.+?)</p>', r"【\1##3】", str(line))
  92. line = re.sub(r'<div( class="[a-z\-]+")?( style="text-(indent|align):.*?")( [a-z\d\-"=]+")?(>.+?)</div>',
  93. r"【div\2\5##3】", str(line), flags=re.S)
  94. # line = re.sub(r'<(strong|em)>(.+?)</\1>', r"【\1##\2##\1】", str(line))
  95. # 分开处理会比较好,嵌套的格式也能全处理
  96. line = re.sub(r'<strong>(.+?)</strong>', r"【strong##\1##strong】", str(line))
  97. line = re.sub(r'<em>(.+?)</em>', r"【em##\1##em】", str(line))
  98. # print(line)
  99. line = line.replace(" ", "【+】")
  100. line = pq(line)
  101. new_line = list(map(lambda x: str(x).replace("【1#", '<span style="text-decoration: underline')
  102. .replace("##】", "</span>").replace("【2#", '<span class="dots">')
  103. .replace("【p【+】style=", "<p style=").replace("【div【+】style=", "<p style=")
  104. .replace("【div【+】class=", "<p class=")
  105. .replace("##3】", "</p>").replace("【strong##", "<strong>").replace("【em##", "<em>")
  106. .replace("##strong】", "</strong>").replace("##em】", "</em>").replace("【+】", " "),
  107. line.text().split("\n")))
  108. a.extend(new_line)
  109. elif str(line).startswith("<p") and line.text().strip():
  110. if '<img src="http://' in str(line) or "text-decoration: underline" in str(line) or "border-bottom:" in str(line) or "text - decoration: underline" in str(line):
  111. # a.append(line.html().replace("<br />", "\n").replace("<br/>", "\n").replace("<br>", "\n") + "\n")
  112. a.append(line.html())
  113. else:
  114. line = replace_k(line)
  115. if re.search(r"\n|<br\s*/?>", str(line), flags=re.S):
  116. line = re.sub(r"\n|<br\s*/?>", "#*#", str(line), flags=re.S)
  117. line = pq(line)
  118. new_line = list(map(lambda x: str(x).replace("#+#", " "), line.text().split("#*#")))
  119. a.extend(new_line)
  120. else:
  121. if line.text().strip():
  122. a.append(line.text().replace("#+#", " "))
  123. elif "<article>" in str(line) and "</article>" in str(line):
  124. line = re.sub(r'<p.*?>', "", str(line.html()))
  125. b = line.replace('</p>', ""). \
  126. replace("<br>", "\n"). \
  127. replace("<br/>", "\n"). \
  128. replace("<br />", "\n"). \
  129. replace('<p style="height: 0;">&nbsp;</p>', "\n"). \
  130. replace('<p style="height: 0;"> </p>', "\n")
  131. b_list = b.split("\n")
  132. # b_list = list(filter(lambda x: str(x), b_list))
  133. b_list = list(filter(filter_data, b_list))
  134. b_list = list(map(lambda x: str(x), b_list))
  135. a.extend(b_list)
  136. elif str(line).startswith("<ul"):
  137. a.append(line.text())
  138. elif line.attr("class") == "slave-datas":
  139. for j, ss in enumerate(line.children().items()):
  140. if ss(".read-list-title").text():
  141. a.append(str(j + 1) + "." + ss(".read-list-title").text())
  142. if ss(".read-list-opt").text():
  143. a.append(ss(".read-list-opt").text().replace("\n", "\t"))
  144. if str(ss).startswith("<ul"):
  145. a.append(ss.text())
  146. elif str(line).startswith("<table"):
  147. a.append(str(line))
  148. elif str(line).startswith("<ol"):
  149. for i, ss in enumerate(line.children().items()):
  150. a.append(str(i + 1) + "." + ss.text())
  151. elif str(line).startswith("<br>") or str(line).startswith("<br/>"):
  152. a.append(str(line))
  153. else:
  154. # print('test:',line.text()) # 自动去掉了图片
  155. if line.text().strip():
  156. line = replace_k(line)
  157. # a.append(line.text().replace("+", "\xa0") + "\n")
  158. a.append(line.text().replace("#+#", " "))
  159. else:
  160. # if html.text().strip(): 把换行\n 都去掉了
  161. # a.append(html.text())
  162. # a.append(str(html)) # 会自动带上</?p> ;&变为&amp;
  163. a.append(content.strip())
  164. new_a = "\n".join(list(map(lambda x: x.strip(), a)))
  165. if subs2img:
  166. new_a = re.sub("|".join(subs2img.keys()), lambda x: subs2img[x.group()], new_a)
  167. # new_a = "<p>" + new_a.replace("\n\n", "\n").replace("\n", "</p>\n<p>") + "</p>"
  168. new_a = re.sub("(?<!</p>)\s*\n", "<br/>", new_a.replace("\n\n", "\n")) # 2024.6.13
  169. new_a = re.sub("<br/>(\n|<br/>)+", "<br/>", new_a)
  170. new_a = "<p>" + new_a + "</p>"
  171. new_a = re.sub(r'<p>(<p (class|style)=.+?)</p>$', r"\1", new_a, flags=re.S)
  172. # for sb, img in subs2img.items(): # 2021
  173. # new_a = new_a.replace(sb, img)
  174. # if parm:
  175. # new_a[0] = "\xa0" * 4 + new_a[0]
  176. new_a = re.sub(r"【(/?su[bp])】", r"<\1>", new_a)
  177. return new_a
  178. def again_parse(content):
  179. # todo add 9-4
  180. content = re.sub(r'<font\s+style="color: red">', "", str(content))
  181. content = re.sub(r'<span\s+style="color: red">', "", str(content))
  182. content = re.sub(r'<span\s+style="color: blue">', "", str(content))
  183. content = re.sub(r'<font\s+style="color: blue">', "", str(content))
  184. content = str(content).replace("</font >", "").replace("</font>", "")
  185. content = str(content).replace("</span >", "").replace("</span>", "")
  186. content = str(content).replace('<p style="height: 0;">&nbsp;</p>', "\n").replace('<p><br/></p>', "\n")
  187. # parm = False
  188. # if "<article>" not in str(content):
  189. # parm = True
  190. html = pq(content, parser="html")
  191. a = []
  192. if html.children():
  193. for line in html.children().items():
  194. if (str(line).startswith("<p") and line.text().strip()) or "http://zxhx-1302712961.cos.ap" in str(line):
  195. if 'http://zsytk2.zhixinhuixue.com/static/images' in str(line) \
  196. or "text-decoration: underline" in str(line) \
  197. or "border-bottom:" in str(line) \
  198. or "text - decoration: underline" in str(line) \
  199. or 'http://zxhx-1302712961.cos.ap' in str(line):
  200. a.append(line.html().replace("<br />", "\n").replace("<br/>", "\n").replace("<br>", "\n") + "\n")
  201. else:
  202. line = replace_k(line)
  203. if "<br>" in str(line) or "<br/>" in str(line) or "<br />" in str(line):
  204. line = str(line).replace("<br/>", "###").replace("<br>", "###").replace("<br />", "###")
  205. line = pq(line)
  206. new_line = list(map(lambda x: str(x).replace("+", "\xa0") + "\n", line.text().split("###")))
  207. a.extend(new_line)
  208. else:
  209. if line.text().strip():
  210. a.append(line.text().replace("+", "\xa0") + "\n")
  211. elif "<article>" in str(line) and "</article>" in str(line):
  212. line = re.sub(r'<p.*?>', "", str(line.html()))
  213. b = line.replace('</p>', ""). \
  214. replace("<br>", "\n"). \
  215. replace("<br/>", "\n"). \
  216. replace("<br />", "\n"). \
  217. replace('<p style="height: 0;">&nbsp;</p>', "\n"). \
  218. replace('<p style="height: 0;"> </p>', "\n")
  219. b_list = b.split("\n")
  220. # b_list = list(filter(lambda x: str(x), b_list))
  221. b_list = list(filter(filter_data, b_list))
  222. b_list = list(map(lambda x: str(x) + "\n", b_list))
  223. a.extend(b_list)
  224. elif str(line).startswith("<ul"):
  225. a.append(line.text() + "\n")
  226. elif line.attr("class") == "slave-datas":
  227. for j, ss in enumerate(line.children().items()):
  228. if ss(".read-list-title").text():
  229. a.append(str(j + 1) + "." + ss(".read-list-title").text() + "\n")
  230. if ss(".read-list-opt").text():
  231. a.append(ss(".read-list-opt").text().replace("\n", "\t") + "\n")
  232. if str(ss).startswith("<ul"):
  233. a.append(ss.text() + "\n")
  234. elif str(line).startswith("<table"):
  235. a.append(str(line) + "\n")
  236. elif str(line).startswith("<ol"):
  237. for i, ss in enumerate(line.children().items()):
  238. a.append(str(i + 1) + "." + ss.text() + "\n")
  239. else:
  240. if line.text().strip():
  241. line = replace_k(line)
  242. a.append(line.text().replace("+", "\xa0") + "\n")
  243. else:
  244. if html.text().strip():
  245. a.append(html.text() + "\n")
  246. a = "\n".join(a).split("\n")
  247. new_a = list(filter(lambda x: x.strip(), a))
  248. # if parm:
  249. # new_a[0] = "\xa0" * 4 + new_a[0]
  250. return new_a
  251. if __name__ == '__main__':
  252. cons = r'''
  253. <div class="stem-wraper" data-v-494b33d9=""><span class="stem" data-v-494b33d9="">下述有关功和能量说法正确的是( )</span></div>
  254. <ul class="stem-options" data-v-494b33d9="">
  255. <li data-v-494b33d9=""><span class="analysis-prefix" data-v-494b33d9="">A.</span><span data-v-494b33d9="">物体做功越多,<span style="text-decoration: underline wavy;">物体的能量就越大</span></span></li>
  256. <li data-v-494b33d9=""><span class="analysis-prefix" data-v-494b33d9="">B.</span><span data-v-494b33d9="">摩擦力可能对物体做正功,也可能做负功,<span style="text-decoration: underline;">也可以不做功</span></span></li>
  257. <li data-v-494b33d9=""><span class="analysis-prefix" data-v-494b33d9="">C.</span><span data-v-494b33d9="">能量耗散表明,能量守恒定律有些情况下并不成立</span></li>
  258. <li data-v-494b33d9=""><span class="analysis-prefix" data-v-494b33d9="">D.</span><span data-v-494b33d9="">弹簧拉伸时的弹性势能一定大于压缩时的弹性势能</span></li>
  259. </ul>
  260. <div class="topic-analysis" data-v-494b33d9="">
  261. <div class="topic-analysis-content" data-v-494b33d9=""><span class="analysis-prefix" data-v-494b33d9="">【答案】</span><span data-v-494b33d9="">B</span></div>
  262. <div class="topic-analysis-content" data-v-494b33d9=""><span class="analysis-prefix" data-v-494b33d9="">【解析】</span><span data-v-494b33d9="">功是能量转化的量度,物体做功越多,物体的能量转化就越多,而不是能量越大。故A错误;摩擦力方向可能与物体运动方向相同、也与物体运动方向相反,所以摩擦力可能对物体做正功,也可能做负功。物体也可能没有位移,摩擦力不做功,故B正确;能量耗散虽然不会使能的总量减少,但能量的可利用率越来越低,即能量的品质越来越低;根据能量守恒定律可知,虽然能量的可利用率越来越低,但能量总和保持不变,仍然遵守能量守恒定律,故C错误;弹簧的弹性势能与形变量有关,弹簧拉伸时与压缩时弹性势能可能相等,也可能拉伸时的弹性势能小于压缩时的弹性势能。故D错误。故选B。</span></div>
  263. </div>
  264. '''
  265. # cons = '''<p style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0;text-align:justify;text-justify:inter-ideograph;line-height:150%"><span style="line-height: 150%;font-size: 14px">Unbelievable</span><span style=";font-family:宋体;line-height:150%;font-size:14px"><span style="font-family:宋体">!</span></span><span style="line-height: 150%;font-size: 14px">Oh..., _____ you don&#39;t mind, I&#39;ll stop and take a deep breath.</span></p><p><br/></p>
  266. # <p>【答案】<p style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0;text-align:justify;text-justify:inter-ideograph;line-height:150%"><span style=";font-family:宋体;line-height:150%;font-size:14px">1</span><span style="line-height: 150%;font-size: 14px">.if</span><span style=";font-family:宋体;line-height:150%;font-size:14px"><span style="font-family:宋体">。考查</span></span><span style="line-height: 150%;font-size: 14px">if</span><span style=";font-family:宋体;line-height:150%;font-size:14px"><span style="font-family:宋体">引导的条件状语从句。根据句意可知。</span></span></p><p><br/></p></p>
  267. # <p>【解析】</p>'''
  268. # pprint(cons)
  269. # print(again_parse(cons))
  270. # print(again_parse(cons))
  271. # print(list(map(lambda x: str(x).replace(" ", " "), again_parse(cons))))
  272. # con1 = r'<p>解:A.研究跨栏动作时,刘翔的大小和形状不能忽略,不能看作质点,故A错误;<br/>B.选取不同的参考系,物体的运动状态是不相同的,故B错误;<br/>C.出租车收费是按路程收费的,故C错误;<br/>D.第<img src="http://192.168.1.145:10811/static/physical_formulas_imgs/16184553930794225.png" data-latex="${4 \rm{s} }$" width="13",height="11" />是指<img src="http://192.168.1.145:10811/static/physical_formulas_imgs/16184553931930702.png" data-latex="${1 \rm{s} }$" width="12",height="11" />的时间,是指从<img src="http://192.168.1.145:10811/static/physical_formulas_imgs/16184553930220437.png" data-latex="${3 \rm{s} }$" width="13",height="11" />末到<img src="http://192.168.1.145:10811/static/physical_formulas_imgs/16184553930794225.png" data-latex="${4 \rm{s} }$" width="13",height="11" />末这一段时间,故D正确;<br/>故选:D.</p>'
  273. cons1 = """
  274. <div class="stem-wraper" data-v-00256703="">
  275. <p>阅读下面这首宋诗,完成下列小题。(本题共2小题,9分)</p>
  276. <p style="text-align: center;"><span style="font-family: 楷体;">除夜野宿常州城外二首(其二)</span></p>
  277. <p style="text-align: center;"><span style="font-family: 楷体;">苏轼</span></p>
  278. <p style="text-align: center;"><span style="font-family: 楷体;">南来三见岁云徂<sup>①</sup>,直恐终身走道途。</span></p>
  279. <p style="text-align: center;"><span style="font-family: 楷体;">老去怕看新历日,退归拟学旧桃符。</span></p>
  280. <p style="text-align: center;"><span style="font-family: 楷体;">烟花已作青春<sup>②</sup>意,霜雪偏寻病客须。</span></p>
  281. <p style="text-align: center;"><span style="font-family: 楷体;">但把穷愁博长健,不辞最后饮屠苏<sup>③</sup>。</span></p>
  282. <p>【注】①苏轼于熙宁四年(1071)冬到杭州任通判,至作此诗,已度过三个除夕。岁云徂,谓年岁辞去。徂,往。②青春:春季。③古俗,正月初一家人先幼后长依次饮屠苏酒。《时镜新书》晋董勋云:“正旦饮酒先从小者,何也?勋曰:‘俗以小者得岁,故先酒贺之,老者失时,故后饮酒。’”</p>
  283. </div>
  284. <div class="slave" data-v-00256703="">
  285. <div class="slave-item" data-v-00256703="">
  286. <div class="content" data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">14</span><span data-v-00256703="">.</span><span data-v-00256703="">下列对这首诗的理解和赏析,不正确的一项是( )</span></div>
  287. <ul class="stem-options" data-v-00256703="">
  288. <li data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">A.</span><span data-v-00256703="">诗人离开朝廷南来已三年,恐怕自己终身奔走于宦途而不能践偿其政治抱负。</span></li>
  289. <li data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">B.</span><span data-v-00256703="">因桃符一年一换,诗人自比“桃符”,寄托了自己在新的一年仕途晋升的愿望。</span></li>
  290. <li data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">C.</span><span data-v-00256703="">颈联对仗工整,诗人以自然界万物复苏的繁丽景象来反衬出自己的衰病老迈。</span></li>
  291. <li data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">D.</span><span data-v-00256703="">本诗抒发了除夜感慨,“桃符”“烟花”“屠苏”等意象照应节令,节日氛围浓厚。</span></li>
  292. </ul>
  293. </div>
  294. <div class="slave-item" data-v-00256703="">
  295. <div class="content" data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">15</span><span data-v-00256703="">.</span><span data-v-00256703="">末句“不辞最后饮屠苏”意蕴丰富,请简要分析。</span></div>
  296. </div>
  297. </div>
  298. <div class="topic-analysis" data-v-00256703="">
  299. <div class="topic-analysis-content" data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">【答案】</span><span data-v-00256703=""><br><span class="analysis-prefix">14.</span>&nbsp;&nbsp;B<br><span class="analysis-prefix">15.</span>&nbsp;&nbsp;①饮屠苏酒的顺序是自少至老,诗人说“最后饮”,表明了他年事已高。②诗人“辞”掉的是富贵荣华,表明他不再以仕途不畅为意。③“不辞最后饮”又表明他不以岁月流逝为意,以豁达乐观待之,表现了诗人豪放旷达的情怀。<br></span></div>
  300. <div class="topic-analysis-content" data-v-00256703=""><span class="analysis-prefix" data-v-00256703="">【解析】</span><span data-v-00256703=""><br><span class="analysis-prefix">14.</span>&nbsp;&nbsp;本题考查学生鉴赏诗歌的形象、表达技巧和情感的能力。B.“寄托了自己在新的一年仕途晋升的愿望”曲解文意。由“退归”可知,本句暗指诗人要抛却不如意的仕途,含有退隐之意。故选B。<br><span class="analysis-prefix">15.</span>&nbsp;&nbsp;本题考查学生理解诗句意蕴的能力。诗人一扫前面的郁闷,表示要用“穷”和“愁”换取长久的健康,要屠苏酒来迎新年。正月初一饮屠苏酒是一种习俗,饮用的顺序是自少至老,诗人说“最后饮”,表明了他年事已高。“烟花”二句,以自然界万物复苏的繁丽景象,反衬自己的衰病老迈。“拟学旧桃符”暗指诗人要抛却不如意的仕途,诗人“辞”掉的是富贵荣华,表明他不再以仕途不畅为意。诗人说“不辞最后饮”,不怕轮到我最后一个把屠苏酒饮,表明他不以岁月流逝为意,以豁达乐观待之,以此观照开篇,更见苏轼豪放旷达的情怀。<br></span></div>
  301. </div>
  302. """
  303. conss = css_label_wash(cons1)
  304. print(conss)