text: import requests
  import sys
  from bs4 import BeautifulSoup
  from itertools import zip_longest
  from .logger import Logger
  
  logger = Logger("cpubenchmark_sraper")
  
  
  def grouper(iterable, n, fillvalue=None):  # https://stackoverflow.com/a/434411/12633579
      args = [iter(iterable)] * n
      return zip_longest(*args, fillvalue=fillvalue)
  
  
  class single_cpu:
      def __init__(
          self, id=None, name=None, mark=None, rank=None, value=None, price=None
      ):
          self._url = None
          self._id = id
          self.id = id
          self._cpu_name = name
          self._cpu_mark = mark
          self._cpu_rank = rank
          self._cpu_value = value
          self._cpu_price = price
  
      @property
      def id(self):
          return self._id
  
      @id.setter
      def id(self, value):
          if type(value) == str:
              value = value.replace("cpu", "")
          self._id = value
          self._url = "https://www.cpubenchmark.net/cpu.php?id=%s" % str(value)
  
      def __repr__(self):
          return str(self.__dict__())
  
      def __dict__(self):
          return {
              "id": self._id,
              "url": self._url,
              "name": self._cpu_name,
              "mark": self._cpu_mark,
              "rank": self._cpu_rank,
              "value": self._cpu_value,
              "price": self._cpu_price,
          }
  
  
  class mega_cpu:
      def __init__(
          self,
          id=None,
          cpu_name=None,
          cpu_price=None,
          cpu_mark=None,
          cpu_value=None,
          thread_mark=None,
          thread_value=None,
          tdp=None,
          power_perf=None,
          test_date=None,
          socket=None,
          category=None,
          clock_speed=None,
          turbo_speed=None,
          cores=None,
          rank=None,
          samples=None,
      ):
          self._url = None
          self._id = id
          self.id = id
          self._cpu_name = cpu_name
          self._cpu_price = cpu_price
          self._cpu_mark = cpu_mark
          self._cpu_value = cpu_value
          self._thread_mark = thread_mark
          self._thread_value = thread_value
          self._tdp = tdp
          self._power_perf = power_perf
          self._test_date = test_date
          self._socket = socket
          self._category = category
          self._clock_speed = clock_speed
          self._turbo_speed = turbo_speed
          self._cores = cores
          self._rank = rank
          self._samples = samples
  
      @property
      def id(self):
          return self._id
  
      @id.setter
      def id(self, value):
          if type(value) == str:
              value = value.replace("cpu", "")
          self._id = value
          self._url = "https://www.cpubenchmark.net/cpu.php?id=%s" % str(value)
  
      def __repr__(self):
          return str(self.__dict__())
  
      def __dict__(self):
          return {
              "id": self.id,
              "url": self._url,
              "cpu_name": self._cpu_name,
              "cpu_price": self._cpu_price,
              "cpu_mark": self._cpu_mark,
              "cpu_value": self._cpu_value,
              "thread_mark": self._thread_mark,
              "thread_value": self._thread_value,
              "tdp": self._tdp,
              "power_perf": self._power_perf,
              "test_date": self._test_date,
              "socket": self._socket,
              "category": self._category,
              "clock_speed": self._clock_speed,
              "turbo_speed": self._turbo_speed,
              "cores": self._cores,
              "rank": self._rank,
              "samples": self._samples,
          }
  
  
  class cpubenchmark_scraper_single:
      def __init__(self, url="https://www.cpubenchmark.net/cpu_list.php"):
          self._url = url
          logger.info("Set URL as %s" % url)
          self._source = None
          self._soup = None
          self._table = None
          self._headers = None
          self._raw_rows = None
          self._cpus = None
  
      def refresh_request(self, url=None):
          if url is None:
              url = self._url
          try:
              _source = requests.get(url).text
          except requests.exceptions.RequestException as e:
              logger.critical("Requests failed to reach %s, error: %s" % (url, e))
              raise e
          logger.info("Requests discovered the webpage successfully")
          return _source
  
      def refresh_soup(self, source=None, features="html.parser"):
          if source is None:
              source = self._source
          try:
              _soup = BeautifulSoup(source, features=features)
          except Exception as e:
              logger.critical(
                  "BS4 failed to create a BeautifulSoup instance, error: %s" % e
              )
              raise e
          logger.info("BS4 successfully created a BeautifulSoup instance")
          return _soup
  
      def refresh_table(self, soup=None):
          if soup is None:
              soup = self._soup
          _table = self._soup.find("table", {"id": "cputable"})
          if _table is None:
              logger.critical(
                  "BS4 was unable to find any table on this website by the id 'cputable'"
              )
              raise Exception(
                  "BS4 failed to find any table on this website by the id 'cputable'"
              )
          else:
              logger.info("BS4 successfully discovered a table by the id 'cputable'")
          return _table
  
      def refresh_headers(self, table=None):
          if table is None:
              table = self._table
          try:
              _header_row = table.find("thead")
              _header_cols = _header_row.find_all("th")
              _header_cols_txt = [col.text for col in _header_cols]
          except AttributeError as e:
              logger.critical(
                  "BS4 was unable to find any headers on this table, error: %s" % e
              )
              raise e
          logger.info(
              "BS4 successfully discovered headers on this table: %s" % _header_cols_txt
          )
          return _header_cols
  
      def refresh_raw_rows(self, table=None):
          if table is None:
              table = self._table
          try:
              _cpu_body = table.find("tbody")
              _cpu_rows = _cpu_body.find_all("tr")
              if _cpu_rows is None:
                  AttributeError(
                      "AttributeError: 'NoneType' object has no attribute 'find_all'"
                  )
          except AttributeError as e:
              logger.critical(
                  "BS4 was unable to find any rows on this table, error: %s" % e
              )
              raise e
          logger.info("BS4 successfully discovered rows on this table")
          return _cpu_rows
  
      def refresh_cpus(self, raw_rows=None, headers=None):
          if raw_rows is None:
              raw_rows = self._raw_rows
          if headers is None:
              headers = self._headers
          _cpus = []
          for idx, row in enumerate(raw_rows):
              _cols = row.find_all("td")
              if len(_cols) < 5:
                  logger.warning("Not enough columns for CPU: %i" % idx)
                  raise Exception("Not enough columns in table for extraction")
              else:
                  _id = row.get("id")
                  if _id is None:
                      logger.warning(
                          "%s: unable to find CPU id, the url returned will most likely be wrong."
                      )
                  _cpus.append(
                      single_cpu(
                          id=_id,
                          name=_cols[0].text,
                          mark=int(_cols[1].text.replace(',', '')),
                          rank=int(_cols[2].text),
                          value=_cols[3].text,
                          price=_cols[4].text,
                      )
                  )
  
          return _cpus
  
      def refresh_all(self):
          self._source = self.refresh_request()
          self._soup = self.refresh_soup()
          self._table = self.refresh_table()
          self._headers = self.refresh_headers()
          self._raw_rows = self.refresh_raw_rows()
          self._cpus = self.refresh_cpus()
  
  
  class cpubenchmark_scraper_mega(cpubenchmark_scraper_single):
      def __init__(self, url="https://www.cpubenchmark.net/CPU_mega_page.html"):
          super().__init__(url=url)
  
      def refresh_raw_rows(self, table=None):
          if table is None:
              table = self._table
          try:
              _cpu_body = table.find("tbody")
              _cpu_rows = _cpu_body.find_all("tr")
              if _cpu_rows is None:
                  AttributeError(
                      "AttributeError: 'NoneType' object has no attribute 'find_all'"
                  )
              _cpu_rows = grouper(_cpu_rows, 2)
          except AttributeError as e:
              logger.critical(
                  "BS4 was unable to find any rows on this table, error: %s" % e
              )
              raise e
          logger.info("BS4 successfully discovered rows on this table")
          return _cpu_rows
  
      def refresh_cpus(self, raw_rows=None, headers=None):
          if raw_rows is None:
              raw_rows = self._raw_rows
          if headers is None:
              headers = self._headers
          _cpus = []
          for idx, row in enumerate(raw_rows):
              _cols = row[0].find_all("td")
              _temp_cpu = {}
              if len(_cols) < 11:
                  logger.warning("Not enough columns for CPU: %i" % idx)
                  raise Exception("Not enough columns in table for extraction")
              else:
                  _id = row[0].get("id")
                  if _id is None:
                      logger.warning(
                          "%s: unable to find CPU id, the url returned will most likely be wrong."
                      )
                  _temp_cpu = dict(
                      id=_id,
                      cpu_name=_cols[0].text,
                      cpu_price=_cols[1].text,
                      cpu_mark=int(_cols[2].text.replace(',', '')),
                      cpu_value=_cols[3].text,
                      thread_mark=int(_cols[4].text.replace(',', '')) if _cols[4].text != 'NA' else _cols[4],
                      thread_value=_cols[5].text,
                      tdp=_cols[6].text,
                      power_perf=_cols[7].text,
                      test_date=_cols[8].text,
                      socket=_cols[9].text,
                      category=_cols[10].text,
                  )
              _cols2 = row[1].find_all("div")
              if len(_cols2) < 5:
                  logger.warning("Not enough MEGA columns for CPU: %i" % idx)
                  raise Exception("Not enough columns in table for extraction")
              else:
                  _temp_cpu.update(
                      {
                          "clock_speed": _cols2[0].text.replace("Clock Speed: ", ""),
                          "turbo_speed": _cols2[1].text.replace("Turbo Speed: ", ""),
                          "cores": _cols2[2].text.replace("No of Cores: ", ""),
                          "rank": _cols2[3].text.replace("Rank: ", ""),
                          "samples": _cols2[4].text.replace("Samples: ", ""),
                      }
                  )
              _cpus.append(mega_cpu(**_temp_cpu))
  
          return _cpus
  