Get Product Data from Bing Shopping with Python and SerpApi
Tags: webscraping, python, tutorial, programming
Published: 2023-04-25 10:25:35

- <a href="#what_will_be_scraped">What will be scraped</a>
- <a href="#fullcode">Full Code</a>
- <a href="#preparation">Preparation</a>
- <a href="#code_explanation">Code Explanation</a>
    - <a href="#top_level_code_environment">Top-level code environment</a>
    - <a href="#get_inline_shopping_results">Get inline shopping results</a>
    - <a href="#get_shopping_results">Get shopping results</a>
- <a href="#output">Output</a>
- <a href="#links">Links</a>


<h2 id="what_will_be_scraped">What will be scraped</h2>

![wwbs-bing-shopping](https://user-images.githubusercontent.com/81998012/232490724-467430b8-141a-403d-a093-1a1925317adf.png)

📌Note: Inline shopping results may not always be present.


<h2 id="fullcode">Full Code</h2>

If you don't need an explanation, have a look at [the full code example in the online IDE](https://replit.com/@chukhraiartur/blog-bing-shopping#main.py).

```python
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()
```


<h2 id="preparation">Preparation</h2>

**Install libraries**

```lang-none
pip install requests selectolax
```

**Reduce the chance of being blocked**

Make sure you're using [request headers](https://docs.python-requests.org/en/master/user/quickstart/#custom-headers) [`user-agent`](https://developer.mozilla.org/en-US/docs/Glossary/User_agent) to act as a "real" user visit. Because default `requests` `user-agent` is [`python-requests`](https://github.com/psf/requests/blob/589c4547338b592b1fb77c65663d8aa6fbb7e38b/requests/utils.py#L808-L814) and websites understand that it's most likely a script that sends a request. [Check what's your `user-agent`](https://www.whatismybrowser.com/detect/what-is-my-user-agent/).

There's a [how to reduce the chance of being blocked while web scraping blog post](https://serpapi.com/blog/how-to-reduce-chance-of-being-blocked-while-web/) that can get you familiar with basic and more advanced approaches.


<h2 id="code_explanation">Code Explanation</h2>

Import libraries:

```python
from selectolax.lexbor import LexborHTMLParser
import requests, json, re
```

| Library | Purpose |
|---------|---------|
| [`LexborHTMLParser`](https://selectolax.readthedocs.io/en/latest/lexbor.html#lexborhtmlparser) | a fast HTML5 parser with CSS selectors using [Lexbor](https://github.com/rushter/selectolax#available-backends) engine. |
| [`requests`](https://requests.readthedocs.io/en/latest/user/quickstart/)| to make a request to the website. |
| [`json`](https://docs.python.org/3/library/json.html)| to convert extracted data to a JSON object. |
| [`re`](https://docs.python.org/3/library/re.html)| 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.


<h3 id="top_level_code_environment">Top-level code environment</h3>

The request headers are generated:

```python
# 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:

```python
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):

```python
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:

```python
bing_shopping_results = {
    'inline_shopping_results': [],
    'shopping_results': []
}
```

Requests are sent in a loop for each page:

```python
while True:
    # pagination
```

Make a request, pass `url` and `headers`. The data extraction itself is done with [`selectolax`](https://pypi.org/project/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](https://dev.to/dmitryzub/8-most-popular-python-html-web-scraping-packages-with-benchmarks-5239):

```python
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.

```python
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.

```python
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:

```python
print(json.dumps(bing_shopping_results, indent=2, ensure_ascii=False))
```

This code uses boilerplate [`__name__ == "__main__"`](https://docs.python.org/3/library/__main__.html#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:

```python
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__'](https://www.youtube.com/watch?v=sugvnHA7ElY&t=1s) for more details.


<h3 id="get_inline_shopping_results">Get inline shopping results</h3>

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:

```python
data = []
```

To retrieve all products from a page, you need to use the [`css()` method](https://selectolax.readthedocs.io/en/latest/lexbor.html#selectolax.lexbor.LexborHTMLParser.css) 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](https://docs.python.org/3/library/functions.html#enumerate). This function adds a counter to the iterable and returns it. The counter is needed to assign a position to each product:

```python
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`](https://selectolax.readthedocs.io/en/latest/lexbor.html#selectolax.lexbor.LexborHTMLParser.css_first), after this we extract the [text value](https://selectolax.readthedocs.io/en/latest/lexbor.html#selectolax.lexbor.LexborHTMLParser.text) or [attribute value](https://selectolax.readthedocs.io/en/latest/parser.html#selectolax.parser.Node.attributes):

```python
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:

```python
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](https://docs.python.org/3/library/re.html) to extract them:

```python
# 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:

```python
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:

```python
return data
```

The function looks like this:

```python
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
```


<h3 id="get_shopping_results">Get shopping results</h3>

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:

```python
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:

```python
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:

```python
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:

```python
return data
```

The function looks like this:

```python
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
```


<h2 id="output">Output</h2>

```json
{
  "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
  ]
}
```


<h2 id="links">Links</h2>

- [Code in the online IDE](https://replit.com/@chukhraiartur/blog-bing-shopping#main.py)
- [Requests](https://requests.readthedocs.io/en/latest/)
- [Selectolax Lexbor module](https://selectolax.readthedocs.io/en/latest/lexbor.html)

<p style="text-align: center;">Join us on <a href="https://twitter.com/serp_api">Twitter</a> | <a href="https://www.youtube.com/channel/UCUgIHlYBOD3yA3yDIRhg_mg">YouTube</a></p>

<p style="text-align: center;">Add a  <a href="https://github.com/serpapi/public-roadmap/issues">Feature Request</a>💫 or a <a href="https://github.com/serpapi/public-roadmap/issues">Bug</a>🐞</p>