Skip to content

Instantly share code, notes, and snippets.

@Xnuvers007
Last active March 6, 2025 09:37
Show Gist options
  • Save Xnuvers007/ac52ba42187d9758ec9a9f5f1174b40c to your computer and use it in GitHub Desktop.
Save Xnuvers007/ac52ba42187d9758ec9a9f5f1174b40c to your computer and use it in GitHub Desktop.
Ini adalah Scraping website drama korea untuk doramaindo.ai harap digunakan dengan bijak :)

Doramaindo Web Scraper

Created by Xnuvers007

Pendahuluan

Doramaindo Web Scraper adalah aplikasi yang dirancang untuk mengekstrak informasi tentang drama dari situs doramaindo.ai dan midori.doramaindo.ai. Dalam proyek ini, beberapa langkah keamanan telah diterapkan untuk melindungi data yang dikumpulkan, serta untuk memastikan bahwa kode ini aman digunakan. Berikut adalah penjelasan mengenai langkah-langkah keamanan yang diterapkan.

Keamanan dalam Aplikasi

1. Pencegahan Serangan Cross-Site Scripting (XSS)

Untuk menghindari serangan XSS, kode ini memanfaatkan fungsi html.escape() untuk membersihkan URL yang dimasukkan oleh pengguna sebelum diproses lebih lanjut. Ini mengurangi risiko memasukkan karakter berbahaya yang dapat disalahgunakan oleh penyerang untuk menyisipkan skrip berbahaya.

url = html.escape(url)
  1. Penggunaan User-Agent Acak Untuk menghindari pemblokiran oleh situs web target, aplikasi ini menggunakan daftar User-Agent yang diacak setiap kali permintaan dibuat. Ini memungkinkan scraper untuk mensimulasikan berbagai perangkat dan browser pengguna, yang mengurangi kemungkinan terdeteksi dan diblokir oleh situs.
headers = {
    'User-Agent': random.choice(listofuseragents),
    'X-Content-Type-Options': 'nosniff',
    'X-Frame-Options': 'DENY',
    'Referrer-Policy': 'no-referrer'
}
  1. Validasi dan Pembatasan URL Aplikasi ini memvalidasi URL yang dimasukkan oleh pengguna untuk memastikan bahwa URL yang diterima hanya berasal dari domain yang diizinkan. Dengan menggunakan regex, kode ini memastikan hanya URL yang valid dan sesuai dengan format yang dibolehkan yang akan diproses lebih lanjut.

Validasi URl:

if not url.startswith("https://"):
    return False

Pembatasan Domain

ALLOWED_DOMAINS = [
    "doramaindo.ai",
    "midori.doramaindo.ai",
    "doramaindo.id",
]

Dengan ini, aplikasi hanya akan mengizinkan URL yang berasal dari domain yang telah ditentukan, sehingga mengurangi risiko melakukan permintaan ke situs yang tidak diinginkan.

  1. Sanitasi Nama Input Fungsi validate_name() melakukan sanitasi terhadap input nama drama yang dimasukkan oleh pengguna, dengan menghapus karakter yang tidak diizinkan, seperti karakter khusus yang dapat digunakan untuk menyisipkan kode berbahaya atau merusak format data.
sanitized_name = re.sub(r"[^a-zA-Z0-9\s()]", "", name)
regex = re.compile(r'^[a-zA-Z0-9\s()]+$')

Selain itu, panjang input juga dibatasi untuk memastikan bahwa nama yang dimasukkan tidak lebih dari 100 karakter, yang dapat membantu mencegah serangan yang melibatkan input yang sangat panjang (misalnya buffer overflow).

  1. Pengelolaan Kesalahan dengan try-except Untuk mencegah aplikasi berhenti secara tak terduga atau memberikan informasi yang berlebihan kepada pengguna, aplikasi ini menangani berbagai jenis pengecualian dengan blok try-except. Hal ini memastikan bahwa kesalahan ditangani dengan cara yang aman, dan pesan kesalahan yang ditampilkan kepada pengguna tidak mengungkapkan informasi sensitif yang dapat dimanfaatkan oleh penyerang.
try:
    # Kode yang dapat menyebabkan kesalahan
except ValueError:
    print("URL tidak valid.")
