import logging import re import lxml from bs4 import BeautifulSoup, Tag from lxml import etree from lxml.html.clean import Cleaner class NewsProcessor: __clean_regex_list = [] def __init__(self): self.__clean_regex_list = self.__build_clean_regex_list() logging.debug('NewsProcessor Sınıfı oluşturuldu') @staticmethod def __build_clean_regex_list(): """ Temizleme işlemi için kullanılacak regex desenlerini içeren bir liste oluşturur. Returns: list: Temizleme işlemi için kullanılacak regex desenlerini içeren bir liste. """ return [re.compile('.*footer.*', re.I), re.compile('.*copyright.*', re.I), re.compile('.*subscribe.*', re.I), re.compile('.*privacy.*', re.I), re.compile( '.*related.*|.*relative.*|.*ilgili.*|.*iliskili.*|.*news-more.*|.*deep-link.*|.*flashNews.*|.*mansetOfDays.*|.*news-continue.*|.*infinite-more.*|.*new_loader.*', re.I), re.compile('.*menu.*', re.I), re.compile('.*form.*', re.I), re.compile('.*keywords.*|.*topics.*|.*tags.*', re.I), re.compile('.*cookie.*', re.I), re.compile('.*popup.*', re.I), # re.compile('.*modal.*', re.I), re.compile('.*donotprint.*', re.I), re.compile('.*google-news.*', re.I), re.compile('.*social.*', re.I), re.compile('.*paylas.*|.*share.*', re.I), re.compile('.*listen.*', re.I), re.compile('.*video.*', re.I), re.compile('.*image.*', re.I), re.compile('.*sponsor.*', re.I), re.compile('.*widget.*|.*gotop.*|.*offline.*|.*comment.*', re.I), re.compile('.*promo.*', re.I), re.compile('.*sidebar.*|.*side-list.*', re.I), re.compile('.*breadcrumb.*|.*global-title.*|.*news-category.*|.*categoryarea.*|.*slogan.*|category-tag', re.I), re.compile('.*adv-.*|.*advertorial.*|.*inline-adv.*', re.I), re.compile('.*below.*', re.I), re.compile('.*more-news.*|.*more-post.*|.*area-header.*', re.I), re.compile('.*next-news.*', re.I), re.compile('.*sticky.*', re.I), re.compile('.*okunan.*', re.I), re.compile( '.*card-spot.*|.*haberkaynagi.*|.*author-title.*|.*news-profile.*|.*detay-foto-editor.*|.*editorSade.*|.*news-source.*|.*pagination-source.*|.*category-detail-mini-title.*', re.I), re.compile('.*comments.*', re.I), re.compile('.*modal-dialog.*', re.I), ] @staticmethod def encode(html): """ Belirtilen HTML içeriğini kodlar ve özel işaretlerle değiştirir. Args: html (str): Kodlanacak HTML içeriği. Returns: str: Kodlanmış HTML içeriği. """ html = html.replace("\0", "") # Delete NULL bytes. html = html.replace("
", "--BRRB--") html = html.replace("
", "--BRRB--") html = html.replace("
", "--BRRB--") html = html.replace("
", "--BRRB--") html = html.replace("
", "--BRRB--") html = html.replace("
", "--BRRB--") html = html.replace("

", "--PSSP--") html = html.replace("

", "--PSSP--") html = html.replace("

", "--PEEP--") html = html.replace("

", "--PEEP--") return html @staticmethod def decode(text, raw=True): """ Belirtilen metni çözümler ve özel işaretleri gerçek HTML etiketlerine dönüştürür. Args: text (str): Çözümlenecek metin. raw (bool): İşlenen metnin ham metin olup olmadığını belirtir. Varsayılan olarak True. Returns: str: Çözümlenmiş metin. """ if not raw: text = text.replace("--BRRB--", "
") text = text.replace("--PSSP--", "

") text = text.replace("--PEEP--", "

") else: text = text.replace("--BRRB--", "") text = text.replace("--PSSP--", "") text = text.replace("--PEEP--", "") return text def __clean_unwanted(self, html): """ Belirtilen HTML içeriğinde istenmeyen sınıflara ve id'lere sahip tag'lar temizler. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') unwanted_classes = tree.findAll(True, attrs={"class": self.__clean_regex_list}) unwanted_ids = tree.findAll(True, attrs={"id": self.__clean_regex_list}) for u in unwanted_classes: u.decompose() for u in unwanted_ids: u.decompose() html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_unwanted error: {str(e)}") raise e return html @staticmethod def __clean_with_lxml_cleaner(html): """ Belirtilen HTML içeriğini lxml cleaner kullanarak temizler. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: cleaner = Cleaner() cleaner.scripts = True # Betik etiketlerini temizler. cleaner.javascript = True # JavaScript kodlarını temizler. cleaner.links = True # Bağlantıları temizler. cleaner.style = True # Stil etiketlerini temizler. cleaner.forms = True # Form etiketlerini temizler. cleaner.comments = True # Kod içindeki yorumlar temizler. cleaner.embedded = True # Gömülü içerikleri temizler. cleaner.meta = False # Meta etiketlerini temizlemez. cleaner.kill_tags = ["img", "footer", "ul", "li", "nav", "blockquote"] # Belirtilen etiketleri tamamen kaldırır. cleaner.page_structure = False # Sayfa yapısını temizlemez. cleaner.safe_attrs = ["name", "content", "itemprop", "property", "class", "datetime"] # Güvenli nitelikleri korur. x = lxml.html.fromstring(html) etree_root = cleaner.clean_html(x) dom_tree = etree.ElementTree(etree_root) html = etree.tostring(dom_tree, pretty_print=True).decode("utf-8") html = re.sub(r"\r\n", " ", html) html = re.sub(r"\n", " ", html) except Exception as e: logging.error(f"An exception occurred in __clean_with_lxml_cleaner error: {str(e)}") raise e return html @staticmethod def __clean_meta_tags(html): """ Belirtilen HTML içeriğindeki istina tanınan meta etiketleri dışındaki meta etiketlerini temizler. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') all_meta = tree.find("head").findAll("meta", recursive=False) for meta in all_meta: allow_meta = False meta_attr_list = ["name", "itemprop", "property"] if any(key in meta.attrs for key in meta_attr_list): allowed_meta_list = ['description', 'datePublished', 'dateModified', 'dateCreated', 'dateUpdated', 'article:published_time', 'article:modified_time'] # istina tutulacak meta etiketlerinin özellikleri for attr in meta_attr_list: if attr in meta.attrs and meta.attrs[attr] in allowed_meta_list: allow_meta = True if not allow_meta: meta.decompose() html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_meta_tags error: {str(e)}") raise e return html @staticmethod def __clean_noscript_tags(html): """ Belirtilen HTML içeriğindeki noscript etiketlerini temizler. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') for u in tree.find_all("noscript"): u.decompose() html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_noscript_tags error: {str(e)}") raise e return html @staticmethod def __move_time_to_header_tags(html): """ Belirtilen HTML içeriğindeki time etiketlerini header etiketlerine taşır. Args: html (str): Taşınacak HTML içeriği. Returns: str: İşlenmiş HTML içeriği. Raises: Exception: Taşıma işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') body = tree.find("body") header = body.find("header") if not header: header = tree.new_tag("header") body.next.insert_before(header) for e in body.find_all("time"): for p in e.find_parents("p"): p.unwrap() for c in e.children: if type(c) is Tag: c.unwrap() header.append(e) html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __move_time_to_header_tags error: {str(e)}") raise e return html @staticmethod def __clean_link_tags(html): """ Belirtilen HTML içeriğindeki a (link) etiketlerini temizler. Eğer etiketleri bir

