Source code for sr.tools.bom.mouser

"""Routines for scraping data about parts from Mouser."""
from bs4 import BeautifulSoup
from decimal import Decimal as D
import re

from sr.tools.bom import distpart
from sr.tools.bom.cachedfetch import grab_url_cached


[docs]class Item(distpart.DistItem): """ An item sold by Mouser. :param part_number: The number of the part. """ def __init__(self, part_number): """Initialise a Mouser item.""" distpart.DistItem.__init__(self, part_number) self._getinfo() # Not sure if this is a think with Mouser self.price_for = 1 def _getinfo(self): """Load information from the distributor.""" mouser_url = 'https://xgoat.com/p/mouser/{part_number}' page = grab_url_cached(mouser_url.format(part_number=self.part_number)) soup = BeautifulSoup(page) if not self._check_exists(soup): raise distpart.NonExistentPart(self.part_number) # Check that the page we've been returned is for the requested part: if not self._soup_check_part(soup): raise distpart.NonExistentPart(self.part_number) self._get_availability(soup) # Only get pricing if it's not discontinued if self.avail is not None and not isinstance(self.avail, bool): self._get_pricing(soup) self._get_constraints(soup) def _check_exists(self, soup): """Work out whether the part exists based on the soup.""" # Simple test: is this div present? if soup.find(attrs={"id": "product-details"}) is None: return False return True def _soup_check_part(self, soup): """Work out whether the info we've retrieved is for the right part.""" sn = soup.find(attrs={"id": "divMouserPartNum"}).text.strip() return self.part_number == sn def _get_availability(self, soup): """Extract the part availability from the soup.""" av = soup.find(attrs={"id": "ctl00_ContentMain_availability_tbl1"}) av = av.find_all("td")[0] av = av.text.strip().replace(",", "") self.avail = int(av) def _get_pricing(self, soup): """Extract pricing information from the soup.""" pt = soup.find(attrs={"id": "ctl00_ContentMain_divPricing"}) prices = [] # There are multiple rows with availability and prices in for row in pt.find("table").find_all("tr"): q = row.find(attrs={"class": "PriceBreakQuantity"}) if q is not None: q = q.contents[1] qStr = q.string if qStr is None: continue quantity = int(qStr.replace(",", "")) ps = row.find(attrs={"class": "PriceBreakPrice"}).contents[1] ps = ps.text try: # The first character is a '£' price = D(ps[1:]) prices.append((quantity, price)) except: # Sometimes Mouser say 'Quote' pass if len(prices): # the minimum order is the smallest quantity from this table self.min_order = prices[0][0] self.prices = prices def _get_constraints(self, soup): """Extract the purchasing constraints from the soup.""" av = soup.find("table", attrs={"id": "ctl00_ContentMain_tbl2"}) av = av.find_all("tr")[1] av = av.find_all("td")[1] mo = re.search("\d", av.text).group(0) # mo now contains the minimum order quantity in string form self.min_order = int(mo) om = av.find("div") om = re.search("\d", om.text).group(0) # om now contains the order multiple in string form om = om.replace(",", "") self.multi = int(om)