Парсинг Амазона на easy без мам, пап и ипотек

Попался мне на глаза на просторах интернета скрипт, который позволяет парсить карточки товаров с Амазона. А мне как раз было необходимо решение подобной задачи.

Я сломал себе голову в поисках того, как спарсить карточки товаров в Амазоне. Проблема в том, что у Амазона используется разные варианты дизайна под различную выдачу, в частности – если необходимо спарсить карточки по поисковому запросу «bags» - карточки будут расположены вертикально, как мне и нужно, а вот если взять, к примеру «t-shirts» - тут уже карточки расположены горизонтально, и с таким расположение скрипт выпадает в ошибку, он отрабатывает открытие страницы, но не хочет скроллить.

Более, того, начитавшись различных статей, где юзеры ломают голову, как обойти капчу на Амазон, я апгрейдил скрипт и теперь он может обойти капчу, если она встретится (работает при помощи 2капча), скрипт проверяет наличие капчи на странице, после каждой загрузки новой страницы и если капча встречается – отправляет запрос на сервер 2капча, а после получения решения, подставляет его и продолжает работу.

Однако, как обойти капчу не самый сложный вопрос, так как это нетривиальная задача в наше время, более насущный вопрос – как сделать так, чтобы скрипт отрабатывал не только с вертикальным расположением карточек товаров, но и с горизонтальным.Ниже я подробно распишу что в себя включает скрипт, продемонстрирую его работу, а если вы сможете помочь в решении задачи, что добавить (поменять) в скрипте, чтобы он срабатывал на горизонтальные карточки – буду благодарен.

Ну, а кому-то скрипт поможет хотя бы в своем ограниченном функционале.

Итак, давайте разберем скрипт по кусочкам!

Подготовка
Сперва скрипт импортирует необходимые для выполнения задачи модули

from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import csv import os from time import sleep import requests

Разберем по частям:

from selenium import webdriver

Импортирует класс webdriver, который позволяет управлять браузером (в моем случае Firefox) через скрипт

from selenium.webdriver.common.by import By

Импортирует класс By, с помощью которого скрипт будет искать элементы для парсинга по XPath (он может искать и другие атрибуты, но в данном случае будет использоваться XPath)

from selenium.webdriver.common.keys import Keys

Импортирует класс Keys, который будет использоваться для симуляции нажатия на клавишы, в случае с этим скриптом, это будет прокрутка страницы вниз Keys.PAGE_DOWN

from selenium.webdriver.common.action_chains import ActionChains

Импортирует класс ActionChains, для создания сложных последовательных действий, в нашем случае – нажатие на кнопку PAGE_DOWN и ожидание загрузки всех элементов на странице (так как в Амазоне карточки загружаются по мере скролла)

from selenium.webdriver.support.ui import WebDriverWait

Импортирует класс WebDriverWait который ждет, пока искомая нам информация прогрузилась, например описание товара, которое мы будем выискивать по XPath

from selenium.webdriver.support import expected_conditions as EC

Импортирует класс expected_conditions (сокращенно EC) который работает в связке с предыдущим классом и указывает WebDriverWait, какое конкретное условие ему необходимо ждать. Повышает надежность работы скрипта, чтобы он не начал взаимодействовать с еще незагруженным содержимым.

import csv

Импортирует модуль csv, для работы с csv файлами.

import os

Импортирует модуль os, для работы с операционной системой (создание директорий, проверка наличие файлов и т.п.).

from time import sleep

Импортируем функцию sleep – эта та самая функция, которая будет приостанавливать скрипт на конкретное время (в моем случае 2 сек, но можно поставить и больше) чтобы элементы при скролле прогрузились.

import requests

Импортирует библиотеку requests, для отправки HTTP-запросов, для взаимодействия с сервисом распознавания капчи 2капча.

Настройка

После того, как все импортировано, скрипт приступает к настройке браузера для работы, в частности:

Установка ключа АПИ для обращения к сервису 2капча

# API key for 2Captcha API_KEY = "Your API Key"