ve
ile birlikte bulunuyor ise
etiketinin içindeki content bırakılır sadece etiket olarak temizlenir. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') all_a = tree.findAll("a") for a in all_a: is_content_el = len(a.parent.findAll(['p', 'br'])) > 0 if not is_content_el: is_content_el = len(a.parent.parent.findAll(['p', 'br'])) > 0 if not is_content_el: a.decompose() else: a.unwrap() html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_link_tags error: {str(e)}") raise e return html @staticmethod def __clean_article_tags(html): """ Belirtilen HTML içeriğindeki article etiketini içini temizler ve içeriğini düzenler. article etikenin içinde header bilgisi var ise bunlar üstte header etiketi içine taşınır. article içinde

ve h1,h2,h3 etiketleri daha alt node'lar olarak bulunuyor ise bunlar article'ın alt çocukları olacak şekilde üste alınır. article etiketi içinde

ve h1,h2,h3 gibi h tagları dışındaki tag'ler içerikleri kalacak şekilde temizlenir. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') article = tree.find("article") if article: header = tree.find("header") inline_header = article.find("header") if inline_header: header.append(inline_header) inline_header.unwrap() for child in article.find_all(recursive=True): if child: if child.attrs and "class" in child.attrs and len(child.attrs["class"]) > 0: if re.match('.*title.*|.*spot.*|.*info.*|.*header.*|.*detail-header.*', child.attrs["class"][0], re.I): header.append(child) parent = article.parent while True: if not parent or parent.name == "body": break for el in parent.previous_elements: if type(el) is Tag: pp = el.find_all("p", recursive=False) if pp: for p in pp: article.append(p) parent = el.parent if not parent or parent.name == "body": break for poh in article.find_all(["p", re.compile(r"h[0-9]")]): article.append(poh) parent = article.parent while True: if not parent or parent.name == "body": break for el in parent.next_elements: if type(el) is Tag: if el.next == "article": break if el.name == "p": el = el.parent pp = el.find_all("p", recursive=False) if pp: for p in pp: article.append(p) parent = el.parent if not parent or parent.name == "body": break for child in article.find_all(recursive=False): if child: if type(child) is Tag: if not (child.name == "p" or re.match(r"h[0-9]", child.name)): child.decompose() html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_article_tags error: {str(e)}") raise e return html @staticmethod def __clean_content_tags(html): """ Belirtilen HTML içeriğindeki içerik etiketlerini temizlenir ve içeriği düzenler.

