Skip to content

Commit

Permalink
Add scraper for: kitchn (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
jayaddison authored and hhursev committed Nov 6, 2019
1 parent 49b88c7 commit 43c5a67
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 0 deletions.
2 changes: 2 additions & 0 deletions recipe_scrapers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .hundredandonecookbooks import HundredAndOneCookbooks
from .inspiralized import Inspiralized
from .jamieoliver import JamieOliver
from .kitchn import Kitchn
from .mybakingaddiction import MyBakingAddiction
from .nihhealthyeating import NIHHealthyEating
from .paninihappy import PaniniHappy
Expand Down Expand Up @@ -53,6 +54,7 @@
HundredAndOneCookbooks.host(): HundredAndOneCookbooks,
Inspiralized.host(): Inspiralized,
JamieOliver.host(): JamieOliver,
Kitchn.host(): Kitchn,
MyBakingAddiction.host(): MyBakingAddiction,
NIHHealthyEating.host(): NIHHealthyEating,
PaniniHappy.host(): PaniniHappy,
Expand Down
59 changes: 59 additions & 0 deletions recipe_scrapers/kitchn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from ._abstract import AbstractScraper
from ._utils import get_minutes, normalize_string, get_yields


class Kitchn(AbstractScraper):

@classmethod
def host(self):
return 'thekitchn.com'

def title(self):
return self.soup.find(
'h2',
{'class': 'Recipe__title'}
).get_text()

def total_time(self):
elements = self.soup.findAll(
'p',
{'class': 'Recipe__time-label'}
)
return sum([get_minutes(element) for element in elements])

def yields(self):
return get_yields(
self.soup.find(
'span',
{'class': 'Recipe__yield-entry'}
)
)

def image(self):
image = self.soup.find(
'meta',
{'property': 'og:image', 'content': True}
)
return image['content'] if image else None

def ingredients(self):
ingredients = self.soup.findAll(
'li',
{'class': 'Recipe__ingredient'}
)

return [
normalize_string(ingredient.get_text())
for ingredient in ingredients
]

def instructions(self):
instructions = self.soup.findAll(
'li',
{'class': 'Recipe__instruction-step'}
)

return '\n'.join([
normalize_string(instruction.get_text())
for instruction in instructions
])
70 changes: 70 additions & 0 deletions recipe_scrapers/tests/test_data/kitchn.testhtml

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions recipe_scrapers/tests/test_kitchn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import os
import unittest

from recipe_scrapers.kitchn import Kitchn


class TestKitchnScraper(unittest.TestCase):
maxDiff = None
def setUp(self):
# tests are run from tests.py
with open(os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'test_data',
'kitchn.testhtml'
)) as file_opened:
self.harvester_class = Kitchn(file_opened, test=True)

def test_host(self):
self.assertEqual(
'thekitchn.com',
self.harvester_class.host()
)

def test_title(self):
self.assertEqual(
self.harvester_class.title(),
'Beef & Cheese Manicotti'
)

def test_total_time(self):
self.assertEqual(
65,
self.harvester_class.total_time()
)

def test_yields(self):
self.assertEqual(
'8 serving(s)',
self.harvester_class.yields()
)

def test_image(self):
self.assertEqual(
'https://cdn.apartmenttherapy.info/image/upload/f_auto,q_auto:eco,c_fill,g_auto,w_1500/k%2FPhoto%2FRecipes%2F2019-10-how-to-beef-manicotti%2F2019-10-04_Kitchn86818_HT-Beef-Manicotti',
self.harvester_class.image()
)

def test_ingredients(self):
self.assertSetEqual(
set([
'1 large shallot, finely chopped',
'2 cups shredded part-skim mozzarella cheese, divided',
'4 cloves garlic, minced',
'12 ounces lean ground beef',
'1/2 cup finely grated Parmesan cheese, divided',
'1 teaspoon kosher salt',
'Cooking spray',
'1 large egg, lightly beaten',
'1/2 cup chopped fresh parsley leaves, divided',
'2 tablespoons olive oil',
'3 cups marinara sauce, divided',
'14 dried manicotti pasta tubes (8 ounces)',
'1 (15 to 16-ounce) container full-fat ricotta cheese',
]),
set(self.harvester_class.ingredients())
)

def test_instructions(self):
return self.assertEqual('\n'.join([
'Cook the beef for the filling. Heat the oil in a large skillet over medium-high heat until shimmering. Add the ground beef and cook until the meat begins to brown, about 5 minutes. Add the shallot and garlic, cook until the shallot is translucent and the beef is cooked through, about 5 minutes more. Transfer to a large bowl and set aside to cool to room temperature.',
'Heat the oven to 375ºF and prepare the baking dish. Arrange a rack in the middle of the the oven and heat the oven to 375ºF. Meanwhile, bring a large pot of heavily salted water to a boil. Coat a 9x13-inch baking dish with cooking spray. Spread about 1 1/2 cups of the marinara sauce in the bottom of the dish and set aside.',
'Boil the manicotti shells. Add the manicotti to the water and boil until they are al dente, about 8 minutes. Drain and set aside.',
'Mix up the filling. Add the ricotta, half of mozzarella, half of the Parmesan, half of the parsley, the egg, and the salt to the cooled beef mixture and stir to combine well.',
'Fill the shells. Transfer the beef and cheese mixture to a piping bag or gallon size ziptop bag. Snip off a 1/2-inch hole in one bottom corner of the bag, then pipe the filling into each manicotti tube (about generous 1/3 cup each). Nestle each filled manicotti in the sauce, packed them tightly together in a single layer.',
'Top the filled shells with the remaining sauce and cheeses. Pour the remaining 1 1/2 cups sauce evenly over the manicotti and sprinkle with the remaining mozzarella and Parmesan.',
'Bake for 35 to 40 minutes. Bake uncovered until the sauce is bubbly, the cheese is browned, and the noodles are very tender, 35 to 40. Let cool for about 10 minutes before serving. Sprinkle with the remaining parsley just before serving.',
]),
self.harvester_class.instructions()
)
1 change: 1 addition & 0 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from recipe_scrapers.tests.test_hundredandonecookbooks import *
from recipe_scrapers.tests.test_inspiralized import *
from recipe_scrapers.tests.test_jamieoliver import *
from recipe_scrapers.tests.test_kitchn import *
from recipe_scrapers.tests.test_mybakingaddiction import *
from recipe_scrapers.tests.test_nihhealthyeating import *
from recipe_scrapers.tests.test_notimplemented import *
Expand Down

0 comments on commit 43c5a67

Please sign in to comment.