except TypeError:
    print("Terjadi kesalahan tipe data.")
except Exception as e:
    print(f"Terjadi kesalahan: {e}")
# Dan except lainnya
  1. Pencegahan Serangan Man-in-the-Middle (MITM) Dengan menggunakan parameter verify=True dalam permintaan HTTP, aplikasi ini memaksa koneksi HTTPS yang aman dan memvalidasi sertifikat SSL untuk memastikan bahwa data yang dikirim dan diterima antara server dan klien aman dari serangan Man-in-the-Middle (MITM).
req = requests.get(url, headers=headers, timeout=10, verify=True)

Jika koneksi SSL gagal (misalnya, sertifikat SSL tidak valid), aplikasi akan mengeluarkan kesalahan dan tidak melanjutkan permintaan, sehingga mengurangi risiko data yang dikirimkan disadap oleh pihak ketiga.

  1. Penundaan Permintaan untuk Mencegah DDoS Untuk menghindari masalah terkait dengan pengiriman permintaan yang terlalu cepat ke server (yang dapat menyebabkan server terbebani atau dianggap sebagai serangan DDoS), aplikasi ini memperkenalkan penundaan acak antara setiap permintaan menggunakan time.sleep(random.uniform(1, 3)). Ini membantu memperlambat laju permintaan, membuatnya lebih menyerupai perilaku pengguna biasa.
time.sleep(random.uniform(1, 3))
  1. Pengelolaan Kesalahan Permintaan HTTP Aplikasi ini menangani berbagai jenis kesalahan permintaan HTTP (seperti Timeout, SSL Errors, HTTP Errors, dll.) dengan hati-hati. Ini memastikan bahwa jika ada masalah dengan permintaan HTTP, aplikasi tetap berfungsi dengan baik dan tidak menampilkan pesan yang tidak diinginkan kepada pengguna.
except requests.exceptions.RequestException as e:
    print(f"An error occurred: {e}")
except requests.exceptions.Timeout:
    print("The request timed out.")
# dan except lainnya
  1. Penggunaan Regex yang Aman Regex digunakan untuk memvalidasi dan menyaring input secara efisien. Dengan menggunakan re.fullmatch dan re.match, aplikasi memastikan bahwa hanya input yang sesuai dengan format yang diinginkan yang akan diterima.
if re.match(regex, sanitized_name) and re.fullmatch(regex, sanitized_name):
    return sanitized_name.strip()
  1. Validasi Input yang Diterima Pengguna Input pengguna (seperti nama drama atau URL) selalu divalidasi dan dibersihkan sebelum diproses lebih lanjut. Ini penting untuk mencegah potensi eksploitasi input yang tidak terkontrol, yang dapat menyebabkan berbagai kerentanannya.

Kesimpulan Kode ini telah dirancang dengan sejumlah langkah-langkah keamanan untuk memastikan bahwa hanya input yang valid dan aman yang diterima, serta untuk meminimalkan risiko dari berbagai jenis serangan seperti XSS, DDoS, Man-in-the-Middle, dan kesalahan terkait HTTP. Meskipun aplikasi ini tidak mengelola data sensitif secara langsung, praktik keamanan yang diterapkan sangat penting dalam melindungi aplikasi dan pengguna dari potensi kerentanannya.

README ini memberikan penjelasan yang jelas tentang bagaimana keamanan diterapkan dalam berbagai bagian kode, termasuk validasi input, sanitasi, penggunaan header yang aman, dan pengelolaan kesalahan.

Menambahkan

  • Penerapan mekanisme logging dan error handling yang aman.
  • Pemeriksaan robots.txt untuk memastikan scraping diizinkan.
  • Rate-limiting agar tidak DDoS websitenya
