Очистите вкладку 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"
Добавьте Запрос функции💫 или Ошибку🐞