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("--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 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 ve 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