인프런 커뮤니티 질문&답변

iniman님의 프로필 이미지
iniman

작성한 질문수

파이썬 동시성 프로그래밍 : 데이터 수집부터 웹 개발까지 (feat. FastAPI)

비동기를 while로 반복하는 방법 문의

작성

·

1K

0

안녕하세요

알려주신 코드를 10개씩 비동기로 묶어서 while로 돌리고 싶은데 잘 안되네요

아래와 같은 코드로 했을 때 얼핏 잘 되는 것 같지만

다른 티스토리 블로그로 바꿔서 해보면 중복 데이터가 발생합니다

비동기를 while 안에 넣어서 써도 되는건지 알고 싶습니다

from bs4 import BeautifulSoup
import aiohttp
import asyncio


async def fetch(session, url, i):
    async with session.get(url) as response:
        html = await response.text()
        soup = BeautifulSoup(html, "html.parser")
        tag = soup.find("p", class_="txt_thumb")  # 첫번째 글제목만
        print(tag.text)


async def main(jump):
    BASE_URL = "https://bjpublic.tistory.com/category/%EC%A0%84%EC%B2%B4%20%EC%B6%9C%EA%B0%84%20%EB%8F%84%EC%84%9C"
    urls = [f"{BASE_URL}?page={i}" for i in range(j + 1, j + 11)]
    async with aiohttp.ClientSession() as session:
        await asyncio.gather(*[fetch(session, url, i) for i, url in enumerate(urls)])


if __name__ == "__main__":
    j = 0
    while True:
        asyncio.run(main(j))
        print("--------")
        j = j + 10
        if j == 20:
            break

답변 1

0

안녕하세요, 인프런 AI 인턴이에요.

비동기 함수를 while 루프 안에서 실행시키는 것은 가능합니다. 하지만 이 경우 비동기 함수가 모두 완료되기 전에 while 루프가 계속 실행되어 중복 데이터가 발생할 수도 있습니다. 이를 방지하기 위해서는 asyncio.Queue를 사용하는 것이 좋습니다.

아래는 asyncio.Queue를 사용하여 비동기 함수를 while 루프 안에서 실행시키는 예시 코드입니다.

import asyncio
from bs4 import BeautifulSoup
import aiohttp


async def fetch(session, url):
    async with session.get(url) as response:
        html = await response.text()
        soup = BeautifulSoup(html, "html.parser")
        tag = soup.find("p", class_="txt_thumb")
        return tag.text


async def worker(queue):
    async with aiohttp.ClientSession() as session:
        while True:
            url = await queue.get()
            if url is None:
                break
            result = await fetch(session, url)
            print(result)
            queue.task_done()


async def main():
    BASE_URL = "https://bjpublic.tistory.com/category/%EC%A0%84%EC%B2%B4%20%EC%B6%9C%EA%B0%84%20%EB%8F%84%EC%84%9C"
    queue = asyncio.Queue()
    urls = [f"{BASE_URL}?page={i}" for i in range(1, 21)]
    for url in urls:
        queue.put_nowait(url)
    workers = [asyncio.create_task(worker(queue)) for _ in range(10)]
    await queue.join()
    for _ in range(10):
        queue.put_nowait(None)
    await asyncio.gather(*workers)


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

위 코드는 크게 worker 함수와 main 함수로 나눌 수 있습니다. worker 함수는 비동기적으로 실행할 함수(fetch)를 실행하고 결과를 출력하는 역할을 합니다. main 함수는 worker를 10개 생성하여 큐에 있는 URL을 worker들이 가져가며 비동기적으로 fetch 함수를 실행합니다.

크게 두 가지만 수정을 하면 대부분의 문제가 해결됩니다. BASE_URL를 %20(공백)으로 변경해주시고 while문 내부에 있는 조건문의 비교상수를 30으로 변경해주세요. 그러면 1페이지부터 30페이지까지의 타이틀을 가져올 수 있습니다.

iniman님의 프로필 이미지
iniman

작성한 질문수

질문하기