Web-scraping with Python to extract microdata for each page from a sitemap.xml

Viewed 262

I'm trying to extract name, brand, prices, stock microdata from pages extracted from sitemap.xml But I'm blocked with the following step, thank you for helping me as I'm a newbie I can't understand the blocking element

  • Scrape the sitemap.xml to have list of urls : OK
  • Extract the metadata : OK
  • Extract the product schema : OK
  • Extract the products not OK
  • Crawl the site and store the products not OK

  • Scrape the sitemap.xml to have list of urls : OK
import pandas as pd
import requests
import extruct
from w3lib.html import get_base_url
import urllib.request
from urllib.parse import urlparse
from bs4 import BeautifulSoup
import advertools as adv
proximus_sitemap = adv.sitemap_to_df('https://www.proximus.be/iportal/sitemap.xml')
proximus_sitemap = proximus_sitemap[proximus_sitemap['loc'].str.contains('boutique')]
proximus_sitemap = proximus_sitemap[proximus_sitemap['loc'].str.contains('/fr/')]
  • Extract the metadata : OK
def extract_metadata(url):
 
    r = requests.get(url)
    base_url = get_base_url(r.text, r.url)
    metadata = extruct.extract(r.text, 
                               base_url=base_url,
                               uniform=True,
                               syntaxes=['json-ld',
                                         'microdata',
                                         'opengraph'])
    return metadata
metadata = extract_metadata('https://www.proximus.be/fr/id_cr_apple-iphone-13-128gb-blue/particuliers/equipement/boutique/apple-iphone-13-128gb-blue.html')
metadata
  • Extract the product schema : OK
def get_dictionary_by_key_value(dictionary, target_key, target_value):
    for key in dictionary:
        if len(dictionary[key]) > 0:
            for item in dictionary[key]:
                if item[target_key] == target_value:
                    return item
Product = get_dictionary_by_key_value(metadata, "@type", "Product")
Product
  • Extract the products not OK => errormessage = errorkey 'offers'
def get_products(metadata):
 
    
    Product = get_dictionary_by_key_value(metadata, "@type", "Product")

    if Product:
    
        products = []

        for offer in Product['offers']['offers']:
            product = {
                'product_name': Product.get('name', ''),
                'brand': offer.get('description', ''),
                'availability': offer.get('availability', ''),
                'lowprice': offer.get('lowPrice', ''),
                'highprice': offer.get('highPrice', ''),
                'price': offer.get('price', ''),
                'priceCurrency': offer.get('priceCurrency', ''),
            }

            products.append(product)

        return products
  • Crawl the site and store the products not OK as blocked during previous step
def scrape_products(proximus_sitemap, url='url'):
 

    df_products = pd.DataFrame(columns = ['product_name', 'brand', 'name', 'availability', 
                                          'lowprice', 'highprice','price','priceCurrency'])

    for index, row in proximus_sitemap.iterrows(): 
        
        metadata = extract_metadata(row[url])          
        products = get_products(metadata)
        
        if products is not None:
            for product in products:             
                df_products = df_products.append(product, ignore_index=True)

    return df_products
df_products = scrape_products(proximus_sitemap, url='loc')
df_products.to_csv('patch.csv', index=False)
df_products.head()
1 Answers

You can simply continue by using the advertools SEO crawler. It has a crawl function that also extracts structured data by default (JSON-LD, OpenGraph, and Twitter).

I tried to crawl a sample of ten pages, and this what the output looks like:

adv.crawl(proximus_sitemap['loc'], 'proximums.jl') 
proximus_crawl = pd.read_json('proximums.jl', lines=True)
proximus_crawl.filter(regex='jsonld').columns

Index(['jsonld_@context', 'jsonld_@type', 'jsonld_name', 'jsonld_url',
       'jsonld_potentialAction.@type', 'jsonld_potentialAction.target',
       'jsonld_potentialAction.query-input', 'jsonld_1_@context',
       'jsonld_1_@type', 'jsonld_1_name', 'jsonld_1_url', 'jsonld_1_logo',
       'jsonld_1_sameAs', 'jsonld_2_@context', 'jsonld_2_@type',
       'jsonld_2_itemListElement', 'jsonld_2_name', 'jsonld_2_image',
       'jsonld_2_description', 'jsonld_2_sku', 'jsonld_2_review',
       'jsonld_2_brand.@type', 'jsonld_2_brand.name',
       'jsonld_2_aggregateRating.@type',
       'jsonld_2_aggregateRating.ratingValue',
       'jsonld_2_aggregateRating.reviewCount', 'jsonld_2_offers.@type',
       'jsonld_2_offers.priceCurrency', 'jsonld_2_offers.availability',
       'jsonld_2_offers.price', 'jsonld_3_@context', 'jsonld_3_@type',
       'jsonld_3_itemListElement', 'jsonld_image', 'jsonld_description',
       'jsonld_sku', 'jsonld_review', 'jsonld_brand.@type',
       'jsonld_brand.name', 'jsonld_aggregateRating.@type',
       'jsonld_aggregateRating.ratingValue',
       'jsonld_aggregateRating.reviewCount', 'jsonld_offers.@type',
       'jsonld_offers.lowPrice', 'jsonld_offers.highPrice',
       'jsonld_offers.priceCurrency', 'jsonld_offers.availability',
       'jsonld_offers.price', 'jsonld_offers.offerCount',
       'jsonld_1_itemListElement', 'jsonld_2_offers.lowPrice',
       'jsonld_2_offers.highPrice', 'jsonld_2_offers.offerCount',
       'jsonld_itemListElement'],
      dtype='object')

These are some of the columns you might be interested in (containing price, currency, availability, etc.)

jsonld_2_description jsonld_2_offers.priceCurrency jsonld_2_offers.availability jsonld_2_offers.price jsonld_description jsonld_offers.lowPrice jsonld_offers.priceCurrency jsonld_offers.availability jsonld_offers.price jsonld_2_offers.lowPrice
0 nan nan nan nan nan nan nan nan nan nan
1 Numéro 7 EUR OutOfStock 369.99 nan nan nan nan nan nan
2 nan nan nan nan Apple 81.82 EUR InStock 487.6 nan
3 nan nan nan nan nan nan nan nan nan nan
4 nan nan nan nan Huawei nan EUR OutOfStock 330.57 nan
5 nan nan nan nan Apple 81.82 EUR LimitedAvailability 487.6 nan
6 Apple EUR InStock 589.99 nan nan nan nan nan 99
7 Apple EUR LimitedAvailability 589.99 nan nan nan nan nan 99
8 nan nan nan nan nan nan nan nan nan nan
9 nan nan nan nan nan nan nan nan nan nan
Related