This url will show the search result of a product in Amazon site.
https://www.amazon.com/s?k=yellow+puma+shoes&s=price-asc-rank&qid=1559364287&ref=sr_st_price-asc-rank
Using this xpath I can get product name
//span[#class='a-size-base-plus a-color-base a-text-normal']
Using this xpath I can get price
//span[#class='a-offscreen']
Is there a way to combine these 2 so that I can relate product name with price. 1 Way I can think of is:
//span[#class='a-size-base-plus a-color-base a-text-normal']
List<WebElements> allProductNames**** Save them in a list. Then run a for loop
for()
{
text = getText
//span[text()='PUMA Mens Basket Classic']/../../../../../..//span[#class='a-offscreen']
}
If you have an idea for easier solution kindly suggest. Thanks in advance.
I would rather get all the items and then iterate through each of them with in a loop as shown below.
// get all products
List<WebElement> products = driver.findElements(By.xpath("//span[#data-component-type='s-search-results']//div[#class='sg-row'][2]"));
// create a map (consider it as dictionary that to hold key value pair) to hold product and price
Map<String, String> map = new HashMap<String, String>();
for (WebElement product : products) {
String item = "";
String price = "";
// get product name
item = product.findElement(By.xpath(".//span[#class='a-size-base-plus a-color-base a-text-normal']")).getText();
// get product price (some products have range so we have to get the prices and display)
List<WebElement> prices = product.findElements(By.xpath(".//span[#class='a-offscreen']"));
for (WebElement pPrice : prices) {
if (price !=""){
price = price + "-" + pPrice.getAttribute("textContent");
}else {
price = pPrice.getAttribute("textContent");
}
}
// push the product and price to the map
map.put(item, price);
}
System.out.println(map);
Related
I have tried using .maxBy .max() and collection.Max and I have only been able to print with it stating every element is max
val fileName = "src/products.txt"
var products = HashMap<Int, Pair<String, Double>>()
var inputFD = File(fileName).forEachLine {
var pieces = it.split(",")
println("Item# Description Price")
println("----- ------------- ------")
for ( (pro,ducts) in products.toSortedMap() ) {
var pax = mutableListOf(ducts).maxBy { it -> it.second }
var highest = listOf<Double>(ducts.second).max()
println("The highest priced record is ${highest}")
}
the file is set up like this (111, shoe, 9.99)
output looks like this
The highest priced record is [(pants, 89.99)]
The highest priced record is [(shoes, 49.99)]
You are trying to print the value within the for-loop, hence it is printing it for every product. Also the variable is initialized everytime in the loop, so every value would be max.
Here is the right approach. Note that you can solve it without using mutable variables.
val fileName = "src/products.txt"
val products = File(fileName).readLines() //read all lines from file to a list
.map { it.split(",") } // map it to list of list of strings split by comma
.map { it[0] to it[1].toDouble() } // map each product to its double value in a Pair
.toMap() // convert list of Pairs to a Map
println("Item# Description Price")
println("----- ------------- ------")
products.keys.forEachIndexed { index, desc ->
println("$index\t$desc\t${products[desc]}")
}
println("The highest priced record is ${products.maxBy { it.value }}")
I am trying to add the items to the cart which are not same Price Items below is my code:
List<WebElement> priceSpans = getDriver().findElements(By.xpath("//div[#class='m-product-mini']//a//span[(contains(text(),'$')) and not(contains(#class,'priceTag-discount'))]"));
List<Double> priceOfProducts = new ArrayList<Double>();
for (WebElement webElement : priceSpans)
{
String priceText = webElement.getText();
if (priceText != null && priceText.length() > 0)
{
Double priceValue = Double.parseDouble(priceText.replace('$', ' ').trim());
priceOfProducts.add(priceValue);
System.out.println("The PLP Products Price are:" + priceValue);
}
}
Using the above code to print the price and below is the output:
The PLP Products Price are:69.99 The PLP Products Price are:64.99 The PLP Products Price are:59.99 The PLP Products Price are:54.99 The PLP Products Price are:49.99 The PLP Products Price are:59.99 The PLP Products Price are:39.99 The PLP Products Price are:79.99 The PLP Products Price are:119.99 The PLP Products Price are:69.99 The PLP Products Price are:79.99 The PLP Products Price are:119.99 The PLP Products Price are:69.99 The PLP Products Price are:119.99
So there are duplicate prices so how do I skip the duplicate one and how do I choose only one from the duplicate (i.e 2 products contains the same price ex:59.99 )
Simplest Solution, Just keep adding Current priceText to dummy String stringSoFar, and then check if that pricetext is already present in stringSoFar:
String stringSoFar="";
int counter=0;
for (WebElement webElement : priceSpans){
List<WebElement> refreshedPriceSpans = getDriver().findElements(By.xpath("//div[#class='m-product-mini']//a//span[(contains(text(),'$')) and not(contains(#class,'priceTag-discount'))]")); //to avoid stale exception
String priceText = refreshedPriceSpans.get(counter).getText();
stringSoFar = stringSoFar + priceText;
if (priceText != null && priceText.length() > 0 && !stringSoFar.contains(priceText))
{
Double priceValue = Double.parseDouble(priceText.replace('$', ' ').trim());
priceOfProducts.add(priceValue);
System.out.println("The PLP Products Price are:"+ priceValue);
}
counter++;
}
what about if we use Set, it self avoid duplicate values, where we can minimize if conditions
Set<Double> priceOfProducts = new HashSet<Double>();
You can use Map to avoid duplicate prices, check example code below.
In the map key is price and value is WebElement.
List<WebElement> priceSpans = getDriver().findElements(By.xpath("//div[#class='m-product-mini']//a//span[(contains(text(),'$')) and not(contains(#class,'priceTag-discount'))]"));
HashMap<Double, WebElement> priceOfProducts = new HashMap<>();
for (int i = 0; i < priceSpans.size(); i++) {
Double priceValue = Double.parseDouble(priceSpans.get(i).getText().replace('$', ' ').trim());
priceOfProducts.put(priceValue, priceSpans.get(i));
//System.out.println("The PLP Products Price are: " + priceValue);
}
priceOfProducts.forEach((k,v) -> System.out.println("The PLP Products Price are: " + k));
One thing before we get started, with your current code priceText is never going to equal null or be zero length because your XPath requires at least a '$' so you can remove the if check.
After that, you should take some time to look into Java streams. You can use them to do all kinds of collection processing, etc. like what you are doing here. Explanation of the different steps are in the code comments below.
By locator = By.xpath("//div[#class='m-product-mini']//a//span[(contains(text(),'$')) and not(contains(#class,'priceTag-discount'))]");
List<Double> priceOfProducts = getDriver().findElements(locator)
.stream() // turns the collection of WebElements into a stream
.map(e -> e.getText().replace("$", "").trim()) // turns the collection of WebElements into a collection of string (from .getText()), removes the '$', and trim()s
.distinct() // removes duplicates
.map(e -> Double.parseDouble(e)) // converts String to Double
.collect(Collectors.toList()); // the final piece... returns a List<Double>
priceOfProducts.forEach(System.out::println);
Since we don't have the original HTML, I mocked up some HTML with some different prices, extra whitespace all over, including one duplicate
<div id="base">
<span> $0.99 </span>
<span>$1.99 </span>
<span> $1.99 </span>
<span> $2.99 </span>
</div>
When I run the code on the HTML above (with an appropriate locator, By.cssSelector("#base > span")), I get the output
0.99
1.99
2.99
NOTE: You will need to add an import for the stream related code, import java.util.stream.Collectors;. Your IDE should help you with this but just in case...
There are several solution, but nothing with hashmap and enum.
Website table element simple tbody, tr,td. The code below, I have webelements in the rows list, or the row data as a string in the other list.
List<WebElement> rows = table.findElements(By.tagName("tr"));
List<ArrayList<String>> rowsData = new ArrayList<ArrayList<String>>();
for(WebElement row:rows){
List<WebElement> rowElements = row.findElements(By.tagName("td"));
ArrayList<String> rowData = new ArrayList<String>();
for(WebElement column:rowElements){
rowData.add(column.getText().toString());
}
rowsData.add(rowData);
}
One row can be:
12, ab, apple, red, -1000;
66, ac, grape, blue, 1000; etc.
I have to check all the rows and find the first row which is red, apple and last row is not bigger than 0 and click.
More useful option can be enum and hashmap. Hashmap keys can be enum values, like ID, NAME, FRUIT, COLOR, NUMBER. Checking hashmap has COLOR= red, name = apple etc.
Any solution for this? I think it can be very useful for everybody!
Thanks in advance!
Have you thought of creating row as an object? So something like this
public class Fruit {
private String name;
private int id;
//go horizontally with your column names;
//have getters/setters here
}
Now your code could be written slightly differently like,
List<WebElement> rows = table.findElements(By.tagName("tr"));
List<Fruit> fruits = new ArrayList<Fruit>();
for(WebElement row:rows){
List<WebElement> rowElements = row.findElements(By.tagName("td"));
Fruit fruit = new Fruit();
//you know first column is name or so on
//or store column header order and use it here
fruit.setName(rowElements.get(0).getText());
fruit.id(rowElements.get(1).getText());
fruits.add(fruit);
}
Now you can iterate over fruits
I manually manage my stocks on Prestashop. I am looking for a solution to automatically return the initial stock after a sales order.
For example, a product is ordered in two copies with an initial stock of 7. I would like the stock to remain at 7 after the order and not at 5.
Do you know a technique that would allow me to realize this automatically?
Put a Hook on Order Confirmation (displayOrderConfirmation) in a new module (you can generate one at https://validator.prestashop.com/) and check whats inside the cart then put it again in your stocks :
public function hookDisplayOrderConfirmation($params) {
$order = $params['order'];
$cart = new Cart($order->id_cart);
$products = $cart->getProducts();
foreach ($products as $product) {
$removed_qty = (int) $product['quantity'];
$past_qty = (int) StockAvailable::getQuantityAvailableByProduct($product['id_product'], $product['id_product_attribute']);
$new_qty = $removed_qty + $past_qty;
StockAvailable::setQuantity($product['id_product'], $product['id_product_attribute'], $new_qty);
}
}
I have the following method to return search results based on a supplied query
private List<Item> GetResults(QueryBase qBase)
{
using (IndexSearchContext sc = SearchManager.GetIndex("story").CreateSearchContext())
{
var hits = sc.Search(qBase, int.MaxValue);
var h1 = hits.FetchResults(0, 25);
var h2 = h1.Select(r => r.GetObject<Item>());
var h3 = h2.Where(item => item != null);
return h3.ToList();
}
}
The index being searched indexes web and master content. If I pass in a query that I know matches a single published item and break at the line beginning var h2 = then I see the variable hits has 2 items. This I expect because actually the items are both the same item, one from web and one from master.
However, the variable h1 only has a single result. The result from web has been omitted.
This is the case whether I'm debugging in the context of web or master. Can anyone explain?
When fetching the items using FetchResults method, Sitecore groups the items from lucene by the id of the item. First of the items becomes a SearchResult in the resulting SearchResultCollection object, and other items become Subresults for this results.
For example if you have a home item with id {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} with one published version and 4 versions in different languages for the home item, what you'll get from lucene is a single result and 4 subresults for this result:
using (IndexSearchContext sc = SearchManager.GetIndex("story").CreateSearchContext())
{
var hits = sc.Search(qBase, int.MaxValue);
var h1 = hits.FetchResults(0, 25);
foreach (SearchResult result in h1)
{
var url = result.Url;
foreach (SearchResult subresult in result.Subresults)
{
var subUrl = subresult.Url; // other versions of this item
}
}
}
The urls for results and subresults in my case would be:
sitecore://web/{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}?lang=en&ver=1
sitecore://master/{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}?lang=en&ver=1 (subresult)
sitecore://master/{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}?lang=ja-JP&ver=1 (subresult)
sitecore://master/{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}?lang=de-DE&ver=1 (subresult)
sitecore://master/{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}?lang=da&ver=1 (subresult)
so for retrieving the all the items with their versions you can use this code:
private List<Item> GetResults(QueryBase qBase)
{
using (IndexSearchContext sc = SearchManager.GetIndex("story").CreateSearchContext())
{
var hits = sc.Search(qBase, int.MaxValue);
var h1 = hits.FetchResults(0, 25);
var h2 = h1.Select(r => r.GetObject<Item>()).ToList();
// add other versions of item to the resulting list
foreach (IEnumerable<SearchResult> subresults in h1.Select(sr => sr.Subresults))
{
h2.AddRange(subresults.Select(r => r.GetObject<Item>()));
}
var h3 = h2.Where(item => item != null);
return h3.ToList();
}
}
You can not assume with item will be returned as the first one from the lucene and which items will be returned as subresults. If you want to get any specific item you need to pass version number, language and database to the query.