Python ve SFTP ile Çalışmak

Python ve SFTP ile Çalışmak

Ağa bağlı bir Python uygulaması için tipik bir kullanım örneği, bir komut dosyasının çalıştığı bilgisayara uzak bir dosyayı kopyalama veya yeni bir dosya oluşturup onu uzak bir sunucuya aktarma gereksinimini içerebilir. Çoğu zaman bu, SFTP (Güvenli Dosya Aktarım Protokolü) kullanılarak gerçekleştirilir. Python’da ağ programlama üzerine üç bölümlük bir dizinin bu ikinci bölümünde, Python ile çalışmanın yollarına bakacağız. SFTP, SSHve soketler.

Bu serinin ilk bölümünü eğiticimizi ziyaret ederek okuyabilirsiniz: Python ve Temel Ağ İşlemleri.

Kodlamaya başlamadan önce, SFTP’nin dosya aktarımlarının güvenli olmasını nasıl sağladığını tam olarak vurgulamak önemlidir. SFTP sürecinin yaptığı önemli bir şey, SSH Parmak İzi uzak SFTP Sunucusunun. SSH Parmak İzi, belirli bir SFTP sunucusuna özgüdür. Bir SFTP Sunucusunun Genel Anahtarının, parola olmadan kimlik doğrulama için kullanılan uzak sunucuda depolanan SSH Ortak Anahtarı ile aynı şey OLMADIĞINI unutmayın.

SSH Parmak İzini doğrulamak, SFTP Sunucusunun gerçekten de uzaktaki bir kullanıcının düşündüğü SFTP Sunucusu olduğundan emin olmak için çok önemlidir. Bir SFTP bağlantı girişimi, SSH Parmak İzinin değiştiğini bildirirse, bu birkaç anlama gelebilir:

  • SFTP Sunucusunun operatörü, onu yükseltti veya SFTP Sunucusunda bazı yapılandırma değişiklikleri yaptı.
  • Kötü niyetli bir kullanıcı, büyük olasılıkla oturum açma kimlik bilgilerini toplamak amacıyla uzak sunucunun kimliğine bürünmeye çalışıyor. Bu, “Ortadaki Adam” saldırısının bir örneğidir.

Her iki durumda da, bir SFTP işlemini otomatikleştiren herhangi bir kodun, SSH Parmak İzinin gerçekten geçerli olduğunu doğrulaması ve geçerli değilse, sunucunun uygun kimliği doğrulanana kadar herhangi bir bağlantı girişimini derhal durdurması kesinlikle zorunludur. SFTP, SSH paketindeki diğer tüm protokoller gibi, TCP Port üzerinde çalışır 22 varsayılan olarak. Bu makaledeki tüm örnekler bu sözleşmeyi izleyecektir. Başka bir bağlantı noktasına ihtiyaç duyulursa, TCP Bağlantı Noktası yerine o bağlantı noktası belirtilmelidir. 22.

SFTP ile İlk SSH Parmak İzini Alma

SFTP Sunucusunun SSH Parmak İzi’nin bir kopyasını almanın en kolay yolu, ona ücretsiz olarak kullanılabilen bir SFTP istemcisi veya hem Linux hem de Windows 10 Professional ile sağlanan OpenSSH araçları ile bağlanmaktır.

Linux

Bir terminalden, sadece sftp doğrudan komut verin. SSH Parmak İzi bilinmiyorsa veya değiştirilmişse, komut kullanıcıdan bu yönde bilgi isteyecektir. Aşağıdaki komut, bağlanmaya çalışmadan önce uzak sunucuyu SSH Parmak İzini alması için sorgulayacaktır. Bağlantı aşağıdaki gibi onaylandıktan sonra, kullanıcıdan belirtilen hesap için parola istenir:

$ sftp sftp://[email protected]

Parmak izi ve algoritma vurgulanmış olarak Linux’ta SSH Parmak İzi Alma

Parmak izinin eklenmesinin onaylanması, parmak izinin ~/.ssh/bilinen_hostlar Linux sistemindeki her bir kullanıcı hesabına özel olan dosya. Yukarıdaki şekilde, doğramak SSH Parmak İzi’nin yanı sıra kullanılan karma algoritma (sha-256) ve kullanılan şifreleme veya imza şeması (Ed25519) tümü kırmızı dikdörtgenlerle vurgulanmıştır.

