Explorar el Código

Recepie new ChromeDriver

Axel Nordh hace 3 años
padre
commit
e07df07a03

BIN
recept/chromedriver.exe


+ 0 - 0
recept/empty.png


+ 36 - 0
recept/pom.xml

@@ -5,4 +5,40 @@
   <version>0.0.1-SNAPSHOT</version>
   <name>Recept App</name>
   <description>En egen recept app</description>
+  <repositories>
+    <repository>
+      <id>nordhs-repo</id>
+      <name>Nords Repo</name>
+      <url>http://nordh.xyz:9099/repository/nordhs-repo/</url>
+    </repository>
+  </repositories>
+  
+  <dependencies>
+	  <dependency>
+	    <groupId>net.sourceforge.htmlunit</groupId>
+	    <artifactId>htmlunit</artifactId>
+	    <version>2.68.0</version>
+	  </dependency>
+	  <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
+	<dependency>
+	    <groupId>com.google.guava</groupId>
+	    <artifactId>guava</artifactId>
+	    <version>31.1-jre</version>
+	</dependency>
+	<dependency>
+	    <groupId>org.seleniumhq.selenium</groupId>
+	    <artifactId>selenium-java</artifactId>
+	    <version>4.6.0</version>
+	</dependency>
+	    <dependency>
+        <groupId>mysql</groupId>
+        <artifactId>mysql-connector-java</artifactId>
+        <version>8.0.30</version>
+    </dependency>
+    <dependency>
+	    <groupId>org.imgscalr</groupId>
+	    <artifactId>imgscalr-lib</artifactId>
+	    <version>4.2</version>
+	</dependency>
+  </dependencies>
 </project>

BIN
recept/recepie-image.png


+ 56 - 0
recept/src/main/java/Main.java

@@ -0,0 +1,56 @@
+import java.util.ArrayList;
+import java.util.List;
+
+import parser.Arla;
+import parser.Ica;
+import parser.Koket;
+import parser.ParserBase;
+import parser.Tasteline;
+
+public class Main {
+
+	public static void main(String[] args) {
+		ParserBase pb = new ParserBase();
+
+		Ica i = new Ica();
+		Koket k = new Koket();
+		Tasteline tl = new Tasteline();
+		Arla a = new Arla();
+		// Kokaihop ko = new Kokaihop();
+
+		List<String> wordsList = new ArrayList<>();
+		// wordsList.add("kyckling");
+
+		// wordsList.add("halloumi");
+		// wordsList.add("potatis");
+		// wordsList.add("kikärtor");
+		// wordsList.add("röda linser");
+		// wordsList.add("köttfärs");
+		// wordsList.add("paj");
+		// wordsList.add("alkoholfri");
+		// wordsList.add("lax");
+		// wordsList.add("torsk");
+		// wordsList.add("glass");
+		// wordsList.add("hamburgare");
+		// wordsList.add("jordgubbar");
+		// wordsList.add("vetemjöl special");
+		// wordsList.add("vita bönor");
+		// wordsList.add("parmesan");
+		// wordsList.add("feta");
+		// wordsList.add("lakritspulver");
+		// wordsList.add("vit sirap");
+
+		// i.findRecepiesWithSearchWords(wordsList, Ica.URL_SEARCH_PATTERN, Ica.PATH_SEPARATOR, Ica.BASE_URL,
+		// Ica.RECEPIELIST_ITEMS_XPATH, Ica.RECEPIE_TITLE_XPATH, Ica.COOKIE_CONSENT_BUTTON_XPATH);
+		// k.findRecepiesWithSearchWords(wordsList, Koket.URL_SEARCH_PATTERN, Koket.PATH_SEPARATOR, Koket.BASE_URL,
+		// Koket.RECEPIELIST_ITEMS_XPATH, Koket.RECEPIE_TITLE_XPATH, Koket.COOKIE_CONSENT_BUTTON_XPATH);
+		// tl.findRecepiesWithSearchWords(wordsList, Tasteline.URL_SEARCH_PATTERN, Tasteline.PATH_SEPARATOR, Tasteline.BASE_URL,
+		// Tasteline.RECEPIELIST_ITEMS_XPATH, Tasteline.RECEPIE_TITLE_XPATH,
+		// Tasteline.COOKIE_CONSENT_BUTTON_XPATH);
+		a.findRecepiesWithSearchWords(wordsList, Arla.URL_SEARCH_PATTERN, Arla.PATH_SEPARATOR, Arla.BASE_URL,
+				Arla.RECEPIELIST_ITEMS_XPATH, Arla.RECEPIE_TITLE_XPATH, Arla.COOKIE_CONSENT_BUTTON_XPATH);
+
+		// ko.findRecepiesWithSearchWords(wordsList, Kokaihop.URL_SEARCH_PATTERN, Kokaihop.PATH_SEPARATOR, Kokaihop.BASE_URL,
+		// Kokaihop.RECEPIELIST_ITEMS_XPATH, Kokaihop.RECEPIE_TITLE_XPATH, Kokaihop.COOKIE_CONSENT_BUTTON_XPATH);
+	}
+}

+ 190 - 0
recept/src/main/java/database/Database.java

