Multiprocess + asyncio kullanarak request göndermek

Herkese merhaba arkadaşlar. Önemli bir problemim var. Multiprocess ile bir liste içerisindeki binlerce subdomaine requests gönderip , dönen response içinde bazı anahtar kelimeler arıyorum. Fakat bir sorunum var. Bazı subdomainleri tarayıcıda açmaya çalıştığında ,tarayıcıdaki yuvarlak simge hep döner ve site hiç açılmaz. Bu tür sitelere request gönderdiğimde program kilitleniyor. Fakat request gönderirken timeout belirttiğimde bu sorun aşılıyor. Bu seferde program çok yavaşlıyor. Yani buradan tek çıkış yöntemi olarak zaman uyumsuz asenkron olarak request göndermek. Ben bir çok örnek gördüm ama request göndermek için türkçe asenkron bilgilerine ulaşamadım. Bu yüzden değerli hocalarımızdan yardım istemeye karar verdim. Örnek olarak kullandığım programı aşağıya yazıyorum. Lütfen bu örneği asekron olarak nasıl yapabilirim yazarsanız sevinirim.

İmport requests
from multiprocessing import Pool, cpu_count

def gonder(url):
    try:

        response= requests.get(url).text
        if "welcome" in response:
            print("merhaba dünya")

    except:
        pass

if __name__ == "__main__":
    url_listesi = ["https://www.google.com"] * 50
    #50 kere google.com'a request göndereceğiz

    with Pool(cpu_count()) as p:
        p.map(gonder,url_listesi)

Ben yukarıdaki programı asenkron olarak çalıştırmak istiyorum. Bu program 50 kere google’a istek gönderecek ama herhangi bir zaman aşımına uğramayacak . Fakat benim kullandığım url adreslerinin çoğunda bu sorun var. Beklemek yerine asenkron bir şekilde bir sonraki url adresini denemesi gerekiyor programın. Lütfen cevap yazarken , kodlarınız ile cevap verirseniz anlamam açısından daha kolay olur.

1 Beğeni

Şöyle bir şeyden mi bahsediyorsunuz?

#!/usr/bin/python3.8
# -*- coding: utf-8 -*-

import requests
import asyncio


async def gonder(url):
    try:
        response = requests.get(url).text
        if "welcome" in response:
            print("merhaba dünya")            
    except:
        pass
    finally:
        await asyncio.sleep(0.1)
        
        
async def main():
    url_listesi = ["https://www.google.com"] * 50
    await asyncio.gather(*(gonder(i) for i in url_listesi))
        

if __name__ == "__main__":
    asyncio.run(main())

öncelikle cevap verdiğiniz için teşekkür ederim. evet hemen hemen istediğim gibi bir örnek yapmışsınız. elinize sağlık. ekstra olarak benim gösterdiğim örnek kodda kullandığım gibi multiprocess kullanabilimiyim ? yada concurrent.futures ThreadPoolExecutor ?? ayrıca aiohttp kullanmamıza gerek varmıdır sizin örneğiniz üzerinden geliştirme yaparak ? yoksa bu isterlerimi karşılayacaakmıdır? sizin kodu çalıştırdım sanki thread kullanıyormuşum gibi hissettim. birde benim ana programrama entegre ettikten sonra test edeceğim. şimdiden yaazacağınız cevaplar için teşekkür ederim.

Ornek zaten cok process’te paralel olarak calisiyor ama process sayisi olarak CPU sayisi belirlenmis—yanlis. Is ezici agirlikla I/O tabanli oldugu icin cok daha fazla process calistirilmasi lazim.

Process paralelliginin yaninda neden process ici asenkron istek ve/ya asyncio istenir bilemedim. Ama her halukarda istek kutuphanesinin asyncio desteklemesi lazim. (Veya istek-basina-thread modelinde klasik multithreading yapilacak.)

3 Beğeni

thread ile bir örnek yapabilirmisin ?

Bu kütüphaneyi buldum, ancak indirme işlemi için Windows Terminal Microsoft Visual C++ Build Tools’a sahip olmanız lazım.


Tabii bu kadar uğraşmak yerine threading de kullanabilirsiniz.

dostum benim sorunum thread ile ilgili değil. Bak aşağıdaki url benzeri url adresleri çıkıyor. Bu yüzden programı çok askıda bekletiyor. istersen aşağıdaki url adresine bir requests gönder ve ne demek isteiğimi anlarsın…

http://certpospa2.starbucks.com

bu arada @dildeolupbiten senin programdada yukarıdaki starbucks url adresine gelince bekliyor. yani devam etmiyor. yani yukarıdaki url 'ye request atıp programın devam etmesi gerekmezmiydi? lütfen yukarıdaki url adresindeki beklemeyi nasıl geçeceğim.

@aib’in de dediği gibi thread per client yapabilirsiniz. O zaman bekleme ile alakalı sıkıntı yaşamamış olursunuz. Zaten threading modülünü biliyorsanız bunu kendinizin de yapabilmeniz lazım.