'''
Sumber = https://gist.github.com/Xnuvers007/ac52ba42187d9758ec9a9f5f1174b40c
pembuat : Xnuvers007
'''
import sys, subprocess, platform, os, socket, re
def clear_screen():
try:
if platform.system() == "Windows":
os.system("cls")
else:
os.system("clear")
except Exception as e:
print(f"Terjadi kesalahan saat membersihkan layar: {e}")
try:
import requests, random, re, time, urllib3, html, logging
from bs4 import BeautifulSoup
from urllib.parse import urlparse, quote
except ImportError as e:
missing_module = str(e).split()[-1]
print(f"Modul {missing_module} tidak ditemukan. Mencoba menginstal...")
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", missing_module])
globals()[missing_module] = __import__(missing_module)
print(f"Berhasil menginstal dan mengimpor {missing_module}.")
except subprocess.CalledProcessError as install_error:
print(f"Terjadi kesalahan saat menginstal {missing_module}: {install_error}")
except socket.gaierror as network_error:
print(f"Kesalahan jaringan: Tidak dapat menginstal {missing_module}. Periksa koneksi internet Anda. {network_error}")
except Exception as e:
print(f"Terjadi kesalahan tak terduga saat menginstal {missing_module}: {e}")
finally:
print("Pemeriksaan instalasi selesai.")
clear_screen()
logging.basicConfig(filename="app.log", level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
time.sleep(random.uniform(1, 3))
listofuseragents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:88.0) Gecko/20100101 Firefox/88.0",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
"Mozilla/5.0 (Linux; Android 10; Pixel 4 Build/QD1A.200205.003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.119 Mobile Safari/537.36",
"Mozilla/5.0 (Linux; Android 11; Samsung Galaxy S21 Build/RP1A.200720.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Mobile Safari/537.36",
"Mozilla/5.0 (Linux; Android 9; OnePlus 6T Build/PKQ1.180904.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36",
"Mozilla/5.0 (Linux; Android 10; Xiaomi Mi 9 Build/QKQ1.190828.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.117 Mobile Safari/537.36",
"Mozilla/5.0 (Linux; Android 8.1.0; Huawei P30 Pro Build/HUAWEIP30Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Mobile Safari/537.36"
]
ALLOWED_DOMAINS = [
"doramaindo.ai",
"midori.doramaindo.ai",
"doramaindo.id",
]
denganlist = "https://midori.doramaindo.ai/drama-list"
headers = {
'User-Agent': random.choice(listofuseragents),
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'no-referrer',
'Accept-Language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7',
'Connection': 'keep-alive'
}
panjang_karakter = 100
def is_scraping_allowed(url):
"""
Memeriksa apakah scraping diizinkan melalui robots.txt.
Jika tidak dapat mengakses robots.txt, default = diizinkan.
"""
parsed_url = urlparse(url)
robots_url = f"{parsed_url.scheme}://{parsed_url.netloc}/robots.txt"
try:
resp = requests.get(robots_url, headers=headers, timeout=5, verify=True)
resp.raise_for_status()
time.sleep(random.uniform(1, 3))
user_agentforrobots = re.compile(r"User-agent:\s+\*")
disallow = re.compile(r"Disallow:\s+")
for line in resp.text.splitlines():
if re.match(user_agentforrobots, line):
for line in resp.text.splitlines():
if re.match(disallow, line):
return False
return True
return True
# if "Disallow: /" in resp.text:
# return False
# return True
except requests.exceptions.RequestException as e:
logging.info(f"Gagal mengakses robots.txt: {e}")
return True
except Exception as e:
logging.info(f"Gagal mengakses robots.txt: {e}")
return True
class Doramaindo:
@staticmethod
def renameFileFromDoramaIndo():
directory = input("Masukkan path folder: ").strip()
directory = os.path.normpath(directory)
if not os.path.isdir(directory):
print("Directory not found.")
exit()
try:
files = os.listdir(directory)
except OSError as e:
print(f"Error accessing directory: {e}")
exit()
files = [file for file in files if re.match(r'\[doramaindo\.(ai|id)\].*\.mp4', file)]
try:
files = sorted(files, key=lambda x: int(re.search(r'E(\d+)', x).group(1)))
except AttributeError as e:
print(f"Error sorting files: {e}")
exit()
for file in files:
match = re.match(r'\[doramaindo\.(ai|id)\](.*)\.E(\d+)\.mp4', file)
if match:
series_name = match.group(2).replace('.', ' ').strip()
episode_number = match.group(3)
new_name = f"{series_name}S1-E{episode_number}-720p.mp4"
new_name = re.sub(r'[<>:"/\\|?*]', '', new_name)
old_file = os.path.join(directory, file)
new_file = os.path.join(directory, new_name)
try:
os.rename(old_file, new_file)
print(f"Renamed: {file} -> {new_name}")
except OSError as e:
print(f"Error renaming file {file}: {e}")
print("Renaming completed.")
@staticmethod
def validate_url(url):
regex = re.compile(r'^(https?://)?[a-zA-Z0-9.-]+(?:\.[a-zA-Z]{2,})+')
if not url.startswith("https://"):
return False
parsed_url = urlparse(url)
if not is_scraping_allowed(url):
return False
# return re.match(regex, parsed_url.scheme + '://' + parsed_url.netloc) is not None
# if not re.match(regex, parsed_url.scheme + '://' + parsed_url.netloc or re.fullmatch(regex, parsed_url.scheme + '://' + parsed_url.netloc)):
if not re.match(regex, parsed_url.scheme + '://' + parsed_url.netloc):
return False
domain = parsed_url.netloc
if domain in ALLOWED_DOMAINS:
return True
if re.match(r"^\d{1,3}(\.\d{1,3}){3}$", parsed_url.netloc):
return False
return False
@staticmethod
def validate_name(name):
sanitized_name = re.sub(r"[^a-zA-Z0-9\s()]", "", name)
regex = re.compile(r'^[a-zA-Z0-9\s()]+$')
if len(sanitized_name) > panjang_karakter:
print("Nama terlalu panjang. Max 100 karakter.")
return None
if re.match(regex, sanitized_name) and re.fullmatch(regex, sanitized_name):
return sanitized_name.strip()
else:
return None
@staticmethod
def getlinkdownload():
try:
url = input("Masukkan URL: ").strip()
url = html.escape(url)
url = quote(url, safe=":/?=&")
if not Doramaindo.validate_url(url):
print("URL tidak valid.")
return
if not is_scraping_allowed(url):
print("Scraping tidak diizinkan oleh robots.txt.")
return
except ValueError:
print("URL tidak valid.")
return
except TypeError:
print("Terjadi kesalahan tipe data.")
return
except Exception as e:
logging.error(f"Input URL error: {e}")
print(f"Terjadi kesalahan: {e}")
return
try:
req = requests.get(url, headers=headers, timeout=10, verify=True)
# req = requests.get(url, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False)
req.raise_for_status()
time.sleep(random.uniform(1, 3))
soup = BeautifulSoup(req.text, 'html.parser')
sinopre = re.compile(r"Sinopsis\s+")
statre = re.compile(r"Status\s+")
genrere = re.compile(r"Genre\s+")
seasre = re.compile(r"Seasons\s+")
typere = re.compile(r"Type\s+")
image_tag = soup.find('img', itemprop='image')
if image_tag:
image_url = image_tag['src']
print(f"Drama Image: {image_url}\n")
sinopsis_div = soup.find('p', string=sinopre)
if sinopsis_div:
# sinopsis = sinopsis_div.find_next('p').text.strip()
sinopsis = sinopsis_div.find_next('p').get_text(strip=True)
print(f"Sinopsis: {sinopsis}\n")
status = soup.find('b', string=statre)
if status:
# status_text = status.find_next('a').text.strip()
status_text = status.find_next('a').get_text(strip=True)
print(f"Status: {status_text}")
genre = soup.find('b', string=genrere)
if genre:
# genre_text = genre.find_next('a').text.strip()
genre_text = genre.find_next('a').get_text(strip=True)
print(f"Genre: {genre_text}")
seasons = soup.find('b', string=seasre)
if seasons:
# seasons_text = seasons.find_next('a').text.strip()
seasons_text = seasons.find_next('a').get_text(strip=True)
print(f"Seasons: {seasons_text}")
type_tag = soup.find('b', string=typere)
if type_tag:
# type_text = type_tag.find_next('a').text.strip()
type_text = type_tag.find_next('a').get_text(strip=True)
print(f"Type: {type_text}")
episode_list_div = soup.find('div', class_='episodelist')
if episode_list_div:
for li in episode_list_div.find_all('li'):
a_tag = li.find('a', href=True)
if a_tag:
download_link = a_tag['href']
print(f"Advanced Link: {download_link}\n\n")
else:
print("Tidak ada episode ditemukan.")
return
try:
# download_req = requests.get(download_link, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False)
download_req = requests.get(download_link, headers=headers, timeout=10, verify=True)
download_req.raise_for_status()
time.sleep(random.uniform(1, 3))
download_soup = BeautifulSoup(download_req.text, 'html.parser')
episode_list_div = download_soup.find('div', id='asw')
if episode_list_div:
download_links = set()
for li in episode_list_div.find_all('li'):
a_tag = li.find('a', href=True)
if a_tag:
download_link = a_tag['href']
download_links.add(download_link)
for link in sorted(download_links):
print(f"Download Link: {link}")
print("-" * 50)
except requests.exceptions.RequestException as e:
print(f"Terjadi kesalahan saat mengunduh: {e}")
except requests.exceptions.Timeout:
print("Permintaan melebihi batas waktu.")
except requests.exceptions.SSLError:
print("Verifikasi SSL gagal.")
except requests.exceptions.HTTPError as e:
print(f"Terjadi kesalahan HTTP: {e}")
except requests.exceptions.MissingSchema:
print("Format URL tidak valid.")
except Exception as e:
print(f"Terjadi kesalahan: {e}")
except requests.exceptions.RequestException as e:
print(f"Terjadi kesalahan: {e}")
except requests.exceptions.Timeout:
print("Permintaan melebihi batas waktu.")
except requests.exceptions.SSLError:
print("Verifikasi SSL gagal.")
except requests.exceptions.HTTPError as e:
print(f"Terjadi kesalahan HTTP: {e}")
except requests.exceptions.MissingSchema:
print("Format URL tidak valid.")
except Exception as e:
print(f"Terjadi kesalahan: {e}")
@staticmethod
def WithName():
try:
nama = input("Masukkan nama Drama: ").strip()
sanitized_name = Doramaindo.validate_name(nama)
if sanitized_name is None:
print("Nama tidak valid.")
return
except ValueError:
print("Nama tidak valid.")
return
except TypeError:
print("Terjadi kesalahan tipe data.")
return
except Exception as e:
logging.error(f"Input nama error: {e}")
print(f"Terjadi kesalahan: {e}")
return
url = f"https://midori.doramaindo.ai/?s={quote(sanitized_name)}&post_type=series"
try:
# req = requests.get(url, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False)
req = requests.get(url, headers=headers, timeout=10, verify=True)
req.raise_for_status()
time.sleep(random.uniform(1, 3))
soup = BeautifulSoup(req.text, 'html.parser')
results = soup.find_all('div', class_='resultnime')
if not results:
print("Tidak ada hasil ditemukan.")
return
for idx, result in enumerate(results, 1):
title_tag = result.find('h2').find('a')
title = title_tag.text.strip() if title_tag else "No title found"
link = title_tag['href'] if title_tag else "#"
image_tag = result.find('div', class_='nimestrike').find('img')
image_url = image_tag['src'] if image_tag else "No image"
status_tag = result.find('div', class_='genrestrike')
status = status_tag.find('a').text.strip() if status_tag else "No status"
genre_tags = result.find_all('a', rel='tag')
genres = [genre.text for genre in genre_tags]
print(f"Drama {idx}:")
print(f"Title: {title}")
print(f"Link: {link}")
print(f"Image: {image_url}")
print(f"Status: {status}")
print(f"Genres: {', '.join(genres) if genres else 'No genres'}")
print("-" * 50)
Doramaindo.getlinkdownload()
except requests.exceptions.RequestException as e:
print(f"Terjadi kesalahan saat mengunduh: {e}")
except requests.exceptions.Timeout:
print("Permintaan melebihi batas waktu.")
except requests.exceptions.SSLError:
print("Verifikasi SSL gagal.")
except requests.exceptions.HTTPError as e:
print(f"Terjadi kesalahan HTTP: {e}")
except requests.exceptions.MissingSchema:
print("Format URL tidak valid.")
except Exception as e:
logging.error(f"Error pada WithName: {e}")
print(f"Terjadi kesalahan: {e}")
@staticmethod
def WithList():
url = denganlist
try:
# req = requests.get(url, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False)
req = requests.get(url, headers=headers, timeout=10, verify=True)
req.raise_for_status()
time.sleep(random.uniform(1, 3))
soup = BeautifulSoup(req.text, 'html.parser')
for group in soup.find_all('div', class_='letter-group'):
letter = group.find('div', class_='letter-cell')
if letter:
print(f"Group: {letter.get_text()}")
for title_row in group.find_all('div', class_='row-cells'):
title_link = title_row.find('a', class_='series')
if title_link:
title = title_link.get_text(strip=True)
link = title_link['href']
print(f"Title: {title} - Link: {link}")
print("-" * 50)
else:
print("No title found")
except requests.exceptions.RequestException as e:
print(f"Terjadi kesalahan saat mengunduh: {e}")
except requests.exceptions.Timeout:
print("Permintaan melebihi batas waktu.")
except requests.exceptions.SSLError:
print("Verifikasi SSL gagal.")
except requests.exceptions.HTTPError as e:
print(f"Terjadi kesalahan HTTP: {e}")
except requests.exceptions.MissingSchema:
print("Format URL tidak valid.")
except Exception as e:
logging.error(f"Error pada WithList: {e}")
print(f"Terjadi kesalahan: {e}")
Doramaindo.getlinkdownload()
dorama = Doramaindo()
print('''
Created by @Xnuvers007
Github: Xnuvers007
Instagram: @Indradwi.25
''')
try:
tanya = input("Apakah ingin mencari drama berdasarkan nama? (y/n)\nTekan R untuk merename file yang di tonton (dari Dorama): ").strip()
except ValueError:
print("Terjadi kesalahan tipe data.")
except TypeError:
print("Terjadi kesalahan tipe data.")
except Exception as e:
print(f"Terjadi kesalahan: {e}")
exit()
if (
tanya.lower() == "y" or
tanya.lower() == "yes" or
re.match(r"y[a]+", tanya.lower()) or
re.match(r"yes[a]+", tanya.lower()) or
"y" in tanya.lower() and len(tanya.split()) == 1 or
any(word.startswith("y") for word in tanya.split()) or
re.search(r"\by+\b", tanya.lower()) or
re.match(r"^y(?:es)?$", tanya.lower()) or
re.search(r"\byes\w*", tanya.lower()) or
re.match(r"y[a-z]*", tanya.lower())
):
dorama.WithName()
elif (
tanya.lower() == "n" or
tanya.lower() == "no" or
re.match(r"n[o]+", tanya.lower()) or
re.match(r"no[a]+", tanya.lower()) or
"n" in tanya.lower() and len(tanya.split()) == 1 or
any(word.startswith("n") for word in tanya.split()) or
re.search(r"\bn+\b", tanya.lower()) or
re.match(r"^n(?:o)?$", tanya.lower()) or
re.search(r"\bno\w*", tanya.lower()) or
re.match(r"n[a-z]*", tanya.lower())
):
dorama.WithList()
elif (
tanya.lower() == "r" or
tanya.lower() == "rename" or
re.match(r"r[e]+", tanya.lower()) or
re.match(r"rename[a]+", tanya.lower()) or
"r" in tanya.lower() and len(tanya.split()) == 1 or
any(word.startswith("r") for word in tanya.split()) or
re.search(r"\br+\b", tanya.lower()) or
re.match(r"^r(?:ename)?$", tanya.lower()) or
re.search(r"\brename\w*", tanya.lower()) or
re.match(r"r[a-z]*", tanya.lower())
):
dorama.renameFileFromDoramaIndo()
else:
print("Pilihan tidak valid.")
exit()
requests
beautifulsoup4
urllib3
urllib
import unittest
from unittest.mock import patch, MagicMock
import requests
from urllib.parse import quote
from main import Doramaindo, is_scraping_allowed, headers, ALLOWED_DOMAINS
class TestDoramaindo(unittest.TestCase):
@patch('requests.get')
def test_is_scraping_allowed_accessible(self, mock_get):
# Mock a valid robots.txt response with 'User-agent: *' and no 'Disallow'
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = "User-agent: *\nDisallow: /private/\n"
mock_get.return_value = mock_response
# Test that scraping is allowed
self.assertTrue(is_scraping_allowed("https://doramaindo.ai/some-drama"))
@patch('requests.get')
def test_is_scraping_allowed_inaccessible(self, mock_get):
# Mock an exception when trying to access robots.txt
mock_get.side_effect = requests.exceptions.RequestException()
# Test that scraping is allowed if there's an error
self.assertTrue(is_scraping_allowed("https://doramaindo.ai/some-drama"))
def test_validate_url_valid(self):
valid_urls = [
"https://doramaindo.ai/some-drama",
"https://midori.doramaindo.ai/another-drama",
"https://doramaindo.id/yet-another-drama",
]
for url in valid_urls:
with patch('doramaindo.is_scraping_allowed', return_value=True):
self.assertTrue(Doramaindo.validate_url(url))
def test_validate_url_invalid(self):
invalid_urls = [
"http://doramaindo.ai/some-drama", # Not HTTPS
"ftp://midori.doramaindo.ai/another-drama", # Wrong scheme
"https://notallowed.com/drama", # Not in ALLOWED_DOMAINS
"https://123.123.123.123/drama", # IP Address
"doramaindo.ai/no-scheme", # Missing scheme
]
for url in invalid_urls:
self.assertFalse(Doramaindo.validate_url(url))
def test_validate_name_valid(self):
valid_names = [
"Valid Name", "Nama_valid_123",
"Nama(3)"
]
for name in valid_names:
self.assertEqual(Doramaindo.validate_name(name), name)
def test_validate_name_invalid(self):
invalid_names = [
"Invalid@Name!", # Invalid characters
"a" * 101, # Exceeds maximum character length
]
for name in invalid_names:
self.assertIsNone(Doramaindo.validate_name(name))
@patch('builtins.input', side_effect=["https://doramaindo.ai/some-drama"])
@patch('requests.get')
def test_getlinkdownload_valid(self, mock_get, mock_input):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = '''
<html><body>
<div class="episodelist">
<li><a href="http://download-link1"></a></li>
<li><a href="http://download-link2"></a></li>
</div>
</body></html>
'''
mock_get.return_value = mock_response
with patch('builtins.print') as mock_print:
Doramaindo.getlinkdownload()
mock_print.assert_any_call("Advanced Link: http://download-link1\n\n")
mock_print.assert_any_call("Advanced Link: http://download-link2\n\n")
@patch('builtins.input', side_effect=["http://invalid-domain.com"])
@patch('builtins.print')
def test_getlinkdownload_invalid_url(self, mock_print, mock_input):
Doramaindo.getlinkdownload()
mock_print.assert_called_with("URL tidak valid.")
@patch('builtins.input', side_effect=["https://doramaindo.ai/some-drama"])
@patch('requests.get')
def test_getlinkdownload_scraping_not_allowed(self, mock_get, mock_input):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = '''
User-agent: *
Disallow: /
'''
mock_get.return_value = mock_response
with patch('builtins.print') as mock_print:
Doramaindo.getlinkdownload()
mock_print.assert_called_with("Scraping tidak diizinkan oleh robots.txt.")
@patch('requests.get')
def test_WithList(self, mock_get):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = '''
<html><body>
<div class="letter-group">
<div class="letter-cell">A</div>
<div class="row-cells">
<a class="series" href="http://link-to-drama">Drama Title</a>
</div>
</div>
</body></html>
'''
mock_get.return_value = mock_response
with patch('builtins.print') as mock_print:
Doramaindo.WithList()
mock_print.assert_any_call("Group: A")
mock_print.assert_any_call("Title: Drama Title - Link: http://link-to-drama")
@patch('builtins.input', side_effect=["Invalid!name"])
@patch('builtins.print')
def test_WithName_invalid(self, mock_print, mock_input):
Doramaindo.WithName()
mock_print.assert_called_with("Nama tidak valid.")
@patch('builtins.input', side_effect=["Valid Name"])
@patch('requests.get')
def test_WithName_valid(self, mock_get, mock_input):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = '''
<html><body>
<div class="resultnime">
<h2><a href="http://link-to-drama">Drama Title</a></h2>
<div class="nimestrike"><img src="http://link-to-image"></div>
<div class="genrestrike"><a>Status</a></div>
<a rel="tag">Genre1</a><a rel="tag">Genre2</a>
</div>
</body></html>
'''
mock_get.return_value = mock_response
with patch('builtins.print') as mock_print:
with patch.object(Doramaindo, 'getlinkdownload', return_value=None):
Doramaindo.WithName()
mock_print.assert_any_call("Title: Drama Title")
if __name__ == "__main__":
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment