Попался мне на глаза на просторах интернета скрипт, который позволяет парсить карточки товаров с Амазона. А мне как раз было необходимо решение подобной задачи.
Я сломал себе голову в поисках того, как спарсить карточки товаров в Амазоне. Проблема в том, что у Амазона используется разные варианты дизайна под различную выдачу, в частности – если необходимо спарсить карточки по поисковому запросу «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 = "Your API Key"
# 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()
Таким образом скрипт отрабатывает без ошибок, но только для вертикальных карточек товара. Вот пример работы скрипта.
Буду рад обсудить его в комментариях, если есть что сказать по делу.
Комментарии (5)
BasiC2k
27.08.2024 19:44+1Надеюсь Вы дойдёте до уровня rainforestapi.com
kentavr009
27.08.2024 19:44О, прикольно, никогда не встречал такой сервис. Зашел почитать комментарии, называется))
barraqud
27.08.2024 19:44Насколько помню, там вообще не нужен драйвер, хватит и одного requests(читай scrappy/aiohttp/httpx), разве что allow_redirects возможно выключить надо. Главное взять asin с поисков, а остальное собрать со страниц карточек(есть паттерн, есть asin, убираете query параметры и в путь, вроде там все нужное просто в аттрибутах лежит. Ну и естественно в асинхронку, только robots.txt не игнорируем:)))). Кстати небольшая хитрость для автора, на том же apify(или аналогах) есть возможность взять пробный пакет, а asin бывают списками в открытых доступах. Так вот если запустить и глянуть логи, то там частенько видно весь процесс сборки. просто повторив можно много времени выиграть и код там часто актуальный:)
sergeseme
27.08.2024 19:44Для эпизодических парсингов пользуюсь расширением браузера Instant Data Scraper - оно отлично справляется с разными расположениями карточек.
HyperWin
Я бы проверил, какую именно информацию он не может получить, и посмотрел исходный код карточек. Возможно, классы другие, и нужно сделать что если эти не получилось прочитать, то нужно другие, и т. д.