В скрипте прописан user-agent (его естественно, можно менять), который устанавливается для браузера. После этого происходит запуск браузера с указанными настройками.

user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" options = webdriver.FirefoxOptions() options.add_argument(f"user-agent={user_agent}") driver = webdriver.Firefox(options=options)

Далее идет модуль для решения капчи. Это именно то место, которое ищут пользователи по запросу как решить капчу. Долго разбирать этот кусок кода не будем, так как с ним особо проблем не возникало.

Вкратце – скрипт, после каждой загрузки страницы, проверяет наличие капчи на странице и если обнаруживает ее там – решает ее путем отправки на сервер 2капча, если капчи нет, просто продолжает выполнение дальше.

def solve_captcha(driver): try: captcha_element = driver.find_element(By.CLASS_NAME, 'g-recaptcha') if captcha_element: print("Captcha detected. Solving...") site_key = captcha_element.get_attribute('data-sitekey') current_url = driver.current_url # Запрос решения капчи к 2Captcha captcha_id = requests.post( 'http://2captcha.com/in.php', data={ 'key': API_KEY, 'method': 'userrecaptcha', 'googlekey': site_key, 'pageurl': current_url } ).text.split('|')[1] # Ожидание решения капчи recaptcha_answer = '' while True: sleep(5) response = requests.get(f"http://2captcha.com/res.php?key={API_KEY}&action=get&id={captcha_id}") if response.text == 'CAPCHA_NOT_READY': continue if 'OK|' in response.text: recaptcha_answer = response.text.split('|')[1] break # Ввод решения капчи на странице driver.execute_script(f'document.getElementById("g-recaptcha-response").innerHTML = "{recaptcha_answer}";') driver.find_element(By.ID, 'submit').click() sleep(5) print("Captcha solved.") except Exception as e: print("No captcha found or error occurred:", e)

Парсинг
Далее идет участок кода, который отвечает за перебор страниц, их загрузку и прокрутку

try: base_url = "https://www.amazon.in/s?k=bags" for page_number in range(1, 10): page_url = f"{base_url}&page={page_number}" driver.get(page_url) driver.implicitly_wait(10) solve_captcha(driver) WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//span[@class="a-size-medium a-color-base a-text-normal"]'))) for _ in range(5): ActionChains(driver).send_keys(Keys.PAGE_DOWN).perform() sleep(2)

Следующий кусок – сбор данных о товарах. Самый важный участок. В этой части скрипт изучает загруженную страницу и забирает оттуда данные, которые указаны, в нашем случае это – название продукта, количество отзывов, цена, URL, оценка товара.

product_name_elements = driver.find_elements(By.XPATH, '//span[@class="a-size-medium a-color-base a-text-normal"]') rating_number_elements = driver.find_elements(By.XPATH, '//span[@class="a-size-base s-underline-text"]') star_rating_elements = driver.find_elements(By.XPATH, '//span[@class="a-icon-alt"]') price_elements = driver.find_elements(By.XPATH, '//span[@class="a-price-whole"]') product_urls = driver.find_elements(By.XPATH, '//a[@class="a-link-normal s-underline-text s-underline-link-text s-link-style a-text-normal"]') product_names = [element.text for element in product_name_elements] rating_numbers = [element.text for element in rating_number_elements] star_ratings = [element.get_attribute('innerHTML') for element in star_rating_elements] prices = [element.text for element in price_elements] urls = [element.get_attribute('href') for element in product_urls]

Далее происходит выгрузка указанных данных в папку (для каждой страницы создается свой файл csv, который сохраняется в папку output files), если папка отсутствует, скрипт ее создает.

output_directory = "output files" if not os.path.exists(output_directory): os.makedirs(output_directory) with open(os.path.join(output_directory, f'product_details_page_{page_number}.csv'), 'w', newline='', encoding='utf-8') as csvfile: csv_writer = csv.writer(csvfile) csv_writer.writerow(['Product Urls', 'Product Name', 'Product Price', 'Rating', 'Number of Reviews']) for url, name, price, star_rating, num_ratings in zip(urls, product_names, prices, star_ratings, rating_numbers): csv_writer.writerow([url, name, price, star_rating, num_ratings])

