I'm developing a scraper (python 3.9), I need to launch make_attempt() and, depending on the results of its execution, to launch additional tasks that should work concurrently.
First I create initial task and add it to a list where I store all asyncio tasks:
self.data["worker"]["tasks"]
Then I launch:
await asyncio.gather(*self.data["worker"]["tasks"])
In make_attempt() I wait for results of POST-request to a server (I use aiohttp client), and, based on the result, I either add new tasks, or repeat make_attempt() after a small delay.
I stop the current task and remove it from the list with asyncio tasks, and then I add new tasks.
async def make_attempt(self):
attempt: int = self.data["res"]["attempt"]
await self.do_something()
await sleep(1)
for task in self.data["worker"]["tasks"]:
print("Task name: %s" % task.get_name())
if task.get_name() == str(attempt):
task.cancel()
self.data["worker"]["tasks"] = [task for task in self.data["worker"]["tasks"] if task.get_name() != str(attempt)]
if 1 > 0: # a condition to start make_attempt() again
self.data["worker"]["tasks"].append(asyncio.create_task(self.make_attempt(), name=attempt))
await asyncio.gather(*self.data["worker"]["tasks"])
async def run(self):
self.data["worker"]["tasks"].append(asyncio.create_task(self.make_attempt(), name=self.data["res"]["attempt"]))
await asyncio.gather(*self.data["worker"]["tasks"])
I'm new to asyncio so maybe you could point me to mistakes or could suggest a better implementation.
Upd. Here is a schema of what I want to achieve:
main_task should be run and if the result is ok, it should launch a couple of instances of another tasks (see loop2). When a result of a task in loop2 is obtained, a new subtask should be run. main_task should wait either for all tasks in loop2 to be finished or a Timeout to be fired. All tasks in loop2 should work concurrently.
UPD 2. This code produces RecursionError: maximum recursion depth exceeded while calling a Python object after about 1000 cycles of execution of the check_base_url() method.
class Scraper:
def __init__(self, data):
self.data = data
async def get_response(self, session, url, method="get", *args, **kwargs) -> Union[
ClientResponse, None]:
for _ in range(0, 20):
if method == "get":
try:
response = await session.get(url, headers={}, proxy="_proxy", *args, **kwargs)
if response.status > 399:
raise ScraperError(response.status)
await sleep(0.1)
return response
except (ClientError, ScraperError) as err:
await sleep(0.25)
continue
else:
try:
response = await session.post(url, headers={}, proxy="_proxy", *args, **kwargs)
if response.status > 399:
raise ScraperError(response.status)
await sleep(0.1)
return response
except (ClientError, ScraperError) as err:
await sleep(0.25)
continue
return None
async def get_captcha(self) -> SolvedCaptcha:
for _ in range(0, 20):
captcha = await self.task_1()
if captcha:
continue
async def final_task(self, url) -> bool:
async with ClientSession(cookies={}) as sess:
resp_step1: Union[ClientResponse, None] = await self.get_response(sess, "url", "post",
data={})
if resp_step1:
resp_step2: Union[ClientResponse, None] = await self.get_response(sess, "url", "get")
if resp_step2:
captcha: SolvedCaptcha = await self.get_captcha()
if captcha:
resp_captcha: Union[ClientResponse, None] = await self.get_response(sess, "url",
"post",
data={})
if resp_captcha:
if 2 > 1:
print("FINISHED")
return True
else:
return False
else:
return False
else:
return False
else:
return False
async def add_task_3(self) -> None:
if 2 > 1:
subtasks = [asyncio.create_task(self.final_task(self.data["res"]["slots_urls"][0]))]
await asyncio.gather(*subtasks)
else:
await self.add_task_3()
def parse(self, html: str, url: str) -> None:
soup = BeautifulSoup(html, "lxml")
# do parsing
async def task_2(self, url) -> bool:
async with ClientSession() as sess:
resp: Union[ClientResponse, None] = await self.get_response(sess, url)
if not resp:
return False
html = await resp.text()
self.parse(html, url)
async def add_task_2(self) -> None:
if 2 > 1:
subtasks = [asyncio.create_task(self.task_2(url)) for url in ["url1", "url2"]]
await asyncio.gather(*subtasks)
async def task_1(self) -> bool:
self.data["res"]["captcha_requested"] += 1
res = await self.captcha.task_1()
if not res:
return False
return True
async def add_task_1(self) -> None:
if 2 > 1:
subtasks = [asyncio.create_task(self.task_1()) for _ in range(0, 5)]
await asyncio.gather(*subtasks)
async def get_calendar_url(self, sess) -> bool:
resp: Union[ClientResponse, None] = await self.get_response(sess, "url", method="post",
data={})
if not resp:
return False
else:
return True
async def check_base_url(self) -> bool:
async with ClientSession() as session_0:
return await self.get_calendar_url(session_0)
async def schedule_tasks(self):
def start_again() -> bool:
if 2 > 1:
return True
return False
res_base_url: bool = await self.check_base_url()
if res_base_url:
tasks = [asyncio.create_task(self.add_task_1()),
asyncio.create_task(self.add_task_2()),
asyncio.create_task(self.add_task_3())]
await asyncio.gather(*tasks)
if start_again():
await sleep(0.1)
await self.schedule_tasks()
else:
await self.schedule_tasks()
async def run(self):
await self.schedule_tasks()
