We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Concurrency

Your web crawler works – but it's crawling pages one at a time. It would take us a really long time to crawl a large website. Let's make it faster using coroutines. This is another long step, but don't get discouraged!

Assignment

    • base_url (the starting URL)
    • base_domain (the domain name)
    • page_data (our dictionary of page data, keyed by normalized URL)
    • lock (an asyncio.Lock to safely update page_data)
    • max_concurrency (to limit the number of requests allowed at once)
    • semaphore (an asyncio.Semaphore - pass it the value of max_concurrency)
    • session (an aiohttp.ClientSession for making HTTP requests)
  1. async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        return self
    
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.session.close()
    
  2. async def add_page_visit(self, normalized_url):
    
    • Take a normalized URL
    • Use async with on the lock field to safely check the page_data dictionary
    • Check if the normalized URL is already a key in the page_data dictionary
    • Return True if it's the first time visiting the page, otherwise return False
    • Use the aiohttp client you set in the session field along with async with to fetch the page.
    • Keep the same error, status, and header checks
    • Call and await your new add_page_visit method. If it isn't a new page, return early
    • Use async with self.semaphore to limit the number of concurrent requests
    • Fetch the page's HTML and extract page data using extract_page_data
    • Add the page data to page_data dictionary using the normalized URL as the key (use the lock to do this safely)
    • Extract new URLs from the page
    • For each URL, create a task to crawl it using asyncio.create_task
    • Wait for all tasks with await asyncio.gather(*tasks)
    • Creates and uses an AsyncCrawler in an async with block
    • Calls/awaits crawl() and returns the final page_data dictionary
    • Instead of calling get_html, call crawl_site_async(base_url) and await the results
    • When iterating over the page data, use page_data.values() to get the page dictionaries
    • Update the if __name__ == "__main__" block to run your async main function using asyncio.run(main_async())

Tips

  • Make sure you're not crawling the same page multiple times. That's why my add_page_visit method returns a boolean: to indicate if it's the first time we've seen the page.
  • Ensure you are only crawling URLs that belong to the same domain.
  • Make sure you're using the semaphore to cap simultaneous requests