Python BeautifulSoup и производительность многопоточности

#python #multithreading #performance #beautifulsoup

#python #многопоточность #Производительность #beautifulsoup

Вопрос:

Я анализирую некоторые веб-страницы, используя BeautifulSoup4 и многопоточность. Каждый поток принимает задания (URL-адреса) из очереди и вызывает parse_results_page , который выполняет HTTP-запрос (вызов get_html_page_source() ), а затем анализирует каждый элемент на странице в цикле for .

Что я заметил: если у меня 5 потоков, для анализа страницы требуется от 0,7 до 8 секунд *. Если у меня 50 потоков, для анализа страницы требуется от 25 до 60 секунд *. Даже с 10 потоками время выполнения значительно увеличивается.

* выполнение измеряется после выполнения HTTP-запроса, поэтому различия не связаны с моей пропускной способностью или временем отклика сервера.

Кроме того, наличие 50 потоков увеличивает использование памяти до 6 ГБ.

Может кто-нибудь объяснить, почему это происходит и как я могу оптимизировать свой код для большего количества потоков?

Ниже приведен мой код. Спасибо

     def parse_item(self, item, *args):
    """Parse a given item
    @item - beautifulsoup.Tag object
    @queue - queue of dicts
    """
    item_url = item.find('h2', class_="heading")
    item_url = re.sub(r'.html?. $', '.html', item_url.a['href'])
    if 'redirect' in item_url or '/external/url' in item_url:
        return

    item_title = item.find('span', class_="mp-listing-title")
    item_title = item_title.get_text().strip() if item_title else None

    description = item.find('div', class_="listing-title-description")
    if description:
        description.h2.extract()
        description = description.get_text().strip()
        description = ' '.join(description.split())

    price = item.find('span', class_="price")
    if price:
        try:
            raw_price = price.get_text().strip().replace(
                                            '.', '').replace(',', '.')
            currency, price = raw_price.split()
            price = price
            try:
                currency = self.currency_code[currency.strip()]
            except KeyError:
                currency = None
                price = raw_price
        except ValueError:
            price = raw_price
            currency = None
    else:
        price, currency = None, None

    seller_id = item.find('div', class_="seller-name")
    if seller_id:
        seller_id = seller_id.find('a')
    if seller_id:
        seller_id = seller_id['href'].split('/')[-1].replace('.html',
                                                             '')
    location = item.find('div', class_='location-name')
    if location:
        location = location.get_text().split(',')
        if len(location) == 1:
            city = location[0]
            region = None
        else:
            city = location[0]
            region = location[1].strip()
    else:
        city, region = None, None
    date_posted = ''
    date_string = item.find('div', class_="date"
                            ).get_text().strip()
    if 'Vandaag' in date_string:
        date_posted = datetime.datetime.now().date().strftime(
                                                        '%Y-%m-%d')

    elif 'Gisteren' in date_string:
        date_posted = datetime.datetime.now(
                                    ).date() - datetime.timedelta(1)
        date_posted = date_posted.strftime('%Y-%m-%d')

    elif 'Eergisteren' in date_string:
        date_posted = datetime.datetime.now(
                                    ).date() - datetime.timedelta(2)
        date_posted = date_posted.strftime('%Y-%m-%d')

    else:
        date_posted = datetime.datetime.strptime(date_string.replace(
                                                            '.', ''),
                                                 "%d %b '%y")

        date_posted = date_posted.strftime('%Y-%m-%d')
    date_scraped = datetime.datetime.now().strftime(
                                                '%Y-%m-%d %H:%M:%S')
    return {"Url": item_url,
            'Category': args[0],
            "AdTitle": item_title,
            "Subcategory": args[1],
            "Description": description,
            "Currency": currency,
            "Price": price,
            "SellerId": seller_id,
            "SellerPhone": None,
            "SellerWebsite": None,
            "SellerEmail": None,
            "SellerCity": city,
            "SellerCountry": None,
            "SellerRegion": region,
            "DatePosted": date_posted,
            "DateSaved": date_scraped}

def parse_results_page(self, url, queue):
    """Get all items from a given page
    @url - string
    @queue - output queue
    """

    page = self.get_html_page_source(url)
    if not page:
        return
    page = BeautifulSoup(page, "html5lib")
    items = page.find_all('article')
    breadcrumb = page.find('ul', class_="breadcrumbs").find_all('span')
    category = breadcrumb[0].get_text().strip()
    subcategory = breadcrumb[1].get_text().strip()
    for item in items:
        queue.put(self.parse_item(item, category, subcategory))