Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

95 rindas
4.4 KiB

  1. import logging
  2. from re import findall
  3. import requests
  4. from services.normalization import normalize_headline
  5. from structs.headline import Headline
  6. logger = logging.getLogger(__name__)
  7. DEFAULT_TIMEOUT = 10
  8. MIN_HEADLINE_WORDS = 3
  9. def is_headline(text, stopwords=None):
  10. if not text or not isinstance(text, str):
  11. return False
  12. cleaned_text = text.strip()
  13. if not cleaned_text:
  14. return False
  15. words = cleaned_text.split()
  16. if len(words) < MIN_HEADLINE_WORDS:
  17. logger.debug("Text rejected as headline (fewer than %d words): %r", MIN_HEADLINE_WORDS, cleaned_text)
  18. return False
  19. if not any(c.isalnum() for c in cleaned_text):
  20. logger.debug("Text rejected as headline (no alphanumeric characters): %r", cleaned_text)
  21. return False
  22. if stopwords is not None:
  23. normalized = normalize_headline(cleaned_text, stopwords)
  24. if not normalized:
  25. logger.debug("Text rejected as headline (no meaningful tokens after stopword removal): %r", cleaned_text)
  26. return False
  27. return True
  28. def prepare_headlines(sources, stopwords, timeout=DEFAULT_TIMEOUT):
  29. logger.info("Starting preparation of headlines for %d sources", len(sources) if sources else 0)
  30. headlines = []
  31. if not sources:
  32. logger.warning("No sources provided to prepare_headlines.")
  33. return headlines
  34. for idx, source in enumerate(sources, start=1):
  35. if not source or not source.strip():
  36. logger.warning("Skipping empty source at index %d", idx)
  37. continue
  38. source_url = source.strip()
  39. logger.info("Fetching source [%d/%d]: '%s'", idx, len(sources), source_url)
  40. try:
  41. response = requests.get(source_url, allow_redirects=True, timeout=timeout, headers={'User-Agent': 'Anya news bot'})
  42. logger.debug("Received HTTP response %d for '%s' (content length: %d bytes)",
  43. response.status_code, source_url, len(response.content))
  44. if response.status_code != 200:
  45. logger.warning("Source '%s' returned non-200 status code: %d", source_url, response.status_code)
  46. source_content = response.text
  47. except requests.exceptions.Timeout as e:
  48. logger.error("Request timed out for source '%s': %s", source_url, e, exc_info=True)
  49. continue
  50. except requests.exceptions.RequestException as e:
  51. logger.error("HTTP request failed for source '%s': %s", source_url, e, exc_info=True)
  52. continue
  53. except Exception as e:
  54. logger.error("Unexpected error fetching source '%s': %s", source_url, e, exc_info=True)
  55. continue
  56. logger.debug("Parsing HTML content from '%s' for headline candidates", source_url)
  57. try:
  58. link_texts = findall(r'<(?:a|span)\b[^>]*>\s*([^<]*?)\s*</(?:a|span)>', source_content)
  59. logger.info("Found %d candidate tags in source '%s'", len(link_texts), source_url)
  60. except Exception as e:
  61. logger.error("Regex extraction failed on content from '%s': %s", source_url, e, exc_info=True)
  62. continue
  63. source_headlines_count = 0
  64. for tag_idx, link_text in enumerate(link_texts, start=1):
  65. cleaned_text = link_text.strip()
  66. if not cleaned_text:
  67. logger.debug("Skipping empty tag text at position %d from '%s'", tag_idx, source_url)
  68. continue
  69. if not is_headline(cleaned_text, stopwords):
  70. logger.debug("Skipping non-headline tag text at position %d from '%s': %r", tag_idx, source_url, cleaned_text)
  71. continue
  72. logger.debug("Processing tag [%d/%d] from '%s': %r", tag_idx, len(link_texts), source_url, cleaned_text)
  73. try:
  74. normalized_headline = normalize_headline(cleaned_text, stopwords)
  75. headline = Headline(cleaned_text, normalized_headline)
  76. headlines.append(headline)
  77. source_headlines_count += 1
  78. except Exception as e:
  79. logger.error("Failed to normalize/create headline for text %r from '%s': %s", cleaned_text, source_url, e, exc_info=True)
  80. logger.info("Successfully extracted %d headlines from source '%s'", source_headlines_count, source_url)
  81. logger.info("Finished preparing headlines. Total headlines collected across all sources: %d", len(headlines))
  82. return headlines