[Python] Optimising requests in my Python code using asynchronicity

Discussão em 'Python' iniciado por Stack, Dezembro 1, 2024.

  1. Stack

    Stack Membro Participativo

    I’m currently attempting to optimise a Python script I wrote that makes multiple HTTP requests using the aiohttp library. I want to take advantage of asynchronous programming in order to handle requests concurrently, without blocking the execution of the rest of the program. I have written the following code with the goal of improving performance by making non-blocking HTTP requests, but after running the new asynchronous code, I don't observe any significant performance improvements when compared to the previous, synchronous version of the script.

    I suspect there might be something I'm missing or doing incorrectly. I’m hoping to understand what could be going wrong with my implementation—whether it's a problem with how I'm structuring the asynchronous code, how I'm managing concurrency, or something else that could be affecting the performance

    import traceback
    import requests
    import aiohttp
    from bs4 import BeautifulSoup

    # Helper function to get BeautifulSoup object
    async def get_soup(session, url, headers, cookies):
    async with session.get(url, headers=headers, cookies=cookies, timeout=30) as response:
    if response.status == 200:
    html_text = await response.text()
    return BeautifulSoup(html_text, "html.parser")
    else:
    raise Exception(f"Failed to retrieve {url}, status code: {response.status}")

    # Worker function for processing each page
    async def process_page(session, site_index, start_page_num, end_page_num, new_listings):
    site = sites[site_index]
    cur_url = site['url']
    site_headers = site['headers']
    cassing = site['casings'][0]['cassingIdentity']
    link_MD = site['casings'][0]['link']
    sitename = site['pageTitle']
    individual_Scraper_Metadata = site['individualCasings'][0]
    COUNTER = site['URL_Counter']

    # Prepare URL for first page
    current_page_num = start_page_num
    end_page_num *= COUNTER

    async with aiohttp.ClientSession() as session:
    try:
    while True:
    # Format the URL dynamically based on the page number
    if cur_url.count('{}') > 1:
    cur_url2 = cur_url.format(current_page_num-1, current_page_num-1)
    else:
    cur_url2 = cur_url.format(current_page_num)
    print(f"Requesting URL: {cur_url2}")

    # Retrieve and parse HTML
    soup = await get_soup(session, cur_url2, site_headers, COOKIEJAR)

    # Get the main listings from the page
    raw_listings = soup.find_all(cassing["tag"], class_=cassing["classname"])
    print(f"Found {len(raw_listings)} raw listings on page {current_page_num}")

    if len(raw_listings) == 0:
    print(f"No listings found at {cur_url2}")
    break # Exit if no listings are found

    # Extract data from each listing
    for raw_listing in raw_listings:
    page_listing = individualListingScraper(individual_Scraper_Metadata, raw_listing, link_MD)
    page_listing['pageNo'] = current_page_num
    page_listing['siteName'] = sitename
    new_listings.append(page_listing)

    # Terminate the loop based on the page number
    if end_page_num != -1 and current_page_num >= end_page_num:
    print(f"Reached last page {current_page_num}")
    break
    current_page_num += COUNTER

    return finalReturn(sitename, new_listings)

    except Exception as e:
    print(f"Error processing page {current_page_num}: {str(e)}")
    traceback.print_exc()

    # Main function to coordinate scraping tasks
    async def autoScraper(SITE_INDEX, START_PAGE_NUM=1, END_PAGE_NUM=-1):
    global COOKIEJAR
    new_listings = []

    if SITE_INDEX >= len(sites):
    return "site number not found"

    site = sites[SITE_INDEX]
    try:
    # Set up cookies for subsequent requests
    initial_response = await aiohttp.ClientSession().get("https://www.google.com/")
    COOKIEJAR = initial_response.cookies

    # Start scraping process
    await process_page(aiohttp.ClientSession(), SITE_INDEX, START_PAGE_NUM, END_PAGE_NUM, new_listings)

    return new_listings

    except Exception as e:
    print(f"Error in autoScraper: {str(e)}")
    return new_listings

    def individual_listing_scraper(individual_metadata, raw_listing_html, link_metadata, site_headers, cookie_jar, timeout_sec):
    try:
    # Extract the listing URL
    listing_url_tag = raw_listing_html.find(link_metadata['tag'], class_=link_metadata['classname'])
    if not listing_url_tag:
    print("Error: Listing URL not found in the raw HTML.")
    return {}

    url = listing_url_tag.get(link_metadata['attrib'])
    if not url:
    print("Error: URL attribute not found in the tag.")
    return {}

    url = link_metadata['baseurlLink'] + url

    # Fetch the HTML of the individual listing page
    response = requests.get(url, headers=site_headers, cookies=cookie_jar, timeout=timeout_sec, verify=False)
    response.raise_for_status() # Raise an error for bad HTTP responses
    individual_page_html = BeautifulSoup(response.text, 'html.parser')

    # Extract the listing data based on metadata
    tag = individual_metadata['cassingIdentity']['tag']
    classname = individual_metadata['cassingIdentity']['classname']
    _id = individual_metadata['cassingIdentity']['id']
    index = individual_metadata['cassingIdentity']['index']

    listing_raw_html = individual_page_html.find_all(tag, class_=classname, id=_id)
    if not listing_raw_html or len(listing_raw_html) <= index:
    print(f"Error: Listing data not found or index out of range.")
    return {}

    listing_data = listing_raw_html[index]

    # Assuming `bulklisting_scraper` function processes this data further
    listing = bulklisting_scraper(individual_metadata, listing_data)
    listing['link'] = url
    return listing

    except requests.exceptions.RequestException as e:
    print(f"Request error: {e}")
    except Exception as e:
    print(f"Unexpected error: {e}")
    traceback.print_tb(e.__traceback__)
    return {}

    Continue reading...

Compartilhe esta Página