tools.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import hashlib
  2. import json
  3. import re
  4. from collections import namedtuple
  5. import requests
  6. from setting import WECHAT_WARNING_URL,WECHAT_WARNING_PHONE,WARNING_INTERVAL,WECHAT_WARNING_ALL
  7. import bson
  8. from feapder.utils.log import log
  9. from feapder.db.mongodb import MongoDB
  10. SearchText = namedtuple('SearchText', ['total'])
  11. def substitute(html_str):
  12. """HTML 替换"""
  13. patterns = {
  14. '<!--.*?-->': '',
  15. '"': "'",
  16. '\n': '',
  17. '\xa0': "",
  18. '<span .*?>': '',
  19. '</span> ': '',
  20. '</span>': '',
  21. '<span>': '',
  22. '<p.*?>': '<br>',
  23. '</p>': '<br>',
  24. '<div>': '<br>',
  25. '<div .*?>': '<br>',
  26. '</div>': '<br>',
  27. '<img .*?>': '<br>',
  28. '<style.*?</style>': '',
  29. '<EpointForm>': '',
  30. '<html.*?</head>': '',
  31. '<input .*?>': '',
  32. '<!DOCTYPE.*?>': '',
  33. '</meta>': '',
  34. '<?xml:.*?>': '',
  35. '<label.*?>': '<br>',
  36. '</label>': '',
  37. 'style=".*?"': '',
  38. "style='.*?'": '',
  39. 'class=".*?"': '',
  40. "class='.*?'": '',
  41. "align='.*?'": '',
  42. 'align=".*?"': '',
  43. 'border=".*?"': '',
  44. "border='.*?'": '',
  45. 'cellpadding=".*?"': '',
  46. "cellpadding='.*?'": '',
  47. 'cellspacing=".*?"': '',
  48. "cellspacing='.*?'": '',
  49. 'center=".*?"': '',
  50. "center='.*?'": '',
  51. 'width=".*?"': '',
  52. "width='.*?'": '',
  53. "bordercolor='.*?'": '',
  54. 'bgcolor=".*?"': '',
  55. 'BORDERCOLOR=".*?"': '',
  56. '<a name=".*?">': '',
  57. '<o:p>': '',
  58. '</o:p>': '',
  59. '<A name=.*?>': '',
  60. '<a .*?>': '',
  61. '</a>': '',
  62. '<font .*?>': '',
  63. '</font>': '',
  64. '<body.*?>': '',
  65. '</body>': '',
  66. '<script.*?>': '',
  67. '</script>': '',
  68. '【关闭】': '',
  69. '【打印】': '',
  70. 'function .*?() ': '',
  71. 'var .*?;': '',
  72. 'if .*?\)': '',
  73. '{[^{}]+}': '',
  74. '{.*?}': '',
  75. }
  76. def substitutes(k, v, c):
  77. return re.sub(k, v, c)
  78. for k, v in patterns.items():
  79. html_str = re.sub(k, v, substitutes(k, v, html_str), re.S, re.M)
  80. return html_str
  81. def get_signature(content: str) -> str:
  82. """
  83. 十六进制数字字符串形式摘要值
  84. @param content: 字符串文本
  85. @return: 摘要值
  86. """
  87. sha1 = hashlib.sha1()
  88. sha1.update(content.encode("utf-8"))
  89. return sha1.hexdigest()
  90. def text_search(content: str) -> SearchText:
  91. """
  92. 中文检索
  93. :param content: 文本
  94. :return: 中文数量
  95. """
  96. if not content:
  97. return SearchText(0)
  98. results = re.findall('[\u4e00-\u9fa5]', content, re.S)
  99. # 列表长度即是中文的字数
  100. return SearchText(len(results))
  101. def int2long(param: int):
  102. """int 转换成 long """
  103. return bson.int64.Int64(param)
  104. def get_spiders(menus):
  105. db = MongoDB(db="editor")
  106. for menu in menus:
  107. spider_info = db.find('luaconfig',{"code":menu.code})
  108. if len(spider_info) >0:
  109. if spider_info[0].get("state") not in (11,):
  110. menus.remove(menu)
  111. def wechat_warning(
  112. message,
  113. message_prefix=None,
  114. rate_limit=None,
  115. url=None,
  116. user_phone=None,
  117. all_users: bool = None,
  118. ):
  119. """企业微信报警"""
  120. # 为了加载最新的配置
  121. rate_limit = rate_limit if rate_limit is not None else WARNING_INTERVAL
  122. url = url or WECHAT_WARNING_URL
  123. user_phone = user_phone or WECHAT_WARNING_PHONE
  124. all_users = all_users if all_users is not None else WECHAT_WARNING_ALL
  125. if isinstance(user_phone, str):
  126. user_phone = [user_phone] if user_phone else []
  127. if all_users is True or not user_phone:
  128. user_phone = ["@all"]
  129. if not all([url, message]):
  130. return
  131. data = {
  132. "msgtype": "text",
  133. "text": {"content": message, "mentioned_mobile_list": user_phone},
  134. }
  135. headers = {"Content-Type": "application/json"}
  136. try:
  137. response = requests.post(
  138. url, headers=headers, data=json.dumps(data).encode("utf8")
  139. )
  140. result = response.json()
  141. response.close()
  142. if result.get("errcode") == 0:
  143. return True
  144. else:
  145. raise Exception(result.get("errmsg"))
  146. except Exception as e:
  147. log.error("报警发送失败。 报警内容 {}, error: {}".format(message, e))
  148. return False
  149. class JyBasicException(Exception):
  150. def __init__(self, code: int, reason: str, **kwargs):
  151. self.code = code
  152. self.reason = reason
  153. self.err_details = kwargs
  154. for key, val in kwargs.items():
  155. setattr(self, key, val)
  156. class CustomCheckError(JyBasicException):
  157. def __init__(self, code: int = 10002, reason: str = '特征条件检查异常', **kwargs):
  158. self.code = code
  159. self.reason = reason
  160. self.err_details = kwargs
  161. for key, val in kwargs.items():
  162. setattr(self, key, val)
  163. class CheckPrePareRequest:
  164. def __init__(self):
  165. self.crawl_keywords = {
  166. '招标', '流标', '评标', '询价', '中标候选人', '抽签', '谈判', '中选', '意见征询',
  167. '更正公告', '废标', '补遗', '议价', '邀请', '资格预审', '竞标', '变更', '遴选',
  168. '磋商', '项目', '评审', '询比', '开标', '澄清', '比选', '中止', '采购', '竟价',
  169. '招投标', '拟建', '成交', '中标', '竞争性谈判', '工程', '验收公告', '更正',
  170. '单一来源', '变更公告', '合同', '违规', '评判', '监理', '竞价', '答疑',
  171. '终止', '系统'
  172. }
  173. @staticmethod
  174. def check_es_cache(title: str, publish_time: int, rows: dict):
  175. """
  176. :param title: 标题
  177. :param publish_time: 发布时间的时间戳(l_np_publishtime)
  178. :param rows: 采集内容
  179. """
  180. pass
  181. # retrieved_result = es_query(title, publish_time)
  182. # if retrieved_result != 0:
  183. # '''es查询数据结果'''
  184. # rows['count'] = retrieved_result
  185. # raise CustomCheckError(code=10105, reason='标题内容已存在es')
  186. def check_crawl_title(self, title: str):
  187. for keyword in self.crawl_keywords:
  188. valid_keyword = re.search(keyword, title)
  189. if valid_keyword is not None:
  190. break
  191. else:
  192. raise CustomCheckError(code=10106, reason='标题未检索到采集关键词', title=title)
  193. def __check(self, rows: dict):
  194. title, publish_time = rows['title'], rows['l_np_publishtime']
  195. self.check_crawl_title(title)
  196. self.check_es_cache(title, publish_time, rows)
  197. def __call__(self, rows: dict, *args, **kwargs):
  198. self.__check(rows)