
Очистите вкладку Google Shopping с помощью Python
- Что будет соскабливать
- Полный код
- Предпосылки
- Пояснение кода
- Получить оригинальные изображения
- Скачать оригинальные изображения
- Получить предлагаемые поисковые данные
- Использование Google Shopping Results API
- Ссылки
Что будет очищено

📌Примечание. Если некоторые результаты не извлекаются, Google изменил некоторые селекторы.
Полный код
import requests, json, re, os from parsel import Selector from serpapi import GoogleSearch # https://docs.python-requests.org/en/master/user/quickstart/#passing-parameters-in-urls params = { "q": "shoes", "hl": "en", # language "gl": "us", # country of the search, US -> USA "tbm": "shop" # google search shopping } # https://docs.python-requests.org/en/master/user/quickstart/#custom-headers headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" } html = requests.get("https://www.google.com/search", params=params, headers=headers, timeout=30) selector = Selector(html.text) def get_original_images(): all_script_tags = "".join( [ script.replace("</script>", "</script>\n") for script in selector.css("script").getall() ] ) image_urls = [] for result in selector.css(".Qlx7of .sh-dgr__grid-result"): # https://regex101.com/r/udjFUq/1 url_with_unicode = re.findall(rf"var\s?_u='(.*?)';var\s?_i='{result.attrib['data-pck']}';", all_script_tags) if url_with_unicode: url_decode = bytes(url_with_unicode[0], 'ascii').decode('unicode-escape') image_urls.append(url_decode) # download_original_images(image_urls) return image_urls def download_original_images(image_urls): for index, image_url in enumerate(image_urls, start=1): image = requests.get(image_url, headers=headers, timeout=30, stream=True) if image.status_code == 200: print(f"Downloading {index} image...") with open(f"images/image_{index}.jpeg", "wb") as file: file.write(image.content) def get_suggested_search_data(): google_shopping_data = [] for result, thumbnail in zip(selector.css(".Qlx7of .i0X6df"), get_original_images()): title = result.css(".Xjkr3b::text").get() product_link = "https://www.google.com" + result.css(".Lq5OHe::attr(href)").get() product_rating = result.css(".NzUzee .Rsc7Yb::text").get() product_reviews = result.css(".NzUzee > div::text").get() price = result.css(".a8Pemb::text").get() store = result.css(".aULzUe::text").get() store_link = "https://www.google.com" + result.css(".eaGTj div a::attr(href)").get() delivery = result.css(".vEjMR::text").get() store_rating_value = result.css(".zLPF4b .XEeQ2 .QIrs8::text").get() # https://regex101.com/r/kAr8I5/1 store_rating = re.search(r"^\S+", store_rating_value).group() if store_rating_value else store_rating_value store_reviews_value = result.css(".zLPF4b .XEeQ2 .ugFiYb::text").get() # https://regex101.com/r/axCQAX/1 store_reviews = re.search(r"^\(?(\S+)", store_reviews_value).group() if store_reviews_value else store_reviews_value store_reviews_link_value = result.css(".zLPF4b .XEeQ2 .QhE5Fb::attr(href)").get() store_reviews_link = "https://www.google.com" + store_reviews_link_value if store_reviews_link_value else store_reviews_link_value compare_prices_link_value = result.css(".Ldx8hd .iXEZD::attr(href)").get() compare_prices_link = "https://www.google.com" + compare_prices_link_value if compare_prices_link_value else compare_prices_link_value google_shopping_data.append({ "title": title, "product_link": product_link, "product_rating": product_rating, "product_reviews": product_reviews, "price": price, "store": store, "thumbnail": thumbnail, "store_link": store_link, "delivery": delivery, "store_rating": store_rating, "store_reviews": store_reviews, "store_reviews_link": store_reviews_link, "compare_prices_link": compare_prices_link, }) print(json.dumps(google_shopping_data, indent=2, ensure_ascii=False))
Предпосылки
Установите библиотеки:
pip install requests lxml parsel google-search-results
google-search-results — это пакет API SerpApi, который будет показан в конце как альтернативное решение.
Уменьшить вероятность блокировки
Убедитесь, что вы используете заголовки запроса user-agent, чтобы действовать как настоящий визит пользователя. Потому что юзер-агент по умолчанию запрашивает python-запросы, и веб-сайты понимают, что скорее всего это скрипт, который отправляет запрос. Проверь, какой у тебя юзер-агент.
Есть как уменьшить вероятность блокировки при парсинге поста в блоге, который может познакомить вас с базовыми и более продвинутыми подходами.
Код Пояснение
Импортировать библиотеки:
import requests, json, re, os from parsel import Selector from serpapi import GoogleSearch
- Селекторный парсер XML/HTML с полной поддержкой селекторов XPath и CSS.
- запросы сделать запрос на сайт.
- lxml для быстрой обработки документов XML/HTML.
- json для преобразования извлеченных данных в объект JSON.
- re для извлечения частей данных с помощью регулярного выражения.
- os для возврата значения переменной среды (ключ SerpApi API).
Создайте параметр URL и заголовки запроса:
# https://docs.python-requests.org/en/master/user/quickstart/#passing-parameters-in-urls params = { "q": "shoes", "hl": "en", # language "gl": "us", # country of the search, US -> USA "tbm": "shop" # google search shopping } # https://docs.python-requests.org/en/master/user/quickstart/#custom-headers headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" }
paramsболее удобный способ передачи параметров URL в запрос.user-agentдействовать как настоящий пользовательский запрос от браузера, передавая его в заголовки запроса. User-agent запросов по умолчанию — это python-reqeusts, чтобы веб-сайты могли понять, что это бот или скрипт, и заблокировать запрос к веб-сайту. Проверь, какой у тебя юзер-агент.
Сделать запрос, передать созданные параметры запроса и заголовки. Запрос вернул HTML для Selector:
html = requests.get("https://www.google.com/search", params=params, headers=headers, timeout=30)
selector = Selector(html.text)
timeout=30чтобы прекратить ожидание ответа через 30 секунд.Selector(), где возвращенные данные HTML будут обработаныparsel.
Получите оригинальные изображения
Чтобы найти правильное разрешение эскиза, нам нужно открыть исходный код страницы (CTRL+U) и найти теги <script>, которые содержат идентификаторы изображений и URL-адреса изображений в закодированном состоянии. Нам также нужен идентификатор изображения, чтобы извлечь правильное изображение из списка, а не какое-то случайное.
Вы можете напрямую анализировать данные из тега img и атрибута src, но вы получите URL-адрес в кодировке base64, который будет заполнителем изображения 1x1. Не особенно полезное разрешение изображения.
Прежде всего, вам нужно выбрать все теги <script>. Для их поиска можно использовать метод css()parsel. Этот метод вернет list всех совпавших <script> тегов.
all_script_tags = "".join(
[
script.replace("</script>", "</script>\n")
for script in selector.css("script").getall()
]
)
"".join()для объединения списка в строку.replace()для замены всех вхождений старой подстроки на новую, чтобы регулярное выражение выполнялось правильно.getall(), чтобы вернуть список со всеми результатами.
Далее наша задача найти теги скрипта, содержащие информацию об URL изображения и его ID. Для этого мы можем использовать цикл for и перебирать list совпадающих элементов.
Цикл for перебирает все изображения на странице (кроме рекламы или предложений магазина) и находит идентификатор текущего изображения в списке all_script_tags с помощью регулярного выражения, чтобы извлечь конкретное изображение, а не случайное, путем проверка его ID var_i:
for result in selector.css(".Qlx7of .sh-dgr__grid-result"):
# https://regex101.com/r/udjFUq/1
url_with_unicode = re.findall(rf"var\s?_u='(.*?)';var\s?_i='{result.attrib['data-pck']}';", all_script_tags)
Вот пример регулярного выражения для извлечения URL-адреса изображения и его идентификатора из тегов <script>:

Вместо \d+ нужно подставить атрибут data-pck, в котором хранится id изображения. Для этого воспользуемся форматированной строкой:
url_with_unicode = re.findall(rf"var\s?_u='(.*?)';var\s?_i='{result.attrib['data-pck']}';", all_script_tags)
Регулярное выражение вернуло URL-адрес изображения в закодированном состоянии. В этом случае вам необходимо декодировать Unicode-сущности. После проделанных операций добавляем адрес в image_urlslist:
url_decode = bytes(url_with_unicode[0], 'ascii').decode('unicode-escape') image_urls.append(url_decode)
Функция выглядит следующим образом:
def get_original_images():
all_script_tags = "".join(
[
script.replace("</script>", "</script>\n")
for script in selector.css("script").getall()
]
)
image_urls = []
for result in selector.css(".Qlx7of .sh-dgr__grid-result"):
# https://regex101.com/r/udjFUq/1
url_with_unicode = re.findall(rf"var\s?_u='(.*?)';var\s?_i='{result.attrib['data-pck']}';", all_script_tags)
if url_with_unicode:
url_decode = bytes(url_with_unicode[0], 'ascii').decode('unicode-escape')
image_urls.append(url_decode)
# download_original_images(image_urls)
return image_urls
Распечатать возвращенные данные:
[
"https://encrypted-tbn2.gstatic.com/shopping?q=tbn:ANd9GcS_YSNcYQrumsokg4AXKHXCM4kSdA1gWxmFUOZeyqRnf7nR5m8CWJ_-tCNIaNjiZzTYD3ERR0iDXenl0Q_Lswu44VHqIQPpIaxrwS0kVV08NnmLyK1lOphA&usqp=CAE",
"https://encrypted-tbn3.gstatic.com/shopping?q=tbn:ANd9GcTshSE9GfoLG_mbwZLwqx_yqnGsjR7tvuSPqmOnM8Z6uLToZ_p4DeHXa0obu5sh8QSp3vfIHuaLn5uiLIQHFVXsFb6lX0QwbSbLwS1R7nBiJLPesluLfR2T&usqp=CAE",
"https://encrypted-tbn0.gstatic.com/shopping?q=tbn:ANd9GcTRbSGCkFzgXlPrVK3EdoBg8oqGZq4mpyLreYFGsRplcXwBBD1tUMzUEU_yiFNyo8sOimNHpaVMGgxeCk3EDXjhOu879Jb3D8JzP2sv2iP0h7vJywzMXazx&usqp=CAE",
"https://encrypted-tbn3.gstatic.com/shopping?q=tbn:ANd9GcQRPOrOELZafWAURXGD2weyznXQn-RKKsX7M9l7JoAcecF-eFwyTU-IDIzorSt4CYtTQwfh3Mr3gb7xgNW15ekBvzGUS2QGuP5FgufKAhB3rMr6saJy-dgxveNR&usqp=CAE",
"https://encrypted-tbn0.gstatic.com/shopping?q=tbn:ANd9GcS_dJ1xHzSAdvHdqnSUV8WoYXPI7boeoqQuhF71iG8-F4gKCrFdevOWILnjO1ePA-wDYtiRuFO4K2pFcEJ3DNO0qhtN6OrL2RLlauy3EkHqdvKSx8wrAH7NHg&usqp=CAE",
"https://encrypted-tbn2.gstatic.com/shopping?q=tbn:ANd9GcRNPyBYHEhBzyKUlZs8nxb25rfeBmAhleNK0B1ClLbCVamO-IYlGgfZbKPYp9cDydlBvxbkaylGXmr2IakZnnsqGBo-OynpBs2yrgNIGqH_vu7lLOkkLuWI5A&usqp=CAE",
"https://encrypted-tbn3.gstatic.com/shopping?q=tbn:ANd9GcR2sF3ADV112NjwYLlS7HOWRgEYHqx8FCMjXaYwQaCcXNKwhQqkPfQTKVr0FURG4lDt0OsNKXCZI3eiKsDRrLGD_lsM9MyhUjkf6l-C5Mb_pk6bJyqypcxq&usqp=CAE",
"https://encrypted-tbn1.gstatic.com/shopping?q=tbn:ANd9GcSp8b4L3ckI6Xqv-HGv8b_a_62OOXHn4I3mC4M2DycS9CqoeIj5uClX24vL_Pwzx3ZtLbUtArVo1pqmIythL-gucrx-z6DwRsJPF4Swp2rB9JEbjeG6GokLMw&usqp=CAE",
"https://encrypted-tbn1.gstatic.com/shopping?q=tbn:ANd9GcQO3t4RziWLeRDDGzY7ZAbmUS7oH0RfNEZ_tGJLJwcijroa1zE2qnA8vG6XCaGd99lbMp3e2O2-GFCnz9SWBf7g7zSJG4DnYTyB5Ib6InMOAOfU5oebGNGx&usqp=CAE",
"https://encrypted-tbn2.gstatic.com/shopping?q=tbn:ANd9GcS9mRGTiqUkVAZHjVnpEIiVIyW9W6haxbitsHXiMgR5Ibf4wC4aqvFclwUe6VIU75Qg_Z84dEiEhCIYc1V8uOEOIZaGESfmQwrW-3-2LUbCQEhqEHlyP_x7&usqp=CAE",
... other results
]
Скачать оригинальные изображения
Теперь у нас есть список со всеми правильными URL-адресами. Мы используем тот же цикл for со встроенной функцией enumerate(). Эта функция добавляет счетчик к итерируемому объекту и возвращает его. Счетчик нужен для присвоения уникального номера каждому изображению на этапе сохранения (если нужно их сохранить).
В каждой итерации цикла вы должны перейти по текущей ссылке и загрузить изображение. Вы можете использовать with open() менеджер контекста, чтобы сохранить изображение локально.
def download_original_images(image_urls):
for index, image_url in enumerate(image_urls, start=1):
image = requests.get(image_url, headers=headers, timeout=30, stream=True)
if image.status_code == 200:
print(f"Downloading {index} image...")
with open(f"images/image_{index}.jpeg", "wb") as file:
file.write(image.content)
На гифке ниже я демонстрирую, как работает эта функция:

