Get Product Data from Bing Shopping with Python and SerpApi
This blog post is a step-by-step tutorial for scraping Bing Shopping using SerpApi and Python.
What will be scraped
πNote: Inline shopping results may not always be present.
Full Code
If you don't need an explanation, have a look at the full code example in the online IDE.
from selectolax.lexbor import LexborHTMLParser
import requests, json, re
def get_inline_shopping_results(parser: LexborHTMLParser, product_counter: int) -> list:
data = []
for position, product in enumerate(parser.root.css('.br-gOffCard'), start=product_counter):
raw_title = product.css_first('.br-offTtl span')
raw_shipping = product.css_first('.br-offDec')
raw_rating = product.css_first('.tags > span')
title = raw_title.attributes.get('title', '') if raw_title else product.css_first('.br-offTtl').text().strip()
product_link = product.css_first('.br-offLink').attributes.get('href', '')
brand = product.css_first('.br-offSecLbl').text().strip() if product.css_first('.br-offSecLbl') else None
seller = product.css_first('.br-offSlr').text().strip()
price = product.css_first('.br-price').text().strip()
# https://regex101.com/r/lap8lr/1
extracted_price = float(re.search(r'[\d|,|.]+', price).group().replace(',', ''))
old_price = product.css_first('.br-standardPriceDemoted').text().strip() if product.css_first('.br-standardPriceDemoted') else None
shipping_text = raw_shipping.text().strip() if raw_shipping else ''
shipping = shipping_text if 'shipping' in shipping_text else None
rating_text = raw_rating.attributes.get('aria-label', '') if raw_rating else None
# https://regex101.com/r/D2YTRI/1
rating = float(re.search(r'[\d.\d|\d]+', rating_text).group(0)) if rating_text else None
reviews_text = raw_rating.text().strip() if raw_rating else None
reviews = reviews_text.replace('(', '').replace(')', '') if reviews_text else None
thumbnail = product.css_first('.cico img').attributes.get('src', None) if product.css_first('.cico img') else None
data.append({
'position': position,
'title': title,
'product_link': product_link,
'brand': brand,
'seller': seller,
'price': price,
'extracted_price': extracted_price,
'old_price': old_price,
'shipping': shipping,
'rating': rating,
'reviews': reviews,
'thumbnail': thumbnail
})
return data
def get_shopping_results(parser: LexborHTMLParser, product_counter: int) -> list:
data = []
for position, product in enumerate(parser.root.css('.br-fullCard'), start=product_counter):
raw_title = product.css_first('.br-title span')
title = raw_title.attributes.get('title', '') if raw_title else product.css_first('.br-title').text().strip()
product_link = f"https://www.bing.com{product.css_first('.br-titlelink').attributes.get('href', '')}"
seller = product.css_first('.br-seller').text().strip()
price = product.css_first('.pd-price').text().strip()
# https://regex101.com/r/lap8lr/1
extracted_price = float(re.search(r'[\d|,|.]+', price).group().replace(',', ''))
sale = True if product.css_first('.br-saletag') else False
old_price = product.css_first('.br-pdOfferPrice').text().strip() if product.css_first('.br-pdOfferPrice') else None
shipping = product.css_first('.br-decoSlot').text().strip() if product.css_first('.br-decoSlot') else None
thumbnail = product.css_first('.br-pdMainImg img').attributes.get('src', None) if product.css_first('.br-pdMainImg img') else None
data.append({
'position': position,
'title': title,
'product_link': product_link,
'seller': seller,
'price': price,
'extracted_price': extracted_price,
'sale': sale,
'old_price': old_price,
'shipping': shipping,
'thumbnail': thumbnail
})
return data
def main():
# 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/111.0.0.0 Safari/537.36'
}
url = 'https://www.bing.com/shop?q=ps4 controller'
inline_shopping_results_counter: int = 1
shopping_results_counter: int = 1
bing_shopping_results = {
'inline_shopping_results': [],
'shopping_results': []
}
while True:
html = requests.get(url, headers=headers)
parser = LexborHTMLParser(html.text)
inline_shopping_page_results = get_inline_shopping_results(parser, inline_shopping_results_counter)
bing_shopping_results['inline_shopping_results'].extend(inline_shopping_page_results)
shopping_page_results = get_shopping_results(parser, shopping_results_counter)
bing_shopping_results['shopping_results'].extend(shopping_page_results)
next_page_button = parser.css_first('.sb_pagN_bp')
if next_page_button:
url = f"https://www.bing.com{next_page_button.attributes.get('href', '')}"
inline_shopping_results_counter += len(inline_shopping_page_results)
shopping_results_counter += len(shopping_page_results)
else:
break
print(json.dumps(bing_shopping_results, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
Preparation
Install libraries
pip install requests selectolax
Reduce the chance of being blocked
Make sure you're using request headers user-agent
to act as a "real" user visit. Because default requests
user-agent
is python-requests
and websites understand that it's most likely a script that sends a request. Check what's your user-agent
.
There's a how to reduce the chance of being blocked while web scraping blog post that can get you familiar with basic and more advanced approaches.
Code Explanation
Import libraries:
from selectolax.lexbor import LexborHTMLParser
import requests, json, re
Library | Purpose |
LexborHTMLParser | a fast HTML5 parser with CSS selectors using Lexbor engine. |
requests | to make a request to the website. |
json | to convert extracted data to a JSON object. |
re | to extract parts of the data via regular expression. |
The next part of the code is divided into functions. Each function is described in the corresponding heading below.
Top-level code environment
The request headers are generated:
# 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/111.0.0.0 Safari/537.36'
}
The url
variable contains a link to the Bing Shopping page:
url = 'https://www.bing.com/shop?q=ps4 controller'
Variables are created to determine the position of the current product, taking into account pagination. For example, if on the first page the position of the last product is 36
, then on the second page the position of the first product will be 37
(not 1):
inline_shopping_results_counter: int = 1
shopping_results_counter: int = 1
The bing_shopping_results
dictionary is formed, where data on the corresponding keys will subsequently be added:
bing_shopping_results = {
'inline_shopping_results': [],
'shopping_results': []
}
Requests are sent in a loop for each page:
while True:
# pagination
Make a request, pass url
and headers
. The data extraction itself is done with selectolax
because it has Lexbor
parser which is incredibly fast, like 186% faster compared to bs4
with lxml
backend when parsing data with 3000 iterations 5 times:
html = requests.get(url, headers=headers)
parser = LexborHTMLParser(html.text)
By calling the appropriate functions, all the necessary data is retrieved and written to the bing_shopping_results
dictionary. These functions are detailed in the sections below.
inline_shopping_page_results = get_inline_shopping_results(parser, inline_shopping_results_counter)
bing_shopping_results['inline_shopping_results'].extend(inline_shopping_page_results)
shopping_page_results = get_shopping_results(parser, shopping_results_counter)
bing_shopping_results['shopping_results'].extend(shopping_page_results)
At the end of the iteration, it checks whether the "Next" button is present. If such a button is present, then the url and counters are updated. Otherwise, the loop is stopped.
next_page_button = parser.css_first('.sb_pagN_bp')
if next_page_button:
url = f"https://www.bing.com{next_page_button.attributes.get('href', '')}"
inline_shopping_results_counter += len(inline_shopping_page_results)
shopping_results_counter += len(shopping_page_results)
else:
break
After receiving the data from all pages, they are output in JSON format:
print(json.dumps(bing_shopping_results, indent=2, ensure_ascii=False))
This code uses boilerplate __name__ == "__main__"
construct that protects users from accidentally invoking the script when they didn't intend to. This indicates that the code is a runnable script:
def main():
# 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/111.0.0.0 Safari/537.36'
}
url = 'https://www.bing.com/shop?q=ps4 controller'
inline_shopping_results_counter: int = 1
shopping_results_counter: int = 1
bing_shopping_results = {
'inline_shopping_results': [],
'shopping_results': []
}
while True:
html = requests.get(url, headers=headers)
parser = LexborHTMLParser(html.text)
inline_shopping_page_results = get_inline_shopping_results(parser, inline_shopping_results_counter)
bing_shopping_results['inline_shopping_results'].extend(inline_shopping_page_results)
shopping_page_results = get_shopping_results(parser, shopping_results_counter)
bing_shopping_results['shopping_results'].extend(shopping_page_results)
next_page_button = parser.css_first('.sb_pagN_bp')
if next_page_button:
url = f"https://www.bing.com{next_page_button.attributes.get('href', '')}"
inline_shopping_results_counter += len(inline_shopping_page_results)
shopping_results_counter += len(shopping_page_results)
else:
break
print(json.dumps(bing_shopping_results, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
This check will only be performed if the user has run this file. If the user imports this file into another, then the check will not work.
You can watch the video Python Tutorial: if name == 'main' for more details.
Get inline shopping results
The function takes a parser
and a product_counter
. Returns a list with the retrieved data.
The data
list is declared to which the extracted data will be added:
data = []
To retrieve all products from a page, you need to use the css()
method and pass the .br-gOffCard
selector there.
After that, you need to iterate over the resulting list of products using the for loop with the built-in enumerate()
function. This function adds a counter to the iterable and returns it. The counter is needed to assign a position to each product:
for position, product in enumerate(parser.root.css('.br-gOffCard'), start=product_counter):
# data extraction
Retrieving the required data is done as follows: we find the required selector for current product using the css_first() method
, after this we extract the text value or attribute value:
product_link = product.css_first('.br-offLink').attributes.get('href', '')
seller = product.css_first('.br-offSlr').text().strip()
price = product.css_first('.br-price').text().strip()
There are data that may not be present in some products. In this case, checks are added:
raw_title = product.css_first('.br-offTtl span')
raw_shipping = product.css_first('.br-offDec')
raw_rating = product.css_first('.tags > span')
title = raw_title.attributes.get('title', '') if raw_title else product.css_first('.br-offTtl').text().strip()
brand = product.css_first('.br-offSecLbl').text().strip() if product.css_first('.br-offSecLbl') else None
old_price = product.css_first('.br-standardPriceDemoted').text().strip() if product.css_first('.br-standardPriceDemoted') else None
shipping_text = raw_shipping.text().strip() if raw_shipping else ''
shipping = shipping_text if 'shipping' in shipping_text else None
rating_text = raw_rating.attributes.get('aria-label', '') if raw_rating else None
reviews_text = raw_rating.text().strip() if raw_rating else None
reviews = reviews_text.replace('(', '').replace(')', '') if reviews_text else None
thumbnail = product.css_first('.cico img').attributes.get('src', None) if product.css_first('.cico img') else None
If you need to get a specific value, then you can use regular expression to extract them:
# https://regex101.com/r/lap8lr/1
extracted_price = float(re.search(r'[\d|,|.]+', price).group().replace(',', ''))
# https://regex101.com/r/D2YTRI/1
rating = float(re.search(r'[\d.\d|\d]+', rating_text).group(0)) if rating_text else None
At the end of each iteration, product data is added to the data
list:
data.append({
'position': position,
'title': title,
'product_link': product_link,
'brand': brand,
'seller': seller,
'price': price,
'extracted_price': extracted_price,
'old_price': old_price,
'shipping': shipping,
'rating': rating,
'reviews': reviews,
'thumbnail': thumbnail
})
After all the operations are done, return the data
list:
return data
The function looks like this:
def get_inline_shopping_results(parser: LexborHTMLParser, product_counter: int) -> list:
data = []
for position, product in enumerate(parser.root.css('.br-gOffCard'), start=product_counter):
raw_title = product.css_first('.br-offTtl span')
raw_shipping = product.css_first('.br-offDec')
raw_rating = product.css_first('.tags > span')
title = raw_title.attributes.get('title', '') if raw_title else product.css_first('.br-offTtl').text().strip()
product_link = product.css_first('.br-offLink').attributes.get('href', '')
brand = product.css_first('.br-offSecLbl').text().strip() if product.css_first('.br-offSecLbl') else None
seller = product.css_first('.br-offSlr').text().strip()
price = product.css_first('.br-price').text().strip()
# https://regex101.com/r/lap8lr/1
extracted_price = float(re.search(r'[\d|,|.]+', price).group().replace(',', ''))
old_price = product.css_first('.br-standardPriceDemoted').text().strip() if product.css_first('.br-standardPriceDemoted') else None
shipping_text = raw_shipping.text().strip() if raw_shipping else ''
shipping = shipping_text if 'shipping' in shipping_text else None
rating_text = raw_rating.attributes.get('aria-label', '') if raw_rating else None
# https://regex101.com/r/D2YTRI/1
rating = float(re.search(r'[\d.\d|\d]+', rating_text).group(0)) if rating_text else None
reviews_text = raw_rating.text().strip() if raw_rating else None
reviews = reviews_text.replace('(', '').replace(')', '') if reviews_text else None
thumbnail = product.css_first('.cico img').attributes.get('src', None) if product.css_first('.cico img') else None
data.append({
'position': position,
'title': title,
'product_link': product_link,
'brand': brand,
'seller': seller,
'price': price,
'extracted_price': extracted_price,
'old_price': old_price,
'shipping': shipping,
'rating': rating,
'reviews': reviews,
'thumbnail': thumbnail
})
return data
Get shopping results
The function takes a parser
and a product_counter
. Returns a list with the retrieved data.
The data
list is declared to which the extracted data will be added:
data = []
In the previous function, the data extraction process was described in detail. In this function, the process is very similar except for different data and correspondingly different selectors:
for position, product in enumerate(parser.root.css('.br-fullCard'), start=product_counter):
raw_title = product.css_first('.br-title span')
title = raw_title.attributes.get('title', '') if raw_title else product.css_first('.br-title').text().strip()
product_link = f"https://www.bing.com{product.css_first('.br-titlelink').attributes.get('href', '')}"
seller = product.css_first('.br-seller').text().strip()
price = product.css_first('.pd-price').text().strip()
# https://regex101.com/r/lap8lr/1
extracted_price = float(re.search(r'[\d|,|.]+', price).group().replace(',', ''))
sale = True if product.css_first('.br-saletag') else False
old_price = product.css_first('.br-pdOfferPrice').text().strip() if product.css_first('.br-pdOfferPrice') else None
shipping = product.css_first('.br-decoSlot').text().strip() if product.css_first('.br-decoSlot') else None
thumbnail = product.css_first('.br-pdMainImg img').attributes.get('src', None) if product.css_first('.br-pdMainImg img') else None
At the end of each iteration, product data is added to the data
list:
data.append({
'position': position,
'title': title,
'product_link': product_link,
'seller': seller,
'price': price,
'extracted_price': extracted_price,
'sale': sale,
'old_price': old_price,
'shipping': shipping,
'thumbnail': thumbnail
})
After all the operations are done, return the data
list:
return data
The function looks like this:
def get_shopping_results(parser: LexborHTMLParser, product_counter: int) -> list:
data = []
for position, product in enumerate(parser.root.css('.br-fullCard'), start=product_counter):
raw_title = product.css_first('.br-title span')
title = raw_title.attributes.get('title', '') if raw_title else product.css_first('.br-title').text().strip()
product_link = f"https://www.bing.com{product.css_first('.br-titlelink').attributes.get('href', '')}"
seller = product.css_first('.br-seller').text().strip()
price = product.css_first('.pd-price').text().strip()
# https://regex101.com/r/lap8lr/1
extracted_price = float(re.search(r'[\d|,|.]+', price).group().replace(',', ''))
sale = True if product.css_first('.br-saletag') else False
old_price = product.css_first('.br-pdOfferPrice').text().strip() if product.css_first('.br-pdOfferPrice') else None
shipping = product.css_first('.br-decoSlot').text().strip() if product.css_first('.br-decoSlot') else None
thumbnail = product.css_first('.br-pdMainImg img').attributes.get('src', None) if product.css_first('.br-pdMainImg img') else None
data.append({
'position': position,
'title': title,
'product_link': product_link,
'seller': seller,
'price': price,
'extracted_price': extracted_price,
'sale': sale,
'old_price': old_price,
'shipping': shipping,
'thumbnail': thumbnail
})
return data
Output
{
"inline_shopping_results": [
{
"position": 1,
"title": "Sony Playstation 4 Dual Shock 4 Controller",
"product_link": "https://www.bing.com/aclick?ld=e8y8UnkNEA5yLnmFwFXVJsIDVUCUxGDjCRfBB7hezInXrQF_5sOcYbnlwQ7FroV_Zn5FefQcj7dQqTSlvA3lj2I21y0MviXMAVnyW-3FkoUoi16_LPCXsLblfhhQ2D_DBXPc7yCF56HaNeXUDrxLymGBGLDEWL241igNH5h1ZNrEBK3Hy1&u=aHR0cHMlM2ElMmYlMmZ3d3cuYW1hem9uLmNvbSUyZlNvbnktUGxheVN0YXRpb24tRHVhbHNob2NrLVdpcmVsZXNzLUNvbnRyb2xsZXItQmxhY2slMmZkcCUyZkIwMEQ4Mlo0WU8lMmZyZWYlM2Rhc2NfZGZfQjAwRDgyWjRZTyUzZnRhZyUzZGJpbmdzaG9wcGluZ2EtMjAlMjZsaW5rQ29kZSUzZGRmMCUyNmh2YWRpZCUzZDc5OTg5NTk3MTQ0NTE1JTI2aHZuZXR3JTNkbyUyNmh2cW10JTNkZSUyNmh2Ym10JTNkYmUlMjZodmRldiUzZGMlMjZodmxvY2ludCUzZCUyNmh2bG9jcGh5JTNkJTI2aHZ0YXJnaWQlM2RwbGEtNDU4MzU4OTExODI4NTM1MCUyNnBzYyUzZDE&rlid=044e0ea8d1d91e49a33a6a95b021d2b3",
"brand": "580+ viewed",
"seller": "Amazon.com",
"price": "$56.00",
"extracted_price": 56.0,
"old_price": null,
"shipping": null,
"rating": 4.5,
"reviews": null,
"thumbnail": "https://th.bing.com/th?id=OP.javWvhvskVjF3A474C474&w=140&h=140&pid=21.1"
},
{
"position": 2,
"title": "Wireless Controller For PS4/Slim/Pro, Berry Blue",
"product_link": "https://www.bing.com/aclick?ld=e8K5Qjv1rx7b91q1TGu2kIHDVUCUzljX6PIJ1_Ag71Iy5QZiGWnbe49xj4OnlDYYwXIZtyHeZHN66fGgfhttXqkZjtRPPAG3TZF7rGH0z6XerddgQBQx_dfYoJlYMoPPnC1ujLoNsFoUL6VTMyu4Ln14u3EoPqCJV8wSvMZ7FHfLWcEayH&u=aHR0cHMlM2ElMmYlMmZjbGlja3NlcnZlLmRhcnRzZWFyY2gubmV0JTJmbGluayUyZmNsaWNrJTNmJTI2JTI2ZHNfZV9hZGlkJTNkNzUzMTY2OTg4MTk5MTglMjZkc19lX3RhcmdldF9pZCUzZHBsYS00NTc4OTE2MjM5MDc0Njc5JTI2ZHNfZV9wcm9kdWN0X2dyb3VwX2lkJTNkNDU3ODkxNjIzOTA3NDY3OSUyNmRzX2VfcHJvZHVjdF9pZCUzZDEzOTY2MDA3OV8xMDAwMTEzNTk4OCUyNmRzX2VfcHJvZHVjdF9jb3VudHJ5JTNkVVMlMjZkc19lX3Byb2R1Y3RfbGFuZ3VhZ2UlM2RFTiUyNmRzX2VfcHJvZHVjdF9jaGFubmVsJTNkT25saW5lJTI2ZHNfdXJsX3YlM2QyJTI2ZHNfZGVzdF91cmwlM2RodHRwcyUzYSUyZiUyZnd3dy53YWxtYXJ0LmNvbSUyZmlwJTJmV2lyZWxlc3MtQ29udHJvbGxlci1mb3ItUFM0LVNsaW0tUHJvLUJlcnJ5LUJsdWUlMmYxMzk2NjAwNzklM2Z3bWxzcGFydG5lciUzZHdscGElMjZzZWxlY3RlZFNlbGxlcklkJTNkMTAxMTE2NjI2JTI2YWRpZCUzZDIyMjIyMjIyMjIyNTc2ODA1MzgwJTI2d21sc3BhcnRuZXIlM2R3bXRsYWJzJTI2d2wwJTNkZSUyNndsMSUzZG8lMjZ3bDIlM2RjJTI2d2wzJTNkNzUzMTY2OTg4MTk5MTglMjZ3bDQlM2RwbGEtNDU3ODkxNjIzOTA3NDY3OSUyNndsNSUzZCUyNndsNiUzZCUyNndsNyUzZCUyNndsMTAlM2RXYWxtYXJ0JTI2d2wxMSUzZE9ubGluZSUyNndsMTIlM2QxMzk2NjAwNzlfMTAwMDExMzU5ODglMjZ3bDE0JTNkcHM0JTI1MjBjb250cm9sbGVyJTI2dmVoJTNkc2VtJTI2Z2NsaWQlM2Q1NjkxZjc3M2I3ODQxMTQ2ZTVhYjkzNGVkNjdjNTQ0OSUyNmdjbHNyYyUzZDNwLmRzJTI2bXNjbGtpZCUzZDU2OTFmNzczYjc4NDExNDZlNWFiOTM0ZWQ2N2M1NDQ5&rlid=5691f773b7841146e5ab934ed67c5449",
"brand": "Brand: SPBPQY",
"seller": "Walmart",
"price": "$19.99",
"extracted_price": 19.99,
"old_price": null,
"shipping": "Free shipping",
"rating": null,
"reviews": null,
"thumbnail": "https://th.bing.com/th?id=OP.AjeAqUx49FCBaQ474C474&w=140&h=140&pid=21.1"
},
{
"position": 3,
"title": "YCCTEAM Wireless Ps4 Controller ,Wireless Game Controller Compatible With Playstation 4/Slim/Pro, Built-In 1000Mah Battery With Turbo/Dual Vibration",
"product_link": "https://www.bing.com/aclick?ld=e8XOuCV1B-F1vb3cgxnxYFWjVUCUxeNZ_G0jSncEe8IzWF8UzLDZihTUvXFtS_U7DFDfJNV_L4aBCyLLwDxIW3jnYGUaW0Q0C4MhhYByxkPdaXmHEexgmFgmfKA1EITFmoVR4_Yla_FXAV-Nmx6yJiQmPLtRwNLSr6eWXTZE07O-ZjlAPX&u=aHR0cHMlM2ElMmYlMmZ3d3cuYW1hem9uLmNvbSUyZkNvbnRyb2xsZXItQ2hhcmdpbmctWUNDVEVBTS1JbmRpY2F0b3ItSm95c3RpY2tzJTJmZHAlMmZCMDdXN0gyMVZWJTJmcmVmJTNkYXNjX2RmX0IwN1c3SDIxVlYlM2Z0YWclM2RiaW5nc2hvcHBpbmdhLTIwJTI2bGlua0NvZGUlM2RkZjAlMjZodmFkaWQlM2Q3OTg1MjEyNDE1MTUzNSUyNmh2bmV0dyUzZG8lMjZodnFtdCUzZGUlMjZodmJtdCUzZGJlJTI2aHZkZXYlM2RjJTI2aHZsb2NpbnQlM2QlMjZodmxvY3BoeSUzZCUyNmh2dGFyZ2lkJTNkcGxhLTQ1ODM0NTE2NjkzOTg1NzklMjZwc2MlM2Qx&rlid=15ad8be3c1af10f584ffefdea057bbd8",
"brand": null,
"seller": "Amazon.com",
"price": "$19.99",
"extracted_price": 19.99,
"old_price": null,
"shipping": null,
"rating": null,
"reviews": null,
"thumbnail": "https://th.bing.com/th?id=OP.NsyWoG%2bGBv4G3A474C474&w=140&h=140&pid=21.1"
},
... other inline shopping results
],
"shopping_results": [
{
"position": 1,
"title": "Sony PS4 Dualshock 4 Wireless Controller - Midnight Blue",
"product_link": "https://www.bing.com/shop/productpage?q=ps4+controller&filters=scenario%3a%2217%22+gType%3a%223%22+gId%3a%22IKQSlWYtjMJCH45YKbCW1INZT5%22+gIdHash%3a%221034604334%22+gGlobalOfferIds%3a%2297596786905%22+AucContextGuid%3a%220%22+GroupEntityId%3a%22IKQSlWYtjMJCH45YKbCW1INZT5%22+NonSponsoredOffer%3a%22True%22&productpage=true&FORM=SHPPDP&browse=true",
"seller": "Walmart",
"price": "$64.00",
"extracted_price": 64.0,
"sale": false,
"old_price": null,
"shipping": "Free shipping",
"thumbnail": "https://th.bing.com/th?id=OP.5KY3rYxghNq1ng474C474&w=272&h=272&o=5&pid=21.1"
},
{
"position": 2,
"title": "Sony Dual Shock 4 Bluetooth Controller For PS4 - Black",
"product_link": "https://www.bing.com/shop/productpage?q=ps4+controller&filters=scenario%3a%2217%22+gType%3a%223%22+gId%3a%22IjzPcGXIjh5kEfHzF4DdKkdm7O%22+gIdHash%3a%22503285226%22+gGlobalOfferIds%3a%2213378540660%22+AucContextGuid%3a%220%22+GroupEntityId%3a%22IjzPcGXIjh5kEfHzF4DdKkdm7O%22+NonSponsoredOffer%3a%22True%22&productpage=true&FORM=SHPPDP&browse=true",
"seller": "Tech Import World",
"price": "$51.92",
"extracted_price": 51.92,
"sale": false,
"old_price": null,
"shipping": null,
"thumbnail": "https://th.bing.com/th?id=OP.2zrQISY7yRggYA474C474&w=272&h=272&o=5&pid=21.1"
},
{
"position": 3,
"title": "Sony PS4 Dualshock 4 Wireless Controller - Green Camouflage",
"product_link": "https://www.bing.com/shop/productpage?q=ps4+controller&filters=scenario%3a%2217%22+gType%3a%223%22+gId%3a%22IsUwpe7StYMy9zWBEooypnccb6%22+gIdHash%3a%221451490157%22+gGlobalOfferIds%3a%2285554586355%22+AucContextGuid%3a%220%22+GroupEntityId%3a%22IsUwpe7StYMy9zWBEooypnccb6%22+NonSponsoredOffer%3a%22True%22&productpage=true&FORM=SHPPDP&browse=true",
"seller": "Walmart",
"price": "$64.00",
"extracted_price": 64.0,
"sale": false,
"old_price": null,
"shipping": "Free shipping",
"thumbnail": "https://th.bing.com/th?id=OP.bmIHvaU977kYvw474C474&w=272&h=272&o=5&pid=21.1"
},
... other shopping results
]
}
Links
Add a Feature Requestπ« or a Bugπ