@EkremDincel güzel kardeşim zaten thread veya multiprocess kullanıyorum. fakat request attıktan sonra thread kullansan dahi bir sonraki url adresine geçmiyor. sen thread kullansanda bu url adresinden ( http://certpospa2.starbucks.com ) cevap gelmedikçe program bır sonraki url adresine request atmıyor. daha sonra bu yukarıdaki url adresine request gönderirken timeout= değeri girmediysen, baya bir askıda bekletiyor programı. aç python idle programı ve ne demek istediğimi görürsün. 6 tane url seç ve yukarıdaki starbucks url adresini tam ortalarda bir sıraya koy. öyle br dene …

Ben seni anladım da sen threadı join ettiriyorsun galiba. Bu kodu dene istiyorsan:

from threading import Thread
import requests as r

urls = [
"http://certpospa2.starbucks.com/",
"http://www.google.com/",
"https://forum.yazbel.com/t/multiprocess-asyncio-kullanarak-request-gondermek/4978/9"
]

requests = []

def connect(url):
    c = r.get(url)
    print(url, "adresine bağlanıldı.")
    requests.append(c)


for i in urls:
    Thread(target=connect,args=(i,)).start()
    
1 Beğeni

@EkremDincel evet thread işimi gördü. Ama şuan 200 thread kullandım ve timeout=2 yaptım ve ilk defa iyi birşekilde çalıştı. şimdi elimdeki url adreslerinin başında http:// veya https:// protokolleri yoktur. bu yüzden hangi url adresi http:// hangi url https:// ile request kabul ediyor bilmiyorum. bu yüzden try & except kullanıyorum. ilk önce http ile deniyor ve timeout=2 saniye beklemesi gerekiyor. daha sonra connectionerror verirse bu sefer https:// ile yeniden request gönderiyor ve timeout=2 … ayrıca dönen response içerisinde çeşitli kelimeler bulmak için request den sonra response başka fonksiyona gönderiyordum. benim hatam şuarada olmuş. thread kullanırken toplam thread’i ayarlamıyordum. thread sayısını cpu_count ayarlıyordum. şimdi thread’i 200 yaptım bomba gibi çalıştı. şimdi ben kullandığm kodları buraya yazmaak istiyorum belki benim gibi sorun yaşayan arakdaşlar olursa bu kodlara bakıp işlerini görebilirler. açıkça thread kullanımı senin gibi yapmıyorum. daha kolay ve daha hızlı kullanmanın bir yolu var.

import requests
from concurrent.futures import ThreadPoolExecutor

#ssl den doğacak hataları bertaraf etmek için 
requests.packages.urllib3.disable_warnings()

def gonder(url):
    header = {"User-Agent":"Mozilla/5.0 (Linux; U; Android 2.2) Mobile Safari/533.1"}
    
    # url adreslerinin hangi protokolü kullandığını bilmediğimiz için ilk önce http
    # ile requests gönderiyoruz. eğer hata alırsak https ile request gönderiyoruz
    # yine başarısız olursak pass geçiyoruz.
    try:

        istek = "http://"+str(url)
        response = requests.get(istek,headers=header, verify=False, timeout=2)
        print(f"request başarılı oldu: {istek}")

    except requests.exceptions.ConnectionError:

        istek = "https://"+str(url)
        response = requests.get(istek,headers=header, verify=False, timeout=2)
        print(f"request başarılı oldu: {istek}")

    except:
        pass
    

def main():
    # subdomains.txt adlı url listesini açıyoruz. Fakat bu url adreslerinin başında 
    # http:// veya https:// protokolleri yoktur

    url_listesi = list()
    with open("subdomains.txt", "r", encoding="utf-8") as f:
        for url in f.read().lower().split("\n"):
            if url:
                url_listesi.append(url)
            else:
                pass
    
    #max_workers ile 200 tane thread kullanmak istediğimizi belirtiyoruz
    
    with ThreadPoolExecutor(max_workers=200) as executor:
        for i in url_listesi:
            executor.submit(gonder,i)

if __name__ == "__main__":

    main()

işte request ile ilgili sorunları bu şekilde çözdüm. subdomain listemde 10.000 den fazla url adresi var. bug bounty programlarına katılıyorum ve web sitelerinde bulduğum güvenlik açıkları için ödül alıyorum. Bu sebeb ile kendi programlarımı yapmak zorundayım. binlerce url adresine dinamik olarak her 2 satte bir request gönderip analiz yapmam gerekiyor. şimdi crontab kullanarak programımı her 2 satte çalışacak şekilde ayarlayabilirim. Cevap yazan herkesden ALLAH razı olsun.

Eğer işinizi gördüyse ne güzel. http mi https mi diye de sürekli kontrol etmeniz programınızı yavaşlatır. Tek yapmanız gereken bir defa programı çalıştırıp doğru protokolü bulmak ve url’nin tam halini bir dosyaya yazmak. Bir daha da protokol kontrolü ile uğraşmazsınız.

Buradaki hızdan kastınız performans ise bunun çok doğru olduğu söylenemez. Çünkü kullandığınız sınıf zaten threading modülünü kullanıyor. Ama güvenlik ve kullanım kolaylığı açısından tabii ki daha iyidir.

ThreadPoolExecutor sınıfı hakkında çok bilgi sahibi değilim ama bana sanki url sayısı kadar örneğini oluşturuyormuşsunuz gibi geldi. with ThreadPoolExecutor(max_workers=200) as executor: ifadesini with open("subdomains.txt", "r", encoding="utf-8") as f: ifadesini de kapsayacak şekilde yazmanız gerekmiyor muydu?

Kodunuzu test ettim ve bu dediğimin doğru olduğunu anladım. URL sayısı kadar ThreadPoolExecutor örneği oluşturuyorsunuz. with ThreadPoolExecutor(max_workers=200) as executor: ifadesini en dışa taşıyın.

yukarıdakii kodda bir eksiklik yapmışım. Normalde ilk önce subdomains.txt dosyasından aldığım subdomainleri bir listeye kayıt ediyorum. örnek : url_listesi
ve aşağıdaki gibi çalıştırıyordum.

with ThreadPoolExecutor(max_workers=200) as executor:
    for i in url_listesi:
        executor.submit(gonder,i)

şimdi en üstte ki kodu tekrar düzenleyim… bidaha kontrol edermisin.