📌Примечание: вы можете заметить, что изображения загружаются не в том порядке, в котором они появляются на сайте. Это связано с тем, что Google отображает страницы для каждого пользователя уникальным образом в зависимости от их местоположения, истории поиска и т. д. Вы можете узнать больше, прочитав этот пост: Причины резкого изменения результатов поиска Google.
Получить рекомендуемые данные поиска
В этой функции я использую встроенную функцию zip(), которая позволяет вам перебирать две итерации одновременно, первая итерация — это селектор всех списков продуктов, вторая — это list URL-адресов эскизов, возвращаемых функцией get_original_images(). .
Вы можете заметить, что некоторые данные могут отсутствовать в некоторых карточках товаров:

Эта функция использует библиотеку Parsel, чтобы избежать использования exceptions. Если вы попытаетесь разобрать несуществующие данные, значение будет автоматически записано в None и программа не остановится. В результате вся информация о товаре добавляется в google_shopping_data список:
def get_suggested_search_data():
google_shopping_data = []
for result, thumbnail in zip(selector.css(".Qlx7of .i0X6df"), get_original_images()):
title = result.css(".Xjkr3b::text").get()
product_link = "https://www.google.com" + result.css(".Lq5OHe::attr(href)").get()
product_rating = result.css(".NzUzee .Rsc7Yb::text").get()
product_reviews = result.css(".NzUzee > div::text").get()
price = result.css(".a8Pemb::text").get()
store = result.css(".aULzUe::text").get()
store_link = "https://www.google.com" + result.css(".eaGTj div a::attr(href)").get()
delivery = result.css(".vEjMR::text").get()
store_rating_value = result.css(".zLPF4b .XEeQ2 .QIrs8::text").get()
# https://regex101.com/r/kAr8I5/1
store_rating = re.search(r"^\S+", store_rating_value).group() if store_rating_value else store_rating_value
store_reviews_value = result.css(".zLPF4b .XEeQ2 .ugFiYb::text").get()
# https://regex101.com/r/axCQAX/1
store_reviews = re.search(r"^\(?(\S+)", store_reviews_value).group() if store_reviews_value else store_reviews_value
store_reviews_link_value = result.css(".zLPF4b .XEeQ2 .QhE5Fb::attr(href)").get()
store_reviews_link = "https://www.google.com" + store_reviews_link_value if store_reviews_link_value else store_reviews_link_value
compare_prices_link_value = result.css(".Ldx8hd .iXEZD::attr(href)").get()
compare_prices_link = "https://www.google.com" + compare_prices_link_value if compare_prices_link_value else compare_prices_link_value
google_shopping_data.append({
"title": title,
"product_link": product_link,
"product_rating": product_rating,
"product_reviews": product_reviews,
"price": price,
"store": store,
"thumbnail": thumbnail,
"store_link": store_link,
"delivery": delivery,
"store_rating": store_rating,
"store_reviews": store_reviews,
"store_reviews_link": store_reviews_link,
"compare_prices_link": compare_prices_link,
})
print(json.dumps(google_shopping_data, indent=2, ensure_ascii=False))
google_shopping_dataвременныйlist, где извлеченные данные будут добавляться в конце функции.zip()для параллельного перебора нескольких итераций. Имейте в виду, чтоzipиспользуется намеренно.zip()заканчивается самым коротким итератором, аzip_longest()повторяется до длины самого длинного итератора.css()для доступа к элементам с помощью переданного селектора.::textили::attr(<attribute>)для извлечения текстовых или атрибутивных данных из узла.get()для фактического извлечения текстовых данных.search()для поиска шаблона в строке и возврата соответствующего объекта соответствия.group()для извлечения найденного элемента из объекта сопоставления.google_shopping_data.append({})вappendданные извлеченных изображений вlistв качестве словаря.
Распечатать возвращенные данные:
[
{
"title": "Jordan Boys AJ 1 Mid - Basketball Shoes Black/Dark Iris/White Size 11.0",
"product_link": "https://www.google.com/shopping/product/2446415938651229617?q=shoes&hl=en&gl=us&prds=eto:8229454466840606844_0,pid:299671923759156329,rsk:PC_2261195288052060612&sa=X&ved=0ahUKEwi5qJ-i1pH5AhU1j2oFHYH0A1EQ8wIIkhQ",
"product_rating": "5.0",
"product_reviews": "2",
"price": "$70.00",
"store": "Nike",
"thumbnail": "https://encrypted-tbn2.gstatic.com/shopping?q=tbn:ANd9GcS_YSNcYQrumsokg4AXKHXCM4kSdA1gWxmFUOZeyqRnf7nR5m8CWJ_-tCNIaNjiZzTYD3ERR0iDXenl0Q_Lswu44VHqIQPpIaxrwS0kVV08NnmLyK1lOphA&usqp=CAE",
"store_link": "https://www.google.com/url?url=https://www.nike.com/t/jordan-1-mid-little-kids-shoes-Rt7WmQ/640734-095%3Fnikemt%3Dtrue&rct=j&q=&esrc=s&sa=U&ved=0ahUKEwi5qJ-i1pH5AhU1j2oFHYH0A1EQguUECJQU&usg=AOvVaw33rVk6a7KZ7tSuibaJiP8L",
"delivery": "Delivery by Thu, Aug 11",
"store_rating": "4.6",
"store_reviews": "2.6K",
"store_reviews_link": "https://www.google.com/url?url=https://www.google.com/shopping/ratings/account/metrics%3Fq%3Dnike.com%26c%3DUS%26v%3D18%26hl%3Den&rct=j&q=&esrc=s&sa=U&ved=0ahUKEwi5qJ-i1pH5AhU1j2oFHYH0A1EQ9-wCCJsU&usg=AOvVaw3pWu7Rw-rfT2lXuzldJ4f-",
"compare_prices_link": "https://www.google.com/shopping/product/2446415938651229617/offers?q=shoes&hl=en&gl=us&prds=eto:8229454466840606844_0,pid:299671923759156329,rsk:PC_2261195288052060612&sa=X&ved=0ahUKEwi5qJ-i1pH5AhU1j2oFHYH0A1EQ3q4ECJwU"
},
... other results
]
Использование API результатов покупок Google
Основное отличие в том, что это более быстрый подход. Google Shopping Results API будет обходить блокировки со стороны поисковых систем, и вам не придется создавать парсер с нуля и поддерживать его.
Пример кода для интеграции:
from serpapi import GoogleSearch
import requests, lxml, os, json
params = {
"q": "shoes", # search query
"tbm": "shop", # shop results
"location": "Dallas", # location from where search comes from
"hl": "en", # language of the search
"gl": "us", # country of the search
# https://docs.python.org/3/library/os.html#os.getenv
"api_key": os.getenv("API_KEY"), # your serpapi api
}
# https://docs.python-requests.org/en/master/user/quickstart/#custom-headers
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
}
search = GoogleSearch(params) # where data extraction happens on the SerpApi backend
results = search.get_dict() # JSON -> Python dict
def download_google_shopping_images():
for index, result in enumerate(results["shopping_results"], start=1):
image = requests.get(result['thumbnail'], headers=headers, timeout=30, stream=True)
if image.status_code == 200:
with open(f"images/image_{index}.jpeg", 'wb') as file:
file.write(image.content)
def serpapi_get_google_shopping_data():
google_shopping_data = results["shopping_results"]
# download_google_shopping_images()
print(json.dumps(google_shopping_data, indent=2, ensure_ascii=False))
Выходы:
[
{
"position": 1,
"title": "Jordan (PS) Jordan 1 Mid Black/Dark Iris-White",
"link": "https://www.google.com/url?url=https://www.nike.com/t/jordan-1-mid-little-kids-shoes-Rt7WmQ/640734-095%3Fnikemt%3Dtrue&rct=j&q=&esrc=s&sa=U&ved=0ahUKEwiS3MaljJX5AhURILkGHTBYCSUQguUECIQW&usg=AOvVaw1GOVGa9RfptVnLwtJxW13f",
"product_link": "https://www.google.com/shopping/product/2446415938651229617",
"product_id": "2446415938651229617",
"serpapi_product_api": "https://serpapi.com/search.json?device=desktop&engine=google_product&gl=us&google_domain=google.com&hl=en&location=Dallas&product_id=2446415938651229617",
"source": "Nike",
"price": "$70.00",
"extracted_price": 70.0,
"rating": 5.0,
"reviews": 2,
"thumbnail": "https://encrypted-tbn2.gstatic.com/shopping?q=tbn:ANd9GcS_YSNcYQrumsokg4AXKHXCM4kSdA1gWxmFUOZeyqRnf7nR5m8CWJ_-tCNIaNjiZzTYD3ERR0iDXenl0Q_Lswu44VHqIQPpIaxrwS0kVV08NnmLyK1lOphA&usqp=CAE",
"delivery": "Delivery by Sun, Aug 14"
},
... other results
]
Ссылки
Первоначально опубликовано на SerpApi: https://serpapi.com/blog/scrape-google-finance-markets-in-python/
Присоединяйтесь к нам в Твиттере | "YouTube"
Добавьте Запрос функции💫 или Ошибку🐞