Konu Başlıkları Gizle
Merhaba,
Bu yazımda, One Piece manga bölümlerini otomatik olarak indiren ufak bir Python projesi örneğini anlatmak istedim. Kimisi için öğretici bir içerik olabileceğini düşündüm.
İyi okumalar.
Siteye girdiğiniz gibi bölümler "One Piece Chapter xx" diye karşınıza çıkacak. Herhangi birine girip sayfanın en üstündeki Available Chapters'tan istediğiniz bölümü seçebiliyorsunuz. Örnek olarak şu bölümü seçebiliriz: One Piece Chapter 183.
İlk sayfanın bağlantısı şu şekilde:
Mesela 4. sayfanın bağlantısı da şu:
Göze çarpanlar:
[CODE lang="python" title="Sayfa bağlantısı elde etme fonksiyonu"]def get_page_url(chapter: int, page: int) -> str:
return f"https://cdn.readonepiece.com/file/mangap/2/1{str(chapter).rjust(4, '0')}000/{page}.jpeg"[/CODE]
Tabii burada üstünde durulması gereken önemli bir detay var:
Sayfa bağlantısına
Bu kısım oldukça basit. Yapmanın birçok yolu var fakat ben
[CODE lang="python" title="Sayfa verisini çekme fonksiyonu"]def get_page_GET_response(chapter: int, page: int):
response = requests.get(get_page_url(chapter, page), stream=True)
return response[/CODE]
Buradaki
Buradaki
[CODE lang="python" title="Sayfa verisinden Image nesnesi oluşturma fonksiyonu"]def convert_page_data_to_image(page_response: requests.Response):
return Image.open(page_response.raw)[/CODE]
Bunun için şu StackOverflow cevabından yardım aldım. İşte burada
Sonrasında bu
[CODE lang="python" title="Resmi bilgisayara kaydetme fonksiyonu"]def save_image(image: Image.Image, filepath: str):
with open(filepath, "wb") as f:
image.save(f)[/CODE]
Resim ikili veri olduğu için kaydetmek istediğimiz dosyayı
[CODE lang="python" title="Tüm bir bölümü indirme kodu"]ap = ArgumentParser()
ap.add_argument("-b", "--bolum", type=int, required=True)
args = ap.parse_args()
chapter, page = args.bolum, 1
while True:
res = get_page_GET_response(chapter, page)
if not res.ok:
print(f"{chapter}. bölümün tüm sayfaları indirildi ({page - 1})")
break
image = convert_page_data_to_image(res)
filedir = f"chapters/{chapter}"
os.makedirs(filedir, exist_ok=True)
filename = f"{page}.jpg"
save_image(image, os.path.join(filedir, filename))
print(f"{chapter}. bölüm {page}. sayfa indirildi")
page += 1[/CODE]
[CODE lang="python" title="Kodun son hali"]import os
from argparse import ArgumentParser
import requests
from PIL import Image
def get_page_url(chapter: int, page: int) -> str:
return f"https://cdn.readonepiece.com/file/mangap/2/1{str(chapter).rjust(4, '0')}000/{page}.jpeg"
def get_page_GET_response(chapter: int, page: int) -> requests.Response:
response = requests.get(get_page_url(chapter, page), stream=True)
return response
def convert_page_data_to_image(page_response: requests.Response) -> Image.Image:
return Image.open(page_response.raw)
def save_image(image: Image.Image, filepath: str) -> None:
with open(filepath, "wb") as f:
image.save(f)
ap = ArgumentParser()
ap.add_argument("-b", "--bolum", type=int, required=True)
args = ap.parse_args()
chapter, page = args.bolum, 1
while True:
res = get_page_GET_response(chapter, page)
if not res.ok:
print(f"{chapter}. bölümün tüm sayfaları indirildi ({page - 1})")
break
image = convert_page_data_to_image(res)
filedir = f"chapters/{chapter}"
os.makedirs(filedir, exist_ok=True)
filename = f"{page}.jpg"
save_image(image, os.path.join(filedir, filename))
print(f"{chapter}. bölüm {page}. sayfa indirildi")
page += 1
[/CODE]
Okuduğunuz için teşekkürler.
Bu yazımda, One Piece manga bölümlerini otomatik olarak indiren ufak bir Python projesi örneğini anlatmak istedim. Kimisi için öğretici bir içerik olabileceğini düşündüm.
İyi okumalar.
Manga sitesi
One Piece mangasını okuyabileceğimiz bir siteyi kullanacağız bu projede: Read One Piece Manga Online.Siteye girdiğiniz gibi bölümler "One Piece Chapter xx" diye karşınıza çıkacak. Herhangi birine girip sayfanın en üstündeki Available Chapters'tan istediğiniz bölümü seçebiliyorsunuz. Örnek olarak şu bölümü seçebiliriz: One Piece Chapter 183.
Bölüm sayfası bağlantı formatı
Yukarıda bağlantısını paylaştığım 183. bölümün ilk sayfasının bağlantısını inceleyelim. Bir sayfanın bağlantısına erişmek için sayfa resmine sağ tıklayıp ister yeni sekmede açabilir, ister "Resim adresini kopyala" seçeneğiyle bağlantıyı kopyalayabilirsiniz. Ben Chrome'da gösterdim, diğer tarayıcılarda da benzer seçenekler vardır tahminimce:İlk sayfanın bağlantısı şu şekilde:
https://cdn.readonepiece.com/file/mangap/2/10183000/1.jpeg.Mesela 4. sayfanın bağlantısı da şu:
https://cdn.readonepiece.com/file/mangap/2/10183000/4.jpeg.Göze çarpanlar:
https://cdn.readonepiece.com/file/mangap/2/10183000/kısmı iki bağlantı için de aynı. Sadece son kısımlar farklı:1.jpegve4.jpeg. Buradaki numaralar, sayfa numarasını temsil ediyor gibi./2/biraz anlamsız duruyor. Hiçbir bölüm/sayfa için değişmediğini, başka bölümleri ve sayfaları inceleyerek görebilirsiniz.10183000, bölüm numarasını temsil ediyor gibi. Gerçekten de öyle. Farklı bir bölüme giderek bunu doğrulayabilirsiniz.
Python Kodu
Sayfa bağlantı formatını kullanarak sayfa bağlantısı elde etme
Elimizde bir taslak format var ve bunun içine bölüm ve sayfa numaralarını yerleştirmek istiyoruz. Bunun için f-string kullanabiliriz:[CODE lang="python" title="Sayfa bağlantısı elde etme fonksiyonu"]def get_page_url(chapter: int, page: int) -> str:
return f"https://cdn.readonepiece.com/file/mangap/2/1{str(chapter).rjust(4, '0')}000/{page}.jpeg"[/CODE]
Tabii burada üstünde durulması gereken önemli bir detay var:
str(chapter).rjust(4, "0"). Öncesinde bilerek es geçtim ama bölüm numarası kısmında (10183000) bölüm numarasının hep 4 haneli olmasına dikkat edin. 183. bölümün numarası 0183 iken 1. bölümün numarası 0001 şeklinde alınıyor. İşte bunun için bize kolaylık sağlayan bir fonksiyon olan rjust fonksiyonunu kullanıyoruz. rjust'ın ilk parametresi istenilen karakter sayısıyken ikinci parametresiyse istenilen karakter sayısına ulaşana kadar sola eklenecek karakter, yani baştaki string'imiz sağa yatık hale getiriliyor. Detaylara buradan ulaşabilirsiniz. Biz bölüm numarasını 4 haneye kadar 0'larla uzatmak istediğimiz için gerektiği şekilde kullandık.Sayfa bağlantısına GET isteği atıp sayfa verisini indirme
Bu kısım oldukça basit. Yapmanın birçok yolu var fakat ben requests kütüphanesini kullanmayı uygun gördüm:[CODE lang="python" title="Sayfa verisini çekme fonksiyonu"]def get_page_GET_response(chapter: int, page: int):
response = requests.get(get_page_url(chapter, page), stream=True)
return response[/CODE]
Buradaki
response nesnesinin içeriğini incelemek için resmi dokümantasyonu okuyabilirsiniz veya favori IDE'nizin otomatik tamamlama özelliğini kullanabilirsiniz (varsa). Dikkat edin ki elimize direkt bir resim gelmeyecek ama resmin ikili (binary) verisi gelecek. Veriyi incelemek için response.content, response.text, response.raw gibi alt nesneleri kullanabilirsiniz.Buradaki
stream=True ayarı, bir sonraki aşama için gerekti. Ben de yazıyı yazarken bir yandan kodunu yazıyordum, internette araştırdığıma göre bu olmadan kod çalışmıyor.Sayfa verisini görsele dönüştürüp görseli bilgisayara kaydetme
Elde ettiğimiz ikili veriyi bilgisayarımıza kaydetmek içinpillow kütüphanesinin Image modülünü kullanabiliriz:[CODE lang="python" title="Sayfa verisinden Image nesnesi oluşturma fonksiyonu"]def convert_page_data_to_image(page_response: requests.Response):
return Image.open(page_response.raw)[/CODE]
Bunun için şu StackOverflow cevabından yardım aldım. İşte burada
stream=True ayarı kullanılmış. Bu olmadan kod hata veriyordu.Sonrasında bu
Image nesnesinin save metodunu kullanabiliriz:[CODE lang="python" title="Resmi bilgisayara kaydetme fonksiyonu"]def save_image(image: Image.Image, filepath: str):
with open(filepath, "wb") as f:
image.save(f)[/CODE]
Resim ikili veri olduğu için kaydetmek istediğimiz dosyayı
wb (write binary, "ikili yaz") modunda açmamız gerekiyor. Direkt w modunda açarsak zaten metot hata veriyor.Tüm bir bölümü indirme
Yalnızca bir sayfayı nasıl indireceğimizi gördük. Şimdiyse sıra bir bölümün tüm sayfalarını sırayla indirmeye geldi. Ben burada şöyle bir mantıkla hareket ettim: Sayfa numarasını bir bir artır, ne zaman ki bağlantıya gönderilen istek hata döndürürse o bölümün tüm sayfaları gezilmiş demektir.[CODE lang="python" title="Tüm bir bölümü indirme kodu"]ap = ArgumentParser()
ap.add_argument("-b", "--bolum", type=int, required=True)
args = ap.parse_args()
chapter, page = args.bolum, 1
while True:
res = get_page_GET_response(chapter, page)
if not res.ok:
print(f"{chapter}. bölümün tüm sayfaları indirildi ({page - 1})")
break
image = convert_page_data_to_image(res)
filedir = f"chapters/{chapter}"
os.makedirs(filedir, exist_ok=True)
filename = f"{page}.jpg"
save_image(image, os.path.join(filedir, filename))
print(f"{chapter}. bölüm {page}. sayfa indirildi")
page += 1[/CODE]
- Bölüm numarasını, programı çalıştırırken verebilmek için
argparsekütüphanesindenArgumentParser'ı kullandım. Bunun yerinesys.argv'yi kullanmak da tercih edilebilirdi ama bu yöntemi daha uygun buldum.- Örneğin
py main.py -b 183, 183. bölümü indirtir. (Sizdepykomutu çalışmayabilir,python,python3vb. çalışabilir.)
- Örneğin
- Sayfa numarasını (
page) 1'den başlatıpwhiledöngüsünde bir bir artırıyoruz. - Ne zaman ki isteğe döndürülen cevabın HTTP durum kodu 200 değilse (
if not res.ok), normal koşullar altında bir önceki iterasyonda son sayfaya gelmişiz demektir. - Dosyalama sistemi olarak şöyle düşündüm:
- Bölümler birer klasör olup
chaptersüst klasörü altında bulunacak. Bölüm klasörlerinin isimleri direkt bölüm numaraları olacak. - Sayfalar, bölüm klasörlerinin altında
{sayfa numarası}.jpgisminde birer JPG dosyası olacak. - Örneğin 183. bölümün 10. sayfasının konumu
chapters/183/10.jpgolacak.
- Bölümler birer klasör olup
- Bu dosyalama sistemine göre öncelikle bölüm klasörü yoksa onu oluşturmamız gerekecek:
chapter/{bölüm numarası}/. Bunun içinos.makedirs(filedir, exist_ok=True)satırını yazdım. Buradaexist_ok=Trueayarı, eğer klasör zaten varsa oluşturmaya kalkmamasını ve dolayısıyla hata vermemesini sağlıyor.
[CODE lang="python" title="Kodun son hali"]import os
from argparse import ArgumentParser
import requests
from PIL import Image
def get_page_url(chapter: int, page: int) -> str:
return f"https://cdn.readonepiece.com/file/mangap/2/1{str(chapter).rjust(4, '0')}000/{page}.jpeg"
def get_page_GET_response(chapter: int, page: int) -> requests.Response:
response = requests.get(get_page_url(chapter, page), stream=True)
return response
def convert_page_data_to_image(page_response: requests.Response) -> Image.Image:
return Image.open(page_response.raw)
def save_image(image: Image.Image, filepath: str) -> None:
with open(filepath, "wb") as f:
image.save(f)
ap = ArgumentParser()
ap.add_argument("-b", "--bolum", type=int, required=True)
args = ap.parse_args()
chapter, page = args.bolum, 1
while True:
res = get_page_GET_response(chapter, page)
if not res.ok:
print(f"{chapter}. bölümün tüm sayfaları indirildi ({page - 1})")
break
image = convert_page_data_to_image(res)
filedir = f"chapters/{chapter}"
os.makedirs(filedir, exist_ok=True)
filename = f"{page}.jpg"
save_image(image, os.path.join(filedir, filename))
print(f"{chapter}. bölüm {page}. sayfa indirildi")
page += 1
[/CODE]
Geliştirme Önerileri
- Programın, verilen bölüm numara aralığına göre çalışması sağlanabilir.
- Kodu temizleştirmek için
chaptervepagebilgilerini içinde barındıran birPagesınıfı yazılıp gerekli yardımcı metotlar implemente edilebilir. - Bir sayfa önceden indirilmişse tekrar indirilmeyebilir.
Okuduğunuz için teşekkürler.