just-one-cookbook.ts 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { Recipe, Ingredient } from "../../model";
  2. import { RecipeDetails, ComposedIngredients } from '../../model';
  3. import { parseIngredients } from '../index';
  4. import R from 'ramda';
  5. const SELECTORS = {
  6. TITLE: 'div.wprm-recipe-name',
  7. PREP_TIME: 'span.wprm-recipe-prep_time-minutes',
  8. COOK_TIME: 'span.wprm-recipe-cook_time-minutes',
  9. SERVINGS: 'span.wprm-recipe-servings',
  10. INGREDIENTS: 'div.wprm-recipe-ingredient-group',
  11. ING: {
  12. AMOUNT: 'span.wprm-recipe-ingredient-amount',
  13. UNIT: 'span.wprm-recipe-ingredient-unit',
  14. NAME: 'span.wprm-recipe-ingredient-name',
  15. NOTE: 'span.wprm-recipe-ingredient-notes'
  16. },
  17. SUB_RECIPE: 'div.wprm-recipe-ingredient-group-name',
  18. SUB_INGREDIENTS: 'li.wprm-recipe-ingredient',
  19. INSTRUCTIONS: 'div.wprm-recipe-instruction-text',
  20. TAGS: 'span.wprm-recipe-keyword',
  21. SUMMARY: 'div.wprm-recipe-summary',
  22. COURSE: 'span.wprm-recipe-course'
  23. }
  24. const getRecipeName = ($: CheerioSelector): string => {
  25. return $(SELECTORS.TITLE).text();
  26. }
  27. const getRecipeDetails = ($: CheerioSelector): RecipeDetails => {
  28. return {
  29. preparationTime: $(SELECTORS.PREP_TIME).text(),
  30. cookingTime: $(SELECTORS.COOK_TIME).text(),
  31. servings: $(SELECTORS.SERVINGS).text(),
  32. }
  33. };
  34. const getIngredients = ($: CheerioSelector): ComposedIngredients[] => {
  35. let ingredients: ComposedIngredients[] = [{name: '', ingredients: []}];
  36. const rawData = $(SELECTORS.INGREDIENTS);
  37. const removeEquivalence = (str: string): string => str.replace(/(\([0-9.]+ \w+\))|(\([0-9.]+ \w+\/[0-9.]+ \w+\))/, '');
  38. const getString = (element: CheerioElement, selector: string): string => $(element).find(selector).text();
  39. const cleanName = (element: CheerioElement, not: string): string => $(element).children().not(not).toArray().map(x => $(x).text()).join(' ');
  40. rawData.each((i, element) => {
  41. const last = ingredients.length - 1;
  42. const getIngredients = (li: Cheerio): Ingredient[] => li.toArray().map(el => ({
  43. ...parseIngredients(cleanName(el, SELECTORS.ING.NOTE)),
  44. note: $(el).find(SELECTORS.ING.NOTE).text(),
  45. _original: $(el).text().trim(),
  46. }));
  47. let subRecipe = $(element).find(SELECTORS.SUB_RECIPE);
  48. let subIngredients = $(element).find(SELECTORS.SUB_INGREDIENTS);
  49. if (subRecipe.contents().length) {
  50. ingredients.push({
  51. name: subRecipe.text(),
  52. ingredients: getIngredients(subIngredients),
  53. })
  54. } else {
  55. ingredients[last].ingredients = getIngredients(subIngredients);
  56. }
  57. })
  58. return ingredients;
  59. }
  60. const getTextList = (SELECTOR: string) => ($: CheerioSelector): string[] => $(SELECTOR).map((i, e) => $(e).text()).get()
  61. const getText = (SELECTOR: string) => ($: CheerioSelector): string => $(SELECTOR).text().trim();
  62. const JOC_CONFIG = {
  63. name: 'Just One Cookbook',
  64. domain: 'justonecookbook',
  65. website: 'https://www.justonecookbook.com',
  66. scrapeRecipe: ($: CheerioSelector): Recipe => ({
  67. name: getRecipeName($),
  68. author: { name: 'Nami' },
  69. details: getRecipeDetails($),
  70. ingredients: getIngredients($),
  71. instructions: getTextList(SELECTORS.INSTRUCTIONS)($),
  72. tags: (getText(SELECTORS.TAGS)($)).split(',').map(R.trim),
  73. course: [getText(SELECTORS.COURSE)($)],
  74. summary: getText(SELECTORS.SUMMARY)($),
  75. })
  76. }
  77. export default JOC_CONFIG;