telegraph_post.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import re
  2. from bs4 import BeautifulSoup as bs
  3. from telegraph import Telegraph, utils
  4. from config import CHANNEL_PICTURE_URL, CHANNELS_NAME
  5. from tidylib import tidy_document
  6. from logger import logging
  7. def __get_image_width(img_el):
  8. """Проверяет ширину HTML элемента img и возвращает ее числовое значение в пикселях"""
  9. try:
  10. width = img_el['width']
  11. except KeyError:
  12. try:
  13. width = img_el['style'].split('width:')[1]
  14. except Exception:
  15. return 0
  16. return int(width.split('px')[0])
  17. def __add_html_links(html: str) -> str:
  18. """
  19. Если в коде есть ссылки просто текстом, без хтмл элемента а, то функция
  20. заменит их все на полноценный html тэг с нужным атрибутом href
  21. """
  22. links = []
  23. for p in html.strip().split('http'):
  24. if 'assets' not in p and 'a>' not in p and (p.startswith('://') \
  25. or p.startswith('s://')):
  26. p = p.split('\n')
  27. links.append('http' + p[0])
  28. for l in links:
  29. link = ' '.join(l.split()) # убираем символы другой кодировки, которые
  30. # не убираются по-другому
  31. a_elem = f'<a href="{link}">{link}</a>'.replace(" </p>", "").replace("</p>", "")
  32. html = html.replace(l, a_elem)
  33. return html
  34. def __get_main_image(soup) -> str:
  35. """Проверяет ширину всех изображений в новости и возвращает
  36. ссылку на первое подходящее, либо на заданное по умолчанию,
  37. если не нашлось подходящего"""
  38. images = [i['src']
  39. for i in soup.find_all('img') if __get_image_width(i) >= 200]
  40. if images:
  41. return images[0]
  42. else:
  43. return CHANNEL_PICTURE_URL
  44. def make_telegraph_post(title: str, url: str, html: str) -> dict:
  45. """Формирует телеграф пост из HTML и отправляет его. Возвращает резельтат отправки"""
  46. telegraph = Telegraph()
  47. telegraph.create_account(short_name='16108')
  48. soup = bs(html, 'html.parser')
  49. html_spec = str(soup)
  50. html_spec = __add_html_links(html_spec)
  51. document, errors = tidy_document(html_spec, options={'numeric-entities':1})
  52. soup = bs(document, 'html.parser')
  53. html_spec = str(soup.body)
  54. # Обработка исходного HTML. Удаление неподходящих для телеграф тегов и замена
  55. html_spec = re.sub('<\/*body>', '', html_spec)
  56. html_spec = re.sub('<\/h\d>', '</h1><br />', html_spec, flags=re.IGNORECASE)
  57. html_spec = re.sub('h\d[^>]*>', 'strong>', html_spec, flags=re.IGNORECASE)
  58. html_spec = re.sub('<\/*table[^>]*>', '', html_spec, flags=re.IGNORECASE)
  59. html_spec = re.sub('<\/*tbody>', '', html_spec, flags=re.IGNORECASE)
  60. html_spec = re.sub('div[^>]*>', 'p>', html_spec, flags=re.IGNORECASE)
  61. html_spec = re.sub('article[^>]*>', 'p>', html_spec, flags=re.IGNORECASE)
  62. html_spec = re.sub('<\/*span[^>]*>', '', html_spec, flags=re.IGNORECASE)
  63. html_spec = re.sub('<\/*td>', '', html_spec, flags=re.IGNORECASE)
  64. html_spec = re.sub('tr>', 'p>', html_spec, flags=re.IGNORECASE)
  65. html_spec = re.sub('<\/*font[^>]*>', '', html_spec, flags=re.IGNORECASE)
  66. # Вставка заглавного изображения в верх поста и удаление его копии из основного HTML
  67. main_image = f'<img src="{__get_main_image(soup)}" alt="{title}">'
  68. html_spec = re.sub(
  69. f'<\/*img[^>]*{__get_main_image(soup)}[^>]*\/*>', '', html_spec, flags=re.IGNORECASE)
  70. html_spec = re.sub(
  71. f'<\/*img[^>]*thumbs_up.png[^>]*\/*>', '🙏', html_spec, flags=re.IGNORECASE)
  72. html_spec = main_image + html_spec
  73. # Специфическая функция для удаления лишнего текст в некоторых постах в канале 151
  74. html_spec = html_spec.replace('- >', '➡️ ')
  75. html_spec = html_spec.replace('- &gt;', '➡️ ')
  76. try:
  77. html_spec = '\n'.join(html_spec.split(
  78. 'Мы в социальных сетях')[0].split('\n')[:-1])
  79. except Exception:
  80. pass
  81. try:
  82. html_spec = '\n'.join(html_spec.split(
  83. 'Copyright ©')[0].split('\n')[:-1])
  84. except Exception:
  85. pass
  86. try:
  87. nodes = utils.html_to_nodes(html_spec)
  88. except Exception as e:
  89. logging.exception('Exception when html convert to node')
  90. if str(e).endswith("tag is not closed"):
  91. html = main_image
  92. for l in soup.body.get_text().split('\n'):
  93. if l.strip():
  94. l = l.replace('- >', '➡️ ')
  95. l = __add_html_links(l)
  96. html += "<p>" + l + "</p>\n"
  97. else:
  98. raise e
  99. return telegraph.create_page(
  100. title,
  101. author_name=CHANNELS_NAME,
  102. author_url=url,
  103. html_content=html
  104. )
  105. return telegraph.create_page(
  106. title,
  107. author_name=CHANNELS_NAME,
  108. author_url=url,
  109. content=nodes
  110. )