И завершающий этап – завершение работы и высвобождение ресурсов.

finally: driver.quit()

Полный скрипт

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import csv import os from time import sleep import requests # API key for 2Captcha API_KEY = "99db184a48626f5d5325216f4dbed069" # Set a custom user agent to mimic a real browser user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" options = webdriver.FirefoxOptions() options.add_argument(f"user-agent={user_agent}") driver = webdriver.Firefox(options=options) def solve_captcha(driver): # Check for the presence of a captcha on the page try: captcha_element = driver.find_element(By.CLASS_NAME, 'g-recaptcha') if captcha_element: print("Captcha detected. Solving...") site_key = captcha_element.get_attribute('data-sitekey') current_url = driver.current_url # Send captcha request to 2Captcha captcha_id = requests.post( 'http://2captcha.com/in.php', data={ 'key': API_KEY, 'method': 'userrecaptcha', 'googlekey': site_key, 'pageurl': current_url } ).text.split('|')[1] # Wait for the captcha to be solved recaptcha_answer = '' while True: sleep(5) response = requests.get(f"http://2captcha.com/res.php?key={API_KEY}&action=get&id={captcha_id}") if response.text == 'CAPCHA_NOT_READY': continue if 'OK|' in response.text: recaptcha_answer = response.text.split('|')[1] break # Inject the captcha answer into the page driver.execute_script(f'document.getElementById("g-recaptcha-response").innerHTML = "{recaptcha_answer}";') driver.find_element(By.ID, 'submit').click() sleep(5) print("Captcha solved.") except Exception as e: print("No captcha found or error occurred:", e) try: # Starting page URL base_url = "https://www.amazon.in/s?k=bags" for page_number in range(1, 2): page_url = f"{base_url}&page={page_number}" driver.get(page_url) driver.implicitly_wait(10) # Attempt to solve captcha if detected solve_captcha(driver) # Explicit Wait WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//span[@class="a-size-medium a-color-base a-text-normal"]'))) for _ in range(5): ActionChains(driver).send_keys(Keys.PAGE_DOWN).perform() sleep(2) product_name_elements = driver.find_elements(By.XPATH, '//span[@class="a-size-medium a-color-base a-text-normal"]') rating_number_elements = driver.find_elements(By.XPATH, '//span[@class="a-size-base s-underline-text"]') star_rating_elements = driver.find_elements(By.XPATH, '//span[@class="a-icon-alt"]') price_elements = driver.find_elements(By.XPATH, '//span[@class="a-price-whole"]') product_urls = driver.find_elements(By.XPATH, '//a[@class="a-link-normal s-underline-text s-underline-link-text s-link-style a-text-normal"]') # Extract and print the text content of each product name, number of ratings, and star rating, urls product_names = [element.text for element in product_name_elements] rating_numbers = [element.text for element in rating_number_elements] star_ratings = [element.get_attribute('innerHTML') for element in star_rating_elements] prices = [element.text for element in price_elements] urls = [element.get_attribute('href') for element in product_urls] sleep(5) output_directory = "output files" if not os.path.exists(output_directory): os.makedirs(output_directory) with open(os.path.join(output_directory, f'product_details_page_{page_number}.csv'), 'w', newline='', encoding='utf-8') as csvfile: csv_writer = csv.writer(csvfile) csv_writer.writerow(['Product Urls', 'Product Name', 'Product Price', 'Rating', 'Number of Reviews']) for url, name, price, star_rating, num_ratings in zip(urls, product_names, prices, star_ratings, rating_numbers): csv_writer.writerow([url, name, price, star_rating, num_ratings]) finally: driver.quit()

Таким образом скрипт отрабатывает без ошибок, но только для вертикальных карточек товара. Вот пример работы скрипта.

Начать дискуссию