@@ -0,0 +1,190 @@
+package database;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+import obejcts.Ingredient;
+import obejcts.Recepie;
+
+public class Database {
+
+	private static final String USERNAME = "recepieUser";
+	private static final String PASSWORD = "v.FV-ssZPzVyev28";
+	private static final String DATABASE = "recept";
+	private static final String URL = "jdbc:mysql://nordh.xyz:3306/";
+	Database instance;
+
+	Connection conn;
+
+	public void addIngredientToRecepie(int recepieId, Ingredient ingredient) {
+		String insertIngredientSql = "INSERT INTO ingredient(name) VALUES (?)";
+		String linkIngredientToRecepieSql = "INSERT INTO recepieIngredientLink(recepieId, ingredientId, amount, measurement) VALUES (?,?,?,?)";
+		int ingredientId = -1;
+		try (PreparedStatement ingredientStat = getConnection().prepareStatement(insertIngredientSql,
+				Statement.RETURN_GENERATED_KEYS);
+				PreparedStatement linkStat = getConnection().prepareStatement(linkIngredientToRecepieSql);) {
+			ingredientId = getIngredient(ingredient.getName());
+			if (ingredientId < 0) {
+				ingredientStat.setString(1, ingredient.getName());
+				ingredientStat.execute();
+
+				ResultSet key = ingredientStat.getGeneratedKeys();
+
+				while (key.next()) {
+					ingredientId = Integer.parseInt(key.getString(1));
+				}
+			}
+
+			linkStat.setInt(1, recepieId);
+			linkStat.setInt(2, ingredientId);
+			linkStat.setString(3, ingredient.getAmount());
+			linkStat.setString(4, ingredient.getMeasurement());
+
+			linkStat.execute();
+
+		} catch (SQLException e) {
+			System.out.println("Failing sql " + linkIngredientToRecepieSql + " " + recepieId + " "
+					+ ingredientId + " " + ingredient.getAmount() + " " + ingredient.getMeasurement() + System.lineSeparator()
+					+ " OR " + System.lineSeparator() + insertIngredientSql + " " + ingredient.getName());
+			e.printStackTrace();
+		}
+	}
+
+	public void addRecepieStep(int recepieId, int stepNumber, String stepDesc) {
+		String sql = "INSERT INTO recepieSteps(recepieId, stepNumber, text) VALUES (?,?,?) ON DUPLICATE KEY UPDATE text = ?";
+
+		try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+			stat.setInt(1, recepieId);
+			stat.setInt(2, stepNumber);
+			stat.setString(3, stepDesc);
+			stat.setString(4, stepDesc);
+
+			stat.execute();
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public Recepie getGetRecepieByTitle(String recepieTitle) {
+		Recepie result = new Recepie();
+		String sql = "SELECT * FROM recepie WHERE name = ?";
+
+		try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+			stat.setString(1, recepieTitle);
+
+			ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				result.setId(rs.getInt("id"));
+				result.setName(rs.getString("name"));
+				result.setDescription(rs.getString("description"));
+				result.setDifficulty(rs.getString("difficulty"));
+				result.setTime(rs.getString("time"));
+				result.setUrl(rs.getString("url"));
+			}
+
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+
+		return result;
+	}
+
+	public int getIngredient(String name) {
+		String sql = "SELECT id FROM ingredient WHERE name = ?";
+		int result = -1;
+		try (PreparedStatement stat = getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+			stat.setString(1, name);
+			ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				result = rs.getInt(1);
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+
+		return result;
+	}
+
+	public List<Recepie> getRecepiesWithoutImage(int count) {
+		List<Recepie> result = new ArrayList<>();
+
+		String sql = "SELECT * FROM recepie WHERE id NOT IN (SELECT recepieId FROM recepieImage) LIMIT ?";
+
+		try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+			stat.setInt(1, count);
+			ResultSet rs = stat.executeQuery();
+
+			while (rs.next()) {
+				result.add(new Recepie(rs.getInt("id"), rs.getString("url"), rs.getString("name"), rs.getString("time"),
+						rs.getString("description"), rs.getString("difficulty")));
+			}
+
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+
+		return result;
+	}
+
+	public int insertRecepie(Recepie newRecepie) {
+		String sql = "INSERT INTO recepie(url, name, time, description, difficulty) VALUES (?,?,?,?,?)";
+
+		int newId = -1;
+		try (PreparedStatement stat = getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+			stat.setString(1, newRecepie.getUrl());
+			stat.setString(2, newRecepie.getName());
+			stat.setString(3, newRecepie.getTime());
+			stat.setString(4, newRecepie.getDescription());
+			stat.setString(5, newRecepie.getDifficulty());
+
+			stat.execute();
+
+			ResultSet rs = stat.getGeneratedKeys();
+
+			while (rs.next()) {
+				newId = Integer.parseInt(rs.getString(1));
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+
+		return newId;
+	}
+
+	public void saveRecepieImage(int recepieId, File imageFile) {
+		String sql = "INSERT INTO recepieImage (recepieId, image) VALUES (?,?)";
+		try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+			stat.setInt(1, recepieId);
+			stat.setBlob(2, new FileInputStream(imageFile));
+			stat.execute();
+		} catch (SQLException e) {
+			e.printStackTrace();
+		} catch (FileNotFoundException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	private Connection getConnection() {
+		if (conn == null) {
+			try {
+				conn = DriverManager.getConnection(URL + DATABASE, USERNAME, PASSWORD);
+			} catch (SQLException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		return conn;
+	}
+}

+ 42 - 0
recept/src/main/java/obejcts/Ingredient.java

@@ -0,0 +1,42 @@
+package obejcts;
+
+public class Ingredient {
+	String name;
+	String amount;
+	String measurement;
+
+	public Ingredient(String name, String amount) {
+		this(name, amount, "");
+	}
+
+	public Ingredient(String name, String amount, String measurement) {
+		this.name = name;
+		this.amount = amount;
+		this.measurement = measurement;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public String getAmount() {
+		return amount;
+	}
+
+	public String getMeasurement() {
+		return measurement;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public void setAmount(String amount) {
+		this.amount = amount;
+	}
+
+	public void setMeasurement(String measurement) {
+		this.measurement = measurement;
+	}
+
+}

+ 71 - 0
recept/src/main/java/obejcts/Recepie.java

@@ -0,0 +1,71 @@
+package obejcts;
+
+public class Recepie {
+	int id;
+	String url;
+	String name;
+	String time;
+	String description;
+	String difficulty;
+
+	public Recepie(int id, String url, String name, String time, String description, String difficulty) {
+		super();
+		this.id = id;
+		this.url = url;
+		this.name = name;
+		this.time = time;
+		this.description = description;
+		this.difficulty = difficulty;
+	}
+
+	public Recepie() {
+	}
+
+	public int getId() {
+		return id;
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public String getTime() {
+		return time;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public String getDifficulty() {
+		return difficulty;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public void setTime(String time) {
+		this.time = time;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public void setDifficulty(String difficulty) {
+		this.difficulty = difficulty;
+	}
+}

+ 110 - 0
recept/src/main/java/parser/Arla.java

@@ -0,0 +1,110 @@
+package parser;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.imgscalr.Scalr;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import obejcts.Ingredient;
+import obejcts.Recepie;
+
+public class Arla extends ParserBase {
+
+	public static final String BASE_URL = "https://www.arla.se/recept/";
+	public static final String RECEPIE_TITLE_XPATH = ".//a[contains(@class,'c-card__title')]";
+	public static final String RECEPIELIST_ITEMS_XPATH = "//div[@class='c-card c-card--vertical']";
+	public static final String PATH_SEPARATOR = "+";
+	public static final String URL_SEARCH_PATTERN = "?search=";
+	public static final String COOKIE_CONSENT_BUTTON_XPATH = "//button[@class='save-preference-btn-handler onetrust-close-btn-handler']";
+	public static final String IMAGE_XPATH = "//div[@class='c-recipe__image']/picture/img";
+
+	public static final String SHOW_MORE_XPATH = "";
+
+	public static boolean isFromHereRecepie(String url) {
+		return url.contains(BASE_URL);
+	}
+
+	@Override protected void parseRecepie(WebElement recepie, String recepieTitle) {
+		try {
+			Thread.sleep(1000);
+			recepie.click();
+			Thread.sleep(200);
+
+			String description = driver.findElement(By.xpath("//div[contains(@class,'c-recipe__description')]")).getText();
+
+			String time = "";
+			if (checkIfElementExists(driver, "//span[@class='c-label--m u-mt--xxs u-mr--s']")) {
+				time = driver.findElement(By.xpath("//span[@class='c-label--m u-mt--xxs u-mr--s']")).getText().replaceAll(
+						"[^+0-9]",
+						"");
+			}
+			Recepie newRecepie = new Recepie();
+			newRecepie.setDescription(description);
+			newRecepie.setTime(time);
+			newRecepie.setName(recepieTitle);
+			newRecepie.setUrl(driver.getCurrentUrl());
+
+			newRecepie.setId(database.insertRecepie(newRecepie));
+
+			List<WebElement> ingredients = driver.findElements(By.xpath("//div[@class='c-recipe__ingredients-inner']//tr"));
+			for (WebElement ingredient : ingredients) {
+				String name = ingredient
+						.findElement(By.xpath(".//th//span[not (@class)]"))
+						.getText().trim();
+				String amountMeasureText = ingredient.findElement(By.xpath(".//td")).getText();
+
+				String amount = getAmountFromText(amountMeasureText);
+				String measurement = getMeasurement(amountMeasureText);
+
+				database.addIngredientToRecepie(newRecepie.getId(), new Ingredient(fixName(name), amount, measurement));
+			}
+
+			int stepCounter = 1;
+			List<WebElement> steps = driver
+					.findElements(By.xpath("//div[@class='c-recipe__instructions-steps']//span[not(@class)]"));
+
+			for (WebElement step : steps) {
+				database.addRecepieStep(newRecepie.getId(), stepCounter++, step.getText());
+			}
+
+			saveImage(newRecepie);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+
+		driver.navigate().back();
+	}
+
+	void saveImage(Recepie newRecepie) {
+		WebElement imageElement = driver.findElement(By.xpath(IMAGE_XPATH));
+		String imgSrc = imageElement.getAttribute("currentSrc");
+
+		try {
+			if (imgSrc.contains(".webp")) {
+				imgSrc = imgSrc.replace(".webp", "");
+			}
+			URL imageUrl = new URL(imgSrc);
+			BufferedImage savedImage = ImageIO.read(imageUrl);
+
+			BufferedImage resizedImage = Scalr.resize(savedImage, 300);
+
+			File file = new File("recepie-image.png");
+			ImageIO.write(resizedImage, "png", file);
+
+			database.saveRecepieImage(newRecepie.getId(), file);
+
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 125 - 0
recept/src/main/java/parser/Ica.java

@@ -0,0 +1,125 @@
+package parser;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.imgscalr.Scalr;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+
+import obejcts.Ingredient;
+import obejcts.Recepie;
+
+public class Ica extends ParserBase {
+
+	public static final String BASE_URL = "https://www.ica.se/recept";
+	public static final String PATH_SEPARATOR = "+";
+	public static final String RECEPIELIST_ITEMS_XPATH = "//div[contains(@class,'grid-items')]";
+	public static final String SHOW_MORE_XPATH = "//div[contains(@class,'show-more-button')]";
+	public static final String RECEPIE_TITLE_XPATH = ".//a[contains(@class,'recipe-card__content__title')]";
+	public static final String URL_SEARCH_PATTERN = "?q=";
+	public static final String COOKIE_CONSENT_BUTTON_XPATH = "//button[@id='onetrust-accept-btn-handler']";
+	public static final String IMAGE_XPATH = "//img[contains(@class,'recipe-header__image') and contains(@class,'image-loaded')]";
+
+	public static boolean isFromHereRecepie(String url) {
+		return url.contains(BASE_URL);
+	}
+
+	@Override protected void parseRecepie(WebElement recepie, String recepieTitle) {
+		scrollElementIntoViewCenter(driver, recepie);
+		wait.until(ExpectedConditions.elementToBeClickable(recepie));
+		try {
+			Thread.sleep(1000);
+		} catch (InterruptedException e1) {
+			e1.printStackTrace();
+		}
+		recepie.click();
+
+		Recepie newRecepie = new Recepie();
+
+		String titleXpath = "//h1[contains(@class,'recipe-header__title')]";
+		String recepieDetailsXpath = "//a[@class='items']";
+		String ingredientXpath = "//div[@class='ingredients-list-group__card']";
+		String stepByStepXpath = "//label[contains(@class, 'cooking-steps-main')]";
+
+		try {
+			Thread.sleep(200);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+
+		wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(titleXpath), 0));
+		newRecepie.setName(driver.findElement(By.xpath(titleXpath)).getText());
+		List<WebElement> details = driver.findElements(By.xpath(recepieDetailsXpath));
+
+		newRecepie.setTime(details.get(0).getText().replaceAll("[^0-9]", ""));
+		if (details.size() > 2) {
+			newRecepie.setDifficulty(details.get(2).getText());
+		}
+
+		newRecepie.setDescription(driver.findElement(By.xpath("//div[@class='recipe-header__preamble']//p")).getText());
+		newRecepie.setUrl(driver.getCurrentUrl());
+
+		newRecepie.setId(database.insertRecepie(newRecepie));
+
+		List<WebElement> ingredients = driver.findElements(By.xpath(ingredientXpath));
+
+		for (WebElement ingredient : ingredients) {
+			String amount = "";
+			String measurement = "";
+			if (checkIfElementExists(ingredient, ".//span[@class='ingredients-list-group__card__qty']")) {
+				WebElement measure = ingredient.findElement(By.xpath(".//span[@class='ingredients-list-group__card__qty']"));
+				amount = getAmountFromText(measure.getText());
+				measurement = measure.getText().replaceAll("[0-9]", "").trim();
+			}
+			WebElement ingr = ingredient.findElement(By.xpath(".//span[@class='ingredients-list-group__card__ingr']"));
+
+			database.addIngredientToRecepie(newRecepie.getId(), new Ingredient(fixName(ingr.getText()), amount, measurement));
+		}
+
+		List<WebElement> steps = driver.findElements(By.xpath(stepByStepXpath));
+
+		int stepCounter = 1;
+		for (WebElement step : steps) {
+			String stepDesc = step.getText();
+
+			database.addRecepieStep(newRecepie.getId(), stepCounter++, stepDesc);
+		}
+
+		saveImage(newRecepie);
+
+		driver.navigate().back();
+	}
+
+	void saveImage(Recepie newRecepie) {
+		WebElement imageElement = driver.findElement(By.xpath(IMAGE_XPATH));
+		String imgSrc = imageElement.getAttribute("currentSrc");
+
+		try {
+			if (imgSrc.contains(".webp")) {
+				imgSrc = imgSrc.replace(".webp", "");
+			}
+			URL imageUrl = new URL(imgSrc);
+			BufferedImage savedImage = ImageIO.read(imageUrl);
+
+			BufferedImage resizedImage = Scalr.resize(savedImage, 300);
+
+			File file = new File("recepie-image.png");
+			ImageIO.write(resizedImage, "png", file);
+
+			database.saveRecepieImage(newRecepie.getId(), file);
+
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 77 - 0
recept/src/main/java/parser/Kokaihop.java

@@ -0,0 +1,77 @@
+package parser;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.imgscalr.Scalr;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import obejcts.Recepie;
+
+public class Kokaihop extends ParserBase {
+
+	public static final String BASE_URL = "https://www.kokaihop.se/recept-sok?type=Recipe&fetchFacets=true&sort=relevence&withImages=true";
+	public static final String PATH_SEPARATOR = "%20";
+	public static final String URL_SEARCH_PATTERN = "&q=";
+
+	public static final String RECEPIE_TITLE_XPATH = ".//h2/a";
+	public static final String RECEPIELIST_ITEMS_XPATH = "//div[@class='recipe-container']/*";
+	public static final String COOKIE_CONSENT_BUTTON_XPATH = "//button[@mode='primary']";
+	public static final String IMAGE_XPATH = "";
+
+	public static final String SHOW_MORE_XPATH = "";
+
+	public static boolean isFromHereRecepie(String url) {
+		return url.contains(BASE_URL);
+	}
+
+	@Override protected void parseRecepie(WebElement recepie, String recepieTitle) {
+		String description = driver.findElement(By.xpath("//div[@id='recipeDetailInfo']/div/p/span")).getText();
+
+		Recepie newRecepie = new Recepie();
+		newRecepie.setDescription(description);
+		newRecepie.setUrl(driver.getCurrentUrl());
+		newRecepie.setName(recepieTitle);
+
+		newRecepie.setId(database.insertRecepie(newRecepie));
+
+		List<WebElement> ingredients = driver.findElements(By.xpath("//div[@class='ingredients_section']/ul/li"));
+
+		for (WebElement ingredient : ingredients) {
+			// NOT DONE, Kind of annoying to parse
+			// https://www.kokaihop.se/recept/kyckling-gryta-4
+		}
+	}
+
+	void saveImage(Recepie newRecepie) {
+		WebElement imageElement = driver.findElement(By.xpath(IMAGE_XPATH));
+		String imgSrc = imageElement.getAttribute("currentSrc");
+
+		try {
+			if (imgSrc.contains(".webp")) {
+				imgSrc = imgSrc.replace(".webp", "");
+			}
+			URL imageUrl = new URL(imgSrc);
+			BufferedImage savedImage = ImageIO.read(imageUrl);
+
+			BufferedImage resizedImage = Scalr.resize(savedImage, 300);
+
+			File file = new File("recepie-image.png");
+			ImageIO.write(resizedImage, "png", file);
+
+			database.saveRecepieImage(newRecepie.getId(), file);
+
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 148 - 0
recept/src/main/java/parser/Koket.java

@@ -0,0 +1,148 @@
+package parser;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.imageio.ImageIO;
+
+import org.imgscalr.Scalr;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+
+import obejcts.Ingredient;
+import obejcts.Recepie;
+
+public class Koket extends ParserBase {
+	public static final String RECEPIE_TITLE_XPATH = ".//h2[contains(@class,'list_item_title')]";
+	public static final String BASE_URL = "https://www.koket.se/";
+	public static final String PATH_SEPARATOR = "%20";
+	public static final String RECEPIELIST_ITEMS_XPATH = "//div[contains(@class,'list_item_wrapper')]";
+	public static final String URL_SEARCH_PATTERN = "search?searchtext=";
+	public static final String COOKIE_CONSENT_BUTTON_XPATH = "//button[@id='onetrust-accept-btn-handler']";
+
+	public static final String IMAGE_XPATH = "//img[contains(@class,'image__')]";
+
+	public static final String SHOW_MORE_XPATH = "//div[contains(@class,'show-more-button')]";
+
+	public static boolean isFromHereRecepie(String url) {
+		return url.contains(BASE_URL);
+	}
+
+	@Override protected String getAmountFromText(String text) {
+		Pattern pattern = Pattern.compile("^[0-9, \\/-]*");
+		Matcher matcher = pattern.matcher(text);
+
+		if (matcher.find()) {
+			return matcher.group();
+		}
+		return "";
+	}
+
+	@Override void handleRecepie(WebElement recepie, String recepieTitle, Recepie recepieFromDb) {
+		if (checkIfElementExists(recepie, ".//div[contains(@class,'rating_wrapper')]")) {
+			super.handleRecepie(recepie, recepieTitle, recepieFromDb);
+		}
+	}
+
+	@Override protected void parseRecepie(WebElement recepie, String recepieTitle) {
+		try {
+			Thread.sleep(1000);
+			recepie.click();
+			Thread.sleep(200);
+
+			wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath("//h1[contains(@class, 'recipe_title')]"), 0));
+
+			String title = driver.findElement(By.xpath("//h1[contains(@class, 'recipe_title')]")).getText();
+
+			String time = "";
+			if (checkIfElementExists(driver, "//div[contains(@class, 'details_wrapper')]//p[2]//span")) {
+				String timeText = driver.findElement(By.xpath("//div[contains(@class, 'details_wrapper')]//p[2]//span"))
+						.getText();
+				time = timeText.replaceAll("[^+0-9]", "");
+			}
+
+			Recepie newRecepie = new Recepie();
+			newRecepie.setTime(time);
+			newRecepie.setUrl(driver.getCurrentUrl());
+			newRecepie.setName(recepieTitle);
+			WebElement desc = driver.findElement(By.xpath("//div[contains(@class, 'description_description')]"));
+
+			scrollElementIntoView(driver, desc);
+			if (checkIfElementExists(desc, ".//p")) {
+				newRecepie.setDescription(desc.findElement(By.xpath(".//p")).getText());
+			}
+
+			newRecepie.setId(database.insertRecepie(newRecepie));
+
+			List<WebElement> ingredients = driver.findElements(By.xpath("//span[@class='ingredient']//span"));
+			String measurement = "";
+			String amount = "";
+			for (WebElement ingredient : ingredients) {
+				String name = fixName(ingredient.getText());
+				measurement = getMeasurement(name);
+				amount = getAmountFromText(name);
+				name = name.replace(measurement, "");
+				name = name.replace(amount, "");
+				name = name.trim();
+
+				database.addIngredientToRecepie(newRecepie.getId(), new Ingredient(fixName(name), amount, measurement));
+			}
+
+			List<WebElement> steps = driver
+					.findElements(By.xpath("//ol[contains(@class, 'instruction_section_numberedList')]//span"));
+
+			int stepCounter = 1;
+			for (WebElement step : steps) {
+				database.addRecepieStep(newRecepie.getId(), stepCounter++, step.getText());
+			}
+
+			saveImage(newRecepie);
+			driver.navigate().back();
+
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+
+	void saveImage(Recepie newRecepie) {
+
+		try {
+			WebElement imageElement = driver.findElement(By.xpath(IMAGE_XPATH));
+			String imgSrc = imageElement.getAttribute("currentSrc");
+			if (imgSrc.contains(".webp")) {
+				imgSrc = imgSrc.replace(".webp", "");
+			}
+			URL imageUrl = new URL(imgSrc);
+			BufferedImage savedImage = ImageIO.read(imageUrl);
+
+			BufferedImage resizedImage = Scalr.resize(savedImage, 300);
+
+			File file = new File("recepie-image.png");
+			ImageIO.write(resizedImage, "png", file);
+
+			database.saveRecepieImage(newRecepie.getId(), file);
+
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (NoSuchElementException ne) {
+			File file = new File("empty.png");
+			try {
+				file.createNewFile();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			database.saveRecepieImage(newRecepie.getId(), file);
+		}
+	}
+
+}

+ 53 - 0
recept/src/main/java/parser/Parser.java

@@ -0,0 +1,53 @@
+package parser;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAdjusters;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public interface Parser {
+
+	public static final String df = "yyyy-MM-dd";
+	static final Pattern timePattern = Pattern.compile("\\d+[,]{0,1}\\d+");
+	static final Pattern timeModifierPattern = Pattern.compile("[A-Za-z]+$");
+
+	public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(df);
+
+	default String getTimeModifierFromTimeCellValue(final String timeCell) {
+		final String timeModifier;
+		final Matcher timeModifierMatcher = timeModifierPattern.matcher(timeCell);
+		if (timeModifierMatcher.find()) {
+			timeModifier = timeModifierMatcher.group();
+		} else {
+			timeModifier = "";
+		}
+		return timeModifier;
+	}
+
+	default float getTimeFromTimeCellValue(final String timeCell) {
+		final float time;
+		final Matcher timeMatcher = timePattern.matcher(timeCell);
+		if (timeMatcher.find()) {
+			time = Float.valueOf(timeMatcher.group().replace(",", "."));
+		} else {
+			time = -1f;
+		}
+		return time;
+	}
+
+	default String getTodaysDate() {
+		return DATE_FORMAT.format(new Date());
+	}
+
+	default String getLastDayOfMonth() {
+		final LocalDate lastDayOfMonth = LocalDate.parse(getTodaysDate(), DateTimeFormatter.ofPattern(df))
+				.with(TemporalAdjusters.lastDayOfMonth());
+
+		return DATE_FORMAT.format(Date.from(lastDayOfMonth.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
+	}
+
+}

+ 278 - 0
recept/src/main/java/parser/ParserBase.java

@@ -0,0 +1,278 @@
+package parser;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.imgscalr.Scalr;
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import com.google.common.base.Strings;
+
+import database.Database;
+import obejcts.Recepie;
+
+public class ParserBase implements Parser {
+
+	List<String> measurements = Arrays.asList("msk", "tsk", "g", "kg", "ml", "dl", "l", "st", "krm", "förp", "kruka", "färsk",
+			"burk", "knippe", "kvist", "cm", "burkar", "cl", "port");
+	protected Database database = new Database();
+	protected ChromeDriver driver;
+	protected WebDriverWait wait;
+	protected JavascriptExecutor jsExecutor;
+
+	public void findRecepiesWithSearchWords(List<String> searchedWords, String urlSearchPattern, String pathSeparator,
+			String baseUrl,
+			String recepiesItemListXpath, String recepieTitleXpath, String cookieConsentButtonXpath) {
+		try {
+			String url = baseUrl;
+			url += urlSearchPattern;
+			String searchWords = "";
+			for (String word : searchedWords) {
+				searchWords += pathSeparator + word;
+			}
+			url += searchWords;
+
+			driver = getSeleniumDriver();
+			wait = getWaitDriver(driver);
+			jsExecutor = getJsExecutor(driver);
+
+			driver.get(url);
+			wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(recepiesItemListXpath), 0));
+
+			Thread.sleep(500);
+			if (checkIfElementExists(driver, cookieConsentButtonXpath)) {
+				driver.findElement(By.xpath(cookieConsentButtonXpath)).click();
+				Thread.sleep(100);
+			}
+
+			List<WebElement> recepies = driver.findElements(By.xpath(recepiesItemListXpath));
+
+			for (int i = 0; i < recepies.size(); i++) {
+				Thread.sleep(1000);
+				recepies = driver.findElements(By.xpath(recepiesItemListXpath));
+				WebElement recepie = recepies.get(i);
+
+				scrollElementIntoViewCenter(driver, recepie);
+				String recepieTitle = recepie.findElement(By.xpath(recepieTitleXpath)).getText();
+
+				Recepie recepieFromDb = database.getGetRecepieByTitle(recepieTitle);
+				handleRecepie(recepie, recepieTitle, recepieFromDb);
+			}
+
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+		driver.close();
+	}
+
+	public void testImportImages(int count) {
+		List<Recepie> recepiesWithoutImage = database.getRecepiesWithoutImage(count);
+
+		for (Recepie recepie : recepiesWithoutImage) {
+			if (Ica.isFromHereRecepie(recepie.getUrl())) {
+				saveImage(recepie, Ica.IMAGE_XPATH);
+			} else if (Koket.isFromHereRecepie(recepie.getUrl())) {
+				saveImage(recepie, Koket.IMAGE_XPATH);
+			} else if (Tasteline.isFromHereRecepie(recepie.getUrl())) {
+				saveImage(recepie, Tasteline.IMAGE_XPATH);
+			} else if (Arla.isFromHereRecepie(recepie.getUrl())) {
+				saveImage(recepie, Arla.IMAGE_XPATH);
+			} else {
+				System.out.println("Cound not find url " + recepie.getUrl());
+			}
+
+		}
+
+		driver.close();
+	}
+
+	protected boolean checkIfElementExists(ChromeDriver driver, String xpath) {
+		return !driver.findElements(By.xpath(xpath)).isEmpty();
+	}
+
+	protected boolean checkIfElementExists(WebElement element, String xpath) {
+		return !element.findElements(By.xpath(xpath)).isEmpty();
+	}
+
+	protected String fixName(String name) {
+		// name = name.replaceAll("[0-9]", "");
+		if (name.indexOf("(") > -1) {
+			int firstIndex = name.indexOf("(");
+			int secondIndex = name.indexOf(")");
+
+			name = name.substring(0, firstIndex) + name.substring(secondIndex + 1);
+		}
+
+		if (name.indexOf("[") > -1) {
+			int firstIndex = name.indexOf("[");
+			int secondIndex = name.indexOf("]");
+
+			name = name.substring(0, firstIndex) + name.substring(secondIndex + 1);
+		}
+
+		return name.trim();
+	}
+
+	protected float formatFloat(String string) {
+		float result = -1f;
+		try {
+			result = Float.parseFloat(string);
+		} catch (NumberFormatException e) {
+			// Empty by design
+		}
+
+		return result;
+	}
+
+	protected String getAmountFromText(String text) {
+		text = text.replaceAll("[^0-9\\/\\.,-½]", "").trim();
+		if (text.endsWith(",")) {
+			text.substring(0, text.length() - 1);
+		}
+		return text.replaceAll("[^0-9\\/\\.,-½]", "").trim();
+	}
+
+	protected JavascriptExecutor getJsExecutor(ChromeDriver driver) {
+		return driver;
+	}
+
+	protected String getMeasurement(String ingredientText) {
+		String result = "";
+		String[] parts = ingredientText.split(" ");
+		for (String part : parts) {
+			if (measurements.contains(part)) {
+				result = part.replaceAll("[\\/\\s]*", "").trim();
+				break;
+			}
+		}
+		return result;
+	}
+
+	protected ChromeDriver getSeleniumDriver() {
+		ChromeOptions options = new ChromeOptions();
+
+		System.setProperty("webdriver.chrome.driver",
+				System.getProperty("user.dir") + "/chromedriver.exe");
+		System.setProperty("webdriver.chrome.silentOutput", "true");
+		// Fixing 255 Error crashes
+		options.addArguments("--no-sandbox");
+		options.addArguments("--disable-dev-shm-usage");
+
+		// Options to trick bot detection
+		// Removing webdriver property
+		options.addArguments("--disable-blink-features=AutomationControlled");
+		options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
+		options.setExperimentalOption("useAutomationExtension", null);
+
+		// Changing the user agent / browser fingerprint
+		options.addArguments("window-size=1920,1080");
+		options.addArguments(
+				"user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36");
+
+		// Other
+		options.addArguments("disable-infobars");
+
+		ChromeDriver driver = new ChromeDriver(options);
+		driver.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");
+		return driver;
+	}
+
+	protected WebDriverWait getWaitDriver(ChromeDriver driver) {
+		return new WebDriverWait(driver, Duration.ofSeconds(30));
+	}
+
+	protected String onlyNumbers(String value) {
+		return value.replaceAll("[^0-9]", "");
+	}
+
+	protected void parseRecepie(WebElement recepie, String recepieTitle) {
+		// Empty by design, overridden by parsers
+	}
+
+	protected void scrollElementIntoView(ChromeDriver driver, WebElement element) {
+		((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView();", element);
+	}
+
+	protected void scrollElementIntoViewCenter(ChromeDriver driver, WebElement element) {
+		((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView({ block: 'center' });", element);
+	}
+
+	protected void scrollToTopOfPage(ChromeDriver driver) {
+		((JavascriptExecutor) driver)
+				.executeScript("document.body.scrollTop = document.documentElement.scrollTop = 0;");
+	}
+
+	void handleRecepie(WebElement recepie, String recepieTitle, Recepie recepieFromDb) {
+		if (Strings.isNullOrEmpty(recepieFromDb.getName())) {
+			parseRecepie(recepie, recepieTitle);
+		} else {
+			System.out.println(recepieFromDb.getName() + " ALREADY EXISTS IN DB");
+		}
+	}
+
+	void saveImage(Recepie newRecepie, String imageXpath) {
+
+		if (driver == null) {
+			driver = getSeleniumDriver();
+		}
+
+		driver.get(newRecepie.getUrl());
+
+		try {
+			Thread.sleep(500);
+		} catch (InterruptedException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+		}
+
+		if (!checkIfElementExists(driver, imageXpath)) {
+			File file = new File("empty.png");
+			try {
+				file.createNewFile();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+			database.saveRecepieImage(newRecepie.getId(), file);
+		} else {
+
+			WebElement imageElement = driver.findElement(By.xpath(imageXpath));
+			String imgSrc = imageElement.getAttribute("currentSrc");
+
+			try {
+				if (imgSrc.contains(".webp")) {
+					imgSrc = imgSrc.replace(".webp", "");
+				}
+				URL imageUrl = new URL(imgSrc);
+				BufferedImage savedImage = ImageIO.read(imageUrl);
+
+				BufferedImage resizedImage = Scalr.resize(savedImage, 300);
+
+				File file = new File("recepie-image.png");
+				ImageIO.write(resizedImage, "png", file);
+
+				database.saveRecepieImage(newRecepie.getId(), file);
+
+			} catch (MalformedURLException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+}

+ 62 - 0
recept/src/main/java/parser/Svenskarecept.java

@@ -0,0 +1,62 @@
+package parser;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+
+import org.imgscalr.Scalr;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import obejcts.Recepie;
+
+public class Svenskarecept extends ParserBase {
+
+	public static final String BASE_URL = "https://svenskarecept.se/";
+
+	public static final String RECEPIE_TITLE_XPATH = "//div[@class='entry-grid hgrid']//h2";
+	public static final String RECEPIELIST_ITEMS_XPATH = "//div[@class='entry-grid hgrid']";
+	public static final String PATH_SEPARATOR = "+";
+	public static final String URL_SEARCH_PATTERN = "?s=";
+	public static final String COOKIE_CONSENT_BUTTON_XPATH = "NA";
+	public static final String IMAGE_XPATH = "";
+
+	public static final String SHOW_MORE_XPATH = "";
+
+	public static boolean isFromHereRecepie(String url) {
+		return url.contains(BASE_URL);
+	}
+
+	@Override protected void parseRecepie(WebElement recepie, String recepieTitle) {
+
+	}
+
+	void saveImage(Recepie newRecepie) {
+		WebElement imageElement = driver.findElement(By.xpath(IMAGE_XPATH));
+		String imgSrc = imageElement.getAttribute("currentSrc");
+
+		try {
+			if (imgSrc.contains(".webp")) {
+				imgSrc = imgSrc.replace(".webp", "");
+			}
+			URL imageUrl = new URL(imgSrc);
+			BufferedImage savedImage = ImageIO.read(imageUrl);
+
+			BufferedImage resizedImage = Scalr.resize(savedImage, 300);
+
+			File file = new File("recepie-image.png");
+			ImageIO.write(resizedImage, "png", file);
+
+			database.saveRecepieImage(newRecepie.getId(), file);
+
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 125 - 0
recept/src/main/java/parser/Tasteline.java

@@ -0,0 +1,125 @@
+package parser;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.imgscalr.Scalr;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+
+import obejcts.Ingredient;
+import obejcts.Recepie;
+
+public class Tasteline extends ParserBase {
+	public static final String RECEPIE_TITLE_XPATH = ".//h3";
+	public static final String BASE_URL = "https://www.tasteline.com/";
+	public static final String PATH_SEPARATOR = "+";
+	public static final String RECEPIELIST_ITEMS_XPATH = "//div[contains(@class,'u-shadow-card')]";
+	public static final String URL_SEARCH_PATTERN = "sok/";
+	public static final String COOKIE_CONSENT_BUTTON_XPATH = "//button[@mode='primary']";
+	public static final String IMAGE_XPATH = "//div[@class='Carousel-item']/img";
+
+	public static final String SHOW_MORE_XPATH = "//div[contains(@class,'show-more-button')]";
+
+	public static boolean isFromHereRecepie(String url) {
+		return url.contains(BASE_URL);
+	}
+
+	@Override protected void parseRecepie(WebElement recepie, String recepieTitle) {
+		try {
+			Thread.sleep(1000);
+			recepie.click();
+			Thread.sleep(200);
+
+			wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath("//h1"), 0));
+
+			List<WebElement> spans = driver.findElements(By.xpath("//h1/following-sibling::div/div//span"));
+
+			String time = "";
+			String difficulty = "";
+			if (spans.size() == 2) {
+				time = onlyNumbers(spans.get(0).getText());
+				difficulty = spans.get(1).getText();
+			} else if (spans.size() == 1) {
+				if (onlyNumbers(spans.get(0).getText()).length() > 0) {
+					time = onlyNumbers(spans.get(0).getText());
+				} else {
+					difficulty = spans.get(0).getText();
+				}
+			}
+
+			Recepie newRecepie = new Recepie();
+			newRecepie.setTime(time);
+			newRecepie.setUrl(driver.getCurrentUrl());
+			newRecepie.setName(recepieTitle);
+			newRecepie.setDifficulty(difficulty);
+
+			newRecepie.setDescription(driver.findElement(By.xpath("//h1/following-sibling::div/p")).getText());
+
+			newRecepie.setId(database.insertRecepie(newRecepie));
+
+			List<WebElement> ingredients = driver.findElements(By.xpath("//li[@class='Ingredient u-contents']"));
+			String measurement = "";
+			String amount = "";
+			for (WebElement ingredient : ingredients) {
+				if (checkIfElementExists(ingredient, ".//div/abbr")) {
+					measurement = ingredient.findElement(By.xpath(".//div/abbr")).getText();
+					amount = ingredient.findElement(By.xpath(".//div/span")).getText();
+				} else {
+					measurement = "";
+					amount = "";
+				}
+
+				String name = ingredient.findElement(By.xpath(".//span[@class='Ingredient-name ']")).getText().trim();
+				database.addIngredientToRecepie(newRecepie.getId(), new Ingredient(fixName(name), amount, measurement));
+			}
+
+			List<WebElement> steps = driver
+					.findElements(By.xpath("//li[contains(@class, 'Step')]/div[2]"));
+
+			int stepCounter = 1;
+			for (WebElement step : steps) {
+				database.addRecepieStep(newRecepie.getId(), stepCounter++, step.getText());
+			}
+
+			saveImage(newRecepie);
+
+			driver.navigate().back();
+
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+
+	void saveImage(Recepie newRecepie) {
+		WebElement imageElement = driver.findElement(By.xpath(IMAGE_XPATH));
+		String imgSrc = imageElement.getAttribute("currentSrc");
+
+		try {
+			if (imgSrc.contains(".webp")) {
+				imgSrc = imgSrc.replace(".webp", "");
+			}
+			URL imageUrl = new URL(imgSrc);
+			BufferedImage savedImage = ImageIO.read(imageUrl);
+
+			BufferedImage resizedImage = Scalr.resize(savedImage, 300);
+
+			File file = new File("recepie-image.png");
+			ImageIO.write(resizedImage, "png", file);
+
+			database.saveRecepieImage(newRecepie.getId(), file);
+
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+}