Aşağıdaki Linux için Python kodu, aşağıdaki durumlarda başarısız olur: “evet” bağlantıya devam etmek isteyip istemediği sorusuna cevap verilmez. Ana bilgisayar kaydının eklenecek olması kritik öneme sahiptir. ~/.ssh/bilinen_hostlar SFTP’yi kullanan herhangi bir program kodunun, son kullanıcı tarafından önceden eklenmiş ana bilgisayarlarla sınırlı olduğundan emin olmak iyi bir güvenlik uygulamasıdır.

Bu bilgiler diğer uygulamalarda önemli olsa da, bu gösterimlerde öne çıkacak olan Python modülü, parmak izinin değeriyle daha çok ilgilenmektedir. Bunlar, içeriğin boşaltılmasıyla listelenebilir. ~/.ssh/bilinen_hostlar dosya:

Örnek hosts.txt dosyası

Örnek bir bilinen_hosts Dosyası

Yukarıdaki şekilde, karmaya karşılık gelen şifreleme veya imza algoritması vurgulanmıştır. Bu özel Linux dağıtımında ve OpenSSH uygulamasında, her satırın en solundaki değer olan ana bilgisayarın kendisinin hash edildiğini unutmayın.

pencereler

Windows 10, uzak bir SFTP Sunucusunun SSH Parmak İzini almak için kullanılabilecek resmi bir OpenSSH uygulaması sağlar. Devam etmeden önce, adresindeki talimatları izleyin. OpenSSH’yi kullanmaya başlayın ve en azından doğrulayın, “OpenSSH İstemcisi” Windows Eklentisi, Windows’a yüklenir. Kurulduktan sonra, aşağıdakine benzer bir dosya ~/.ssh/bilinen_hostlar dosya şu komut kullanılarak oluşturulabilir:

C…> ssh-keyscan my-sftp-host-or-ip > known_hosts.txt

bilinen_hosts.txt dosya aşağıdakine benzer görünecektir. Bu durumda IP Adresinin (veya ana bilgisayar adının) nasıl hash edilmediğine dikkat edin:

Python SFTP örnekleri

Bilinen_hosts dosyası doppelganger.

Linux’ta olduğu gibi ~/.ssh/bilinen_hostlar dosyasında imza algoritması SSH Parmak İzi ile listelenir, ancak OpenSSH’nin Windows uygulamasında ana bilgisayar girişleri karma değildir.

Artık söz konusu uzak sunucunun SSH Parmak İzleri bilindiğine göre, koda geçmenin zamanı geldi. Buradaki sunucu örnekleri, ssh-ed25519 imza algoritması ve SHA-256 karma. Diğer sunucular farklı şifreleme şemaları kullanabilir ve kodun buna göre ayarlanması gerekir.

Okumak: Python Öğrenmek için En İyi Çevrimiçi Kurslar

Paramiko Modülü

paramiko uygulayan bir Python modülüdür SSHv2. Bu Python eğitimindeki gösterimler, kesinlikle SFTP bağlantısına ve temel SFTP kullanımına odaklanacaktır. Aşağıdaki örnek, Python sürüm 3.10.4 ile Ubuntu 22.04 LTS üzerinde çalıştırılmıştır. Bu sistemde komut piton3 Python 3’ü çağırmak için açıkça kullanılmalıdır. pip bu sistemle ilişkili komut pip3. Diğer sistemler komuta takma ad verebilir piton Python 3’ü çağırmak için. Bu durumlarda komut şöyle olur: pip.

yüklemek için paramiko modül, şu komutu kullanın:

$ pip3 install paramiko

Linux

Aşağıdaki kod Linux’tan bağlanır ve ana bilgisayar anahtarını ~/.ssh/bilinen_hostlar dosya. Kod, bir bağlantıya izin vermeden önce SSH parmak izinin eşleştiğini doğrular:

# demo-sftp.py

import paramiko
import sys