ve etiketletinin alt node'ları kaldırılı içerikleri

ve içine alınır. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') phll = tree.find_all(["p", re.compile(r"h[0-9]")]) if phll: for ph in phll: if ph.children: for phc in ph.children: if type(phc) is Tag: phc.unwrap() p = tree.find("body").find("p") if p: for c in p.parent.children: if type(c) is Tag: if c.name != "p" or re.match(r"h[0-9]", c.name): c.decompose() html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_content_tags error: {str(e)}") raise e return html @staticmethod def __clean_header_tags(html): """ Belirtilen HTML içeriğindeki başlık etiketlerini temizler ve düzenler. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Başlık etiketleri temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') body = tree.find("body") header = body.find("header") if header: pl = header.find_all("p") if pl: for p in pl: h2 = tree.new_tag("h2", **p.attrs) h2.string = p.string p.replace_with(h2) html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_header_tags error: {str(e)}") raise e return html @staticmethod def __encode_content_tags(html): """ Belirtilen HTML içeriğindeki içerik etiketlerini

ve kodlar ve özel işaretlerle değiştirir.. Args: html (str): Kodlanacak HTML içeriği. Returns: str: İçerik etiketleri kodlanmış HTML içeriği. Raises: Exception: Kodlama işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') while True: fp = tree.find("body").find("p") if fp: for c in fp.parent.children: if type(c) is Tag: if c.name == "p": c.string = f'--PSSP--{c.string}--PEEP--' c.unwrap() elif re.match(r"h[0-9]", c.name): i = re.sub(r"[^0-9.]", "", str(c.name), 1) c.string = f'--H{i}SH--{c.string}--H{i}EH--' c.unwrap() else: break html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_content_tags error: {str(e)}") raise e return html @staticmethod def __clean_empty_leaf_tags(html): """ Belirtilen HTML içeriğindeki boş yaprak etiketleri temizler. Args: html (str): Temizlenecek HTML içeriği. Returns: str: Boş yaprak etiketleri temizlenmiş HTML içeriği. Raises: Exception: Temizleme işlemi sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') while True: found = False for el in tree.find("body").find_all(): no_has_child = len(el.find_all()) == 0 if no_has_child and len(el.text.strip()) == 0: el.decompose() found = True if not found: break html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __clean_empty_leaf_tags error: {str(e)}") raise e return html def __move_head_tags_to_body(self, html): """ Belirtilen HTML içeriğindeki head altında olan meta etiketlerini body içine taşır. Args: html (str): İşlenecek HTML içeriği. Returns: str: meta etiketleri body içine taşınmış HTML içeriği. Raises: Exception: İşleme sırasında bir hata oluşursa fırlatılır. """ try: tree = BeautifulSoup(html, 'html.parser') body = tree.find("body") head = tree.find("head") meta = head.find_all("meta") if meta: for m in meta: value = m.attrs["content"] name = '' if "name" in m.attrs: name = m.attrs["name"] elif "property" in m.attrs: name = m.attrs["property"] elif "itemprop" in m.attrs: name = m.attrs["itemprop"] name = name.lower() name = re.sub(r"[^a-zA-Z]", "", name, ) name = f'meta{name}' if not body.find(name): tag = tree.new_tag(name) tag.string = value body.next.insert_before(tag) title = tree.find("title") body.next.insert_before(title) if head: head.decompose() html = tree.prettify() except Exception as e: logging.error(f"An exception occurred in __move_meta_tags_to_body error: {str(e)}") raise e return html def transform(self, html): html = self.__clean_unwanted(html) html = self.__move_time_to_header_tags(html) html = self.__clean_with_lxml_cleaner(html) html = self.__clean_meta_tags(html) html = self.__clean_noscript_tags(html) html = self.__clean_link_tags(html) html = self.__clean_article_tags(html) html = self.__clean_header_tags(html) html = self.__clean_content_tags(html) html = self.__encode_content_tags(html) html = self.__clean_empty_leaf_tags(html) html = self.__move_head_tags_to_body(html) return html