Skip to content

Commit

Permalink
Adds support for ottolenghi.co.uk (#1227)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomtjes authored Aug 26, 2024
1 parent 1938651 commit bb8a4b3
Show file tree
Hide file tree
Showing 7 changed files with 2,525 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Scrapers available for:
- `https://www.bodybuilding.com/ <https://www.bodybuilding.com/>`_
- `https://bonappetit.com/ <https://bonappetit.com>`_
- `https://bongeats.com/ <https://bongeats.com/>`_
- `https://books.ottolenghi.co.uk <https://books.ottolenghi.co.uk/>`_ (*)
- `https://bowlofdelicious.com/ <https://bowlofdelicious.com/>`_
- `https://breadtopia.com/ <https://breadtopia.com/>`_
- `https://briceletbaklava.ch/ <https://briceletbaklava.ch/>`_
Expand Down
3 changes: 3 additions & 0 deletions recipe_scrapers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from .ottolenghibooks import OttolenghiBooks

__all__ = (
"AbstractScraper",
"ElementNotFoundInHtml",
Expand Down Expand Up @@ -498,6 +500,7 @@
NotEnoughCinnamon.host(): NotEnoughCinnamon,
NutritionFacts.host(): NutritionFacts,
OneSweetAppetite.host(): OneSweetAppetite,
OttolenghiBooks.host(): OttolenghiBooks,
PeelWithZeal.host(): PeelWithZeal,
PinchOfYum.host(): PinchOfYum,
PotatoRolls.host(): PotatoRolls,
Expand Down
93 changes: 93 additions & 0 deletions recipe_scrapers/ottolenghibooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import re

from ._abstract import AbstractScraper
from ._grouping_utils import group_ingredients
from ._utils import get_yields, normalize_string


class OttolenghiBooks(AbstractScraper):
@classmethod
def host(cls):
return "books.ottolenghi.co.uk"

def author(self):
return "Ottolenghi"

def title(self):
return normalize_string(self.soup.h1.string)

def yields(self):
recipeyield = self.soup.find(itemprop="recipeYield").get_text().lower()
replacements = [
("one", "1"),
("two", "2"),
("three", "3"),
("four", "4"),
("five", "5"),
("six", "6"),
("seven", "7"),
("eight", "8"),
("nine", "9"),
("ten", "10"),
("eleven", "11"),
("twelve", "12"),
("thirteen", "13"),
("fourteen", "14"),
("fifteen", "15"),
("sixteen", "16"),
("seventeen", "17"),
("eighteen", "18"),
("nineteen", "19"),
("twenty", "20"),
]
for word, number in replacements:
recipeyield = re.sub(r"\b" + word + r"\b", number, recipeyield)
return get_yields(recipeyield)

def ingredient_groups(self):
return group_ingredients(
self.ingredients(),
self.soup.find(class_="recipe__aside"),
"h3",
"li",
)

def ingredients(self):
ingredients = self.soup.find(class_="recipe__aside").find_all("li")
return [normalize_string(i.get_text().replace(" - ", " ")) for i in ingredients]

def instructions(self):
recipe = self.soup.find(class_="recipe-list")
tags = recipe.find_all("li")
instructions = [normalize_string(tag.get_text()) for tag in tags]
return "\n".join(instructions)

def description(self):
tags = self.soup.find(class_="recipe").find_all("p")
desc = [normalize_string(tag.get_text()) for tag in tags]
return "\n".join(desc)

def dietary_restrictions(self):
# this is consistently used only in the books Simple and Sweet
diets = self.soup.find(class_="recipe__types").css.select(
'a[href^="/simple/"], a[href^="/sweet/"]'
)
return [normalize_string(diet.get_text().lower()) for diet in diets]

def keywords(self):
# for the book Simple:
simple_keywords = self.soup.find_all(class_="simple-icon")
keywords = [
normalize_string(k.get_text().replace("Simple ", ""))
for k in simple_keywords
]
# except for the books Simple and Sweet (see dietary_restrictions):
other_keywords = self.soup.find(class_="recipe__types").css.select(
'a:not([href^="/simple/"]):not([href^="/sweet/"])'
)
for k in other_keywords:
keywords.append(normalize_string(k.get_text()))
return keywords

def category(self):
return normalize_string(self.soup.find(class_="recipe-meta").get_text())
56 changes: 56 additions & 0 deletions tests/test_data/books.ottolenghi.co.uk/ottolenghibooks_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"author": "Ottolenghi",
"canonical_url": "https://books.ottolenghi.co.uk/otk-shelf-love/recipe/chickpeas-cacio-e-pepe/",
"site_name": "Ottolenghi Books",
"host": "books.ottolenghi.co.uk",
"language": "en-GB",
"title": "Chickpeas cacio e pepe",
"ingredients": [
"300g dried chickpeas, soaked overnight in plenty of water and 1 tsp bicarbonate of soda",
"3 tbsp olive oil",
"8 garlic cloves, crushed",
"80g Parmesan, finely grated, plus 1–2 optional Parmesan rinds (60g)",
"¼ tsp bicarbonate of soda",
"2 red chillies, thinly sliced into rounds, seeds and all",
"2 tbsp apple cider vinegar",
"250g baby spinach",
"15g parsley, roughly chopped",
"100g unsalted butter, fridge cold and cut into 2cm cubes",
"salt and black pepper"
],
"ingredient_groups": [
{
"ingredients": [
"300g dried chickpeas, soaked overnight in plenty of water and 1 tsp bicarbonate of soda",
"3 tbsp olive oil",
"8 garlic cloves, crushed",
"80g Parmesan, finely grated, plus 1–2 optional Parmesan rinds (60g)",
"¼ tsp bicarbonate of soda",
"2 red chillies, thinly sliced into rounds, seeds and all",
"2 tbsp apple cider vinegar",
"250g baby spinach",
"15g parsley, roughly chopped",
"100g unsalted butter, fridge cold and cut into 2cm cubes",
"salt and black pepper"
]
}
],
"instructions": "Preheat the oven to 160°C fan.\nDrain the chickpeas well and set them aside. Put 2 tablespoons of oil into a large, high-sided ovenproof sauté pan or cast-iron saucepan, for which you have a lid, and place on a medium-high heat. Once hot, add the garlic and cook for 90 seconds, until starting to colour. Add the Parmesan rinds, if using, the drained chickpeas, bicarbonate of soda, 1.2 litres of water and a very generous amount of coarsely cracked black pepper (give it about 40 grinds). Bring to the boil, skimming the scum from the surface as needed, then cover with the lid and bake in the oven for 75 minutes. Add ¾ teaspoon of salt and continue cooking, covered, for another 30 minutes, or until the chickpeas are very soft and the liquid has reduced by about half.\nMeanwhile, mix together the chillies, vinegar and a small pinch of salt in a small bowl. Set aside to pickle.\nTowards the last 10 minutes of cooking the chickpeas, put the last tablespoon of oil into a large frying pan on a medium-high heat and, once hot, cook the spinach, adding it to the pan in batches with ¼ teaspoon of salt until just wilted, about 4 minutes. Add the parsley and remove from the heat.\nWhen the chickpeas are ready, remove the lid and, while still hot from the oven but off the heat, add a quarter of the butter cubes and about 15g of grated Parmesan, mixing until the butter has melted into the sauce. Continue in this way, adding a quarter more of the butter and 15g more of Parmesan until you’ve used up all 100g of butter and 60g of cheese. Finally, add another very generous grind of coarsely ground black pepper. The sauce will have thickened significantly, coating the chickpeas nicely. Add a splash more water if you like it looser. Remove the Parmesan rinds, if using.\nTop with the spinach mixture, the pickled chillies and their liquid and a final sprinkling of Parmesan, serving any extra grated Parmesan alongside.",
"instructions_list": [
"Preheat the oven to 160°C fan.",
"Drain the chickpeas well and set them aside. Put 2 tablespoons of oil into a large, high-sided ovenproof sauté pan or cast-iron saucepan, for which you have a lid, and place on a medium-high heat. Once hot, add the garlic and cook for 90 seconds, until starting to colour. Add the Parmesan rinds, if using, the drained chickpeas, bicarbonate of soda, 1.2 litres of water and a very generous amount of coarsely cracked black pepper (give it about 40 grinds). Bring to the boil, skimming the scum from the surface as needed, then cover with the lid and bake in the oven for 75 minutes. Add ¾ teaspoon of salt and continue cooking, covered, for another 30 minutes, or until the chickpeas are very soft and the liquid has reduced by about half.",
"Meanwhile, mix together the chillies, vinegar and a small pinch of salt in a small bowl. Set aside to pickle.",
"Towards the last 10 minutes of cooking the chickpeas, put the last tablespoon of oil into a large frying pan on a medium-high heat and, once hot, cook the spinach, adding it to the pan in batches with ¼ teaspoon of salt until just wilted, about 4 minutes. Add the parsley and remove from the heat.",
"When the chickpeas are ready, remove the lid and, while still hot from the oven but off the heat, add a quarter of the butter cubes and about 15g of grated Parmesan, mixing until the butter has melted into the sauce. Continue in this way, adding a quarter more of the butter and 15g more of Parmesan until you’ve used up all 100g of butter and 60g of cheese. Finally, add another very generous grind of coarsely ground black pepper. The sauce will have thickened significantly, coating the chickpeas nicely. Add a splash more water if you like it looser. Remove the Parmesan rinds, if using.",
"Top with the spinach mixture, the pickled chillies and their liquid and a final sprinkling of Parmesan, serving any extra grated Parmesan alongside."
],
"category": "Pulses & Grains",
"yields": "4 servings",
"description": "These chickpeas are inspired by cacio e pepe, a rich Italian dish of pasta coated in lavish amounts of butter, black pepper and cheese. This uses a similar technique applied to our favourite little bean, using the flavourful Parmesan chickpea water to create the emulsified sauce.\nChickpeas differ in size depending on where you are in the world, so if you feel that this should be a little looser, just add a splash of water. Be sure to start the night before by soaking your chickpeas.",
"dietary_restrictions": [],
"image": "https://books.ottolenghi.co.uk/wp-content/uploads/2021/08/121_CacioEPepeChickpeas_3237-scaled.jpg",
"keywords": [
"Meat, Seafood & Dairy",
"Spices"
]
}
Loading

0 comments on commit bb8a4b3

Please sign in to comment.