def main(argv):
  hostkeys = paramiko.hostkeys.HostKeys (filename="/home/phil/.ssh/known_hosts")
  # The host fingerprint is stored using the ed25519 algorithm. This was revealed
  # when the host was initially connected to from the sftp program invoked earlier.
  hostFingerprint = hostkeys.lookup ("my-sftp-host-or-ip")['ssh-ed25519']    


  try:
    # Note that the parameters below represent a low-level Python Socket, and 
    # they must be represented as such.
    tp = paramiko.Transport("my-sftp-host-or-ip", 22)

    # Note that while you *can* connect without checking the hostkey, you really
    # shouldn't. Without checking the hostkey, a malicious actor can steal
    # your credentials by impersonating the server.
    tp.connect (username = "my-username", password="my-password", hostkey=hostFingerprint)
    try:
      sftpClient = paramiko.SFTPClient.from_transport(tp)
      fileCount = 0
      # Proof of concept - List First 10 Files
      for file in sftpClient.listdir():
        print (str(file))
        fileCount = 1 + fileCount
        if 10 == fileCount:
          break
      sftpClient.close()
    except Exception as err:
      print ("SFTP failed due to [" + str(err) + "]")

    tp.close()
  except paramiko.ssh_exception.AuthenticationException as err:
    print ("Can't connect due to authentication error [" + str(err) + "]")
  except Exception as err:
    print ("Can't connect due to other error [" + str(err) + "]")

if __name__ == "__main__":
  main(sys.argv[1:])

Aşağıda çıktının bir örneği verilmiştir:

Python ağ oluşturma örnekleri

Liste 3’ün Çıktısı

pencereler

yüklemek için aynı komut kullanılabilir. paramiko Windows’ta modül:

C…> pip3 install paramiko

Bir kere paramiko Windows’ta yüklüyse, not edin bilinen_hostlar.txt Yukarıda oluşturulan dosya. Aşağıdaki Windows uygulaması, bilinen_hosts.txt dosyasının Python koduyla aynı dizinde olduğunu varsayar.

Daha önce aynı kod Windows için uyarlanabilir:

# demo-sftp-windows.py

import paramiko
import sys

def main(argv):
  hostkeys = paramiko.hostkeys.HostKeys (filename="known_hosts.txt")
  # The host fingerprint is stored using the ed25519 algorithm. This was revealed
  # when the host was initially connected to from the sftp program invoked earlier.
  hostFingerprint = hostkeys.lookup ("my-sftp-host-or-ip")['ssh-ed25519']    


  try:
    # Note that the parameters below represent a low-level Python Socket, and 
    # they must be represented as such.
    tp = paramiko.Transport("my-sftp-host-or-ip", 22)

    # Note that while you *can* connect without checking the hostkey, you really
    # shouldn't. Without checking the hostkey, a malicious actor can steal
    # your credentials by impersonating the server.
    tp.connect (username = "my-username", password="my-password", hostkey=hostFingerprint)
    try:
      sftpClient = paramiko.SFTPClient.from_transport(tp)
      fileCount = 0
      # Proof of concept - List First 10 Files
      for file in sftpClient.listdir():
        print (str(file))
        fileCount = 1 + fileCount
        if 10 == fileCount:
          break
      sftpClient.close()
    except Exception as err:
      print ("SFTP failed due to [" + str(err) + "]")

    tp.close()
  except paramiko.ssh_exception.AuthenticationException as err:
    print ("Can't connect due to authentication error [" + str(err) + "]")
  except Exception as err:
    print ("Can't connect due to other error [" + str(err) + "]")

if __name__ == "__main__":
  main(sys.argv[1:])

Kapatmanın Önemi

Her iki kod listesinde de, hem sftpİstemci nesne ve tp nesneler, kullanıldıkları blokların sonuna yakın kapatılır. Bu çok önemlidir, çünkü bu nesneler kapatılmazsa bazı temel işlemler engellenebilir.

Okumak: Uzak Geliştiriciler için En İyi Araçlar

Güvenlik Sorunlarını Simüle Etme

Bu Python öğreticisi, SSH Parmak İzi’nin başlangıçta keşfedilenle eşleşmesini sağlamak için “büyük bir anlaşma” yaptığından, bir ana bilgisayar kimliğine bürünme saldırısının nasıl görünebileceğini “simüle etmek” ilginç olabilir. Bunu yapmak için, sadece açın ~/.ssh/bilinen_hostlar Dosyayı açın ve Liste 1’de bağlanılan sistemin ana bilgisayar anahtarında bir değişiklik yapın:

Bozuk bir SSH dosyası örneği

Linux’ta SSH Parmak İzini Kasten Bozmak

Bu Linux dağıtımı, girişler için ana bilgisayarlar yerine karmalar kullandığından, bu, Ed25519 algoritma, bunun değiştirilmesi gereken giriş olduğunu. Bu tür bir test gerekliyse, uygun ana bilgisayar kaydının değiştirilmesini sağlamak geliştiricinin sorumluluğundadır. Yukarıdaki örnek bir harfe odaklanırken, o satırdaki herhangi bir karakter “ssh-ed25519” değiştirilebilir.

Şimdi Kodu Liste 1’de çalıştırmak tekrar şu hatayı veriyor:

Uyumsuz SSH Parmak İzi

Uyumsuz bir SSH Parmak İzi nedeniyle Uygun Bir Hata

SSH Parmak İzi, kötü niyetli bir kullanıcının SSH sunucusunu taklit etmeye çalışması nedeniyle sunucu tarafında değiştirilmişse, SSH Parmak İzi eşleşmemesi durumunda güvenlik kimlik bilgileri iletilmediğinden bu çok hoş bir hata olacaktır.

Yukarıda oluşturulan sorunu çözmek için, SSH Parmak İzini keşfetmek için kullanılan orijinal komutu çağırmanız ve sağladığı talimatları izlemeniz yeterlidir:

Python SSH parmak izi

Kasıtlı olarak oluşturulan SSH Parmak İzi uyuşmazlığını düzeltme

Sorunlu girişler kaldırıldıktan sonra, SSH Parmak İzini yeniden eklemek için yukarıdaki ilk adımlara göre SFTP’yi orijinal ana bilgisayara yeniden denemeniz yeterlidir. ~/.ssh/bilinen_hostlar dosya.

Aynı güvenlik sorunu, Windows’ta benzer bir değişiklik yapılarak simüle edilebilir. bilinen_hostlar.txt yukarıda oluşturulan dosya:

Python'da bozuk SSH parmak izi

Windows’ta SSH Parmak İzini Kasten Bozmak

Aynı hata, kodun Windows sürümünde de görünür. Yukarıdaki sorunu çözmek için, sadece bilinen_hostlar.txt Yukarıdaki adımlara göre dosya.

Python ile Ortak SFTP Görevleri

bu paramiko modülü, basit olduğu kadar çok karmaşık SFTP görevleri için çok zengin ve sağlam bir araç takımı sağlar. Bu bölüm, daha temel ve yaygın SFTP görevlerinden bazılarını vurgulayacaktır.

SFTP ve Python ile Dosya Yükleme

bu koy yöntem, mevcut bir açık SFTP bağlantısı bağlamında SFTP Sunucusuna bir dosya yükler:

# demo-sftp-upload.py

import paramiko
import sys

def main(argv):
	hostkeys = paramiko.hostkeys.HostKeys (filename="/home/phil/.ssh/known_hosts")
	# The host fingerprint is stored using the ed25519 algorithm. This was revealed
	# when the host was initially connected to from the sftp program invoked earlier.
	hostFingerprint = hostkeys.lookup ("my-sftp-host-or-ip")['ssh-ed25519']


	try:
		# Note that the parameters below represent a low-level Python Socket, and 
		# they must be represented as such.
		tp = paramiko.Transport("my-sftp-host-or-ip", 22)

		# Note that while you *can* connect without checking the hostkey, you really
		# shouldn't. Without checking the hostkey, a malicious actor can steal
		# your credentials by impersonating the server.
		tp.connect (username = "my-username", password="my-password", hostkey=hostFingerprint)

		# Use a dictionary object to create a list of files to upload, along with their remote paths.
		# Note that the first entry attempts to upload to a directory without write permissions.
		filesToUpload = {"./Wiring Up Close - Annotated.jpeg":"./no-upload-allowed/Wiring Up Close - Annotated.jpeg",
			"./lipsum.txt":"./lipsum.txt", 
			"./3 Separate LEDs - Full Diagram - Cropped.jpeg":"./3 Separate LEDs - Full Diagram - Cropped.jpeg"}


		sftpClient = paramiko.SFTPClient.from_transport(tp)
		for key, value in filesToUpload.items():
			try:
				sftpClient.put(key, value)
				print ("[" + key + "] successfully uploaded to [" + value + "]")
			except PermissionError as err:
				print ("SFTP Operation Failed on [" + key + 
					"] due to a permissions error on the remote server [" + str(err) + "]")
			except Exception as err:
				print ("SFTP failed due to other error [" + str(err) + "]")

		# Make sure to close all created objects.
		sftpClient.close()

		tp.close()
	except paramiko.ssh_exception.AuthenticationException as err:
		print ("Can't connect due to authentication error [" + str(err) + "]")
	except Exception as err:
		print ("Can't connect due to other error [" + str(err) + "]")

if __name__ == "__main__":
	main(sys.argv[1:])





Listing 3 - Uploading Files

Hem yerel hem de uzaktan yüklenecek her dosya için tam dizinin nasıl belirtildiğine dikkat edin. Bunun nedeni, koy Yalnızca uzak dizin belirtilirse yöntem bir hata oluşturabilir.

bu “yüklemeye izin verilmez” uzak SFTP sunucusundaki dizin, böyle bir dizine yükleme yapılmaya çalışıldığında ne olduğunu göstermek amacıyla açıkça yazılamaz olacak şekilde yapılandırılır.

Beklendiği gibi, yazılabilir olmayan bir dizine yükleme denemesi bir izin hatasıyla sonuçlandı. Diğer yüklemeler başarılı oldu.

Python ile SFTP’de Dosya İndirme

bu almak yöntem, mevcut bir açık SFTP bağlantısı bağlamında dosyaları SFTP Sunucusundan indirir:

# demo-sftp-download.py

import os
import paramiko
import sys

def main(argv):
 hostkeys = paramiko.hostkeys.HostKeys (filename="known_hosts.txt")
 # The host fingerprint is stored using the ed25519 algorithm. This was revealed
 # when the host was initially connected to from the sftp program invoked earlier.
 hostFingerprint = hostkeys.lookup ("my-sftp-host-or-ip")['ssh-ed25519']


 try:
  # Note that the parameters below represent a low-level Python Socket, and 
  # they must be represented as such.
  tp = paramiko.Transport("my-sftp-host-or-ip", 22)

  # Note that while you *can* connect without checking the hostkey, you really
  # shouldn't. Without checking the hostkey, a malicious actor can steal
  # your credentials by impersonating the server.
  tp.connect (username = "my-username", password="my-password", hostkey=hostFingerprint)

  # Use a dictionary object to create a list of files to download, along with their remote paths.
  # Note that the first entry attempts to download from a directory with no files.
  
  # Note that while this dictionary shows the local path as the key and the remote path
  # as the value, the get method expects the remote path as its first parameter, so the 
  # call to that will look "backwards."
  filesToDownload = {"./Wiring Up Close - Annotated.jpeg":"./no-upload-allowed/Wiring Up Close - Annotated.jpeg",
   "./lipsum.txt":"./lipsum.txt", 
   "./3 Separate LEDs - Full Diagram - Cropped.jpeg":"./3 Separate LEDs - Full Diagram - Cropped.jpeg"}
  sftpClient = paramiko.SFTPClient.from_transport(tp)
  for key, value in filesToDownload.items():
   # Note how the remote file to download is specified first. The path to which it will be saved
   # locally is the second parameter.
   try:
    sftpClient.get (value, key)
    print ("[" + value + "] successfully downloaded to [" + key + "]")
   except FileNotFoundError as err:
    print ("File download failed because [" + value + "] did not exist on the remote server.")
    # Note that the get method may leave a zero-length file in the local path.
    # This should be deleted.
    if os.path.exists(key):
     os.remove(key)
   except Exception as err:
    print ("File download failed for [" + value + "] due to other error [" + str(err) + "]")
  
  # Make sure to close all created objects.
  sftpClient.close()
  tp.close()
 except paramiko.ssh_exception.AuthenticationException as err:
  print ("Can't connect due to authentication error [" + str(err) + "]")
 except Exception as err:
  print ("Can't connect due to other error [" + str(err) + "]")

if __name__ == "__main__":
 main(sys.argv[1:])




Listing 4 - Downloading Files

Bu listeden çıkarılan en önemli sonuç, burada kullanılan sözlüğün önceki listedekiyle aynı değerleri içermesine rağmen, indirme işlemini yürüten anahtarın aksine değer olmasıdır. Başka bir küçük bükülme, bazı durumlarda, indirilemediğinde sıfır uzunlukta bir dosyanın oluşturulmasıdır. Bu tür dosyaları silmek iyi bir uygulamadır.

Yukarıdaki liste aşağıdaki çıktıyı verir, önceki ve sonraki dizin listesine dikkat edin:

Python ve SFTP

İndirilen dosyaları gösteren Liste 4’ün çıktısı.

Dosya yüklerken olduğu gibi, var olmayan bir dosyayı indirmeye çalışırken bir hata mesajı görüntülendi.

SFTP ve Python ile Dosyaları Silme

bu kaldırmak yöntem, sunucuda oturum açmak için kullanılan hesabın bunu yapmak için yeterli izinlere sahip olduğunu varsayarak uzak sunucudaki dosyaları siler:

# demo-sftp-delete.py

import paramiko
import sys

def main(argv):
	hostkeys = paramiko.hostkeys.HostKeys (filename="/home/phil/.ssh/known_hosts")
	# The host fingerprint is stored using the ed25519 algorithm. This was revealed
	# when the host was initially connected to from the sftp program invoked earlier.
	hostFingerprint = hostkeys.lookup ("my-sftp-host-or-ip")['ssh-ed25519']


	try:
		# Note that the parameters below represent a low-level Python Socket, and 
		# they must be represented as such.
		tp = paramiko.Transport("my-sftp-host-or-ip", 22)

		# Note that while you *can* connect without checking the hostkey, you really
		# shouldn't. Without checking the hostkey, a malicious actor can steal
		# your credentials by impersonating the server.
		tp.connect (username = "my-username", password="my-password", hostkey=hostFingerprint)

		# Use a list to create a list of files to delete, including their remote paths.
		filesToDelete = [ "./no-upload-allowed/Wiring Up Close - Annotated.jpeg",
			"./lipsum.txt", "./3 Separate LEDs - Full Diagram - Cropped.jpeg",
			"./no-upload-allowed/Non-Blocking Input - Key Codes Kali.png"]

		sftpClient = paramiko.SFTPClient.from_transport(tp)

		for file in filesToDelete:
			try:
				sftpClient.remove(file)
				print ("[" + file + "] successfully deleted.")
			except PermissionError as err:
				print ("SFTP Delete Failed on [" + file + 
					"] due to a permissions error on the remote server [" + str(err) + "]")
			except FileNotFoundError as err:
				print ("SFTP Delete Failed on [" + file + "] because it was not found.")
			except Exception as err:
				print ("SFTP failed due to other error [" + str(err) + "]")

		# Make sure to close all created objects.
		sftpClient.close()

		tp.close()
	except paramiko.ssh_exception.AuthenticationException as err:
		print ("Can't connect due to authentication error [" + str(err) + "]")
	except Exception as err:
		print ("Can't connect due to other error [" + str(err) + "]")

if __name__ == "__main__":
	main(sys.argv[1:])






Listing 5 - Deleting Files

Bir silme işleminin başarısız olmasının iki yaygın nedenini kapsamak için ek istisnalar gerektiğini unutmayın.

Diğer SFTP ve Python Hususları

SFTP etkin bir Python uygulamasının amacı, indirilecek bir dosya alt kümesi üzerinde bir tür işlem gerçekleştirmekse, her dosyayı ayrı ayrı yerel bilgisayarın geçici dizinine indirmek iyi bir uygulamadır. Ek işlemler gerçekleştirmeden önce uzak bir sitenin tüm içeriğini “kopyalamak” neredeyse hiçbir zaman iyi bir fikir değildir.

Kötü amaçlı yazılım genellikle yerel bir bilgisayarın dosyalarını çalmak için SFTP kullanır ve belirli virüs tarama yazılımları genellikle FileZilla veya WinSCP gibi geleneksel bir SFTP istemcisinin kapsamı dışında gerçekleşen birden çok ardışık SFTP işlemi arar. Bu gibi durumlarda, bu virüs tarama yazılımının, SFTP etkin bir Python uygulamasının yürütülmesini basitçe engellediği bilinmektedir. SFTP’nin etkin olduğu Python uygulamalarının çalışmasına izin vermek için bir istisna oluşturmak gerekli olabilir.

Bu Python ağ programlama öğreticisinin bir sonraki bölümünde, istemci tarafında Python ve HTTPS ile çalışmanın yollarına bakacağız.

Devamını oku Python programlama eğitimleri ve yazılım geliştirme kılavuzları.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.