JSoup web-scraping: How to select each value - android

I'm currently trying to get some values from a website with Jsoup for my android application.
Basically, I wanted to extract these values:
so that in the code, the model name should go to gpuModel variable, prices goes to gpuPrice and so on for every single Graphic card. and then I would store them in firebase using this code:
public void addToDatabase(String model, int price, int rating, double value,
double bench) {
Map<String, Object> docData = new HashMap<>();
docData.put("model", model);
docData.put("rating", rating);
docData.put("bench", bench);
docData.put("value", value);
docData.put("price", price);
db.collection("gpu").document(model).set(docData);
}
This is the HTML code for each GPU:
i am currently trying to extract only the gpu model name to keep it simple (in the end i want to extract all the other values too), this is my current web scraping code:
public void webScrape(){
new Thread(new Runnable() {
#Override
public void run() {
final StringBuilder builder = new StringBuilder();
try {
Document doc = Jsoup.connect("https://www.videocardbenchmark.net/GPU_mega_page.html").get();
Elements gpus = doc.select("[id^=gpu]");
for (Element i : gpus) {
Elements gpuModel = i.select("tr td:nth-child(2)");
String gpuName = gpuModel.text().replace("/", "");
addToDatabase(gpuName, 12,12,12,12);
}
} catch (IOException e){
e.printStackTrace();
}
runOnUiThread(new Runnable() {
#Override
public void run() {
}
});
}
}).start();
going by my logic, this should select all the GPU, and then in the for loop, it would extract only the card's name. set the gpuName to whatever was extracted and push it to the database.
But instead, I got this in my database:
It extracted the GPU name, but also all the other unwanted fields (including the prices, value, test date, etc).
so my question is, how do i select each value separately? all the examples i found on the internet was done with a very simple website where each value has their own ID so I can't really learn from them.
If there's anything i should provide, please do tell me.
simple code representation of what i want:
Document doc = Jsoup.connect(the url).get();
Elements gpus = doc.select(all gpu);
for (each gpu in all gpus) {
gpuName = gpu.select(name);
gpuPrice = gpu.select(price);
gpuValue = gpu.select(value);
gpuPower = gpu.select(power);
addToDatabase(gpuName, gpuPrice, gpuPower, gpuValue);
}

You should use a:nth-child(2) as the model name selector inside your run method:
Elements gpuModel = i.select("a:nth-child(2)");

Related

Image ordering in Android studio

I am trying to figure out an ordering of images in Android studio, as I am primarly iOS developer and Android is new to me.
I have simple app, which shows list of values, names, text attached to them. The text seem to be attached to the right names, however the pictures are completely mixed, not attached properly. Pictures are in folders in assets. I also tried to reorder them as "hard-coded" by naming them with numbers, which doesnt work.
I've noticed this piece of code, how ever Im not sure how to order, attach the pictures properly.
private String getImagePath(int position) {
String path = null;
AssetManager assetManager = getAssets();
try {
String[] imageNames = assetManager.list(foodType.getName(this));
path = foodType.getName(this) + "/" + imageNames[position];
} catch (IOException e) {
e.printStackTrace();
}
return path;
}
Lucas you first need to define the field/value you will use to sort the list. After you do that you either need to implement the Comparable interface in your model or sort the list inline just like this:
```
// Ascending Order
Collections.sort(calls, new Comparator<Calls>() {
#Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
// Descending Order
Collections.sort(calls, new Comparator<Calls>() {
#Override
public int compare(String o1, String o2) {
return o2.compareToIgnoreCase(o1);
}
});

Couchbase Lite: reading document vs querying data

I'm switching an Android project to using Couchbase Lite, and I'm confused with ways for fetching the data from the database.
I have a document, which contains only one property:
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("data_key", json);
Document document = database.getDocument("data_doc_id");
document.putProperties(properties);
The next my step is getting the stored data from the database. I found two ways:
The first approach is reading the document
Document doc = database.getDocument("data_doc_id");
String json = (String) doc.getProperty("data_key");
The second one is querying
View view = database.getView("data_json");
view.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
if ("data_doc_id".equals(document.get("_id")) {
String json = (String) doc.getProperty("data_key");
emitter.emit("data_key", json);
}
}
}, "1.0");
QueryEnumerator queryEnumerator = view.createQuery().run();
String dataJson = "";
for (QueryRow queryRow : run) {
if ("data_key".equals(queryRow.getKey()) {
json = (String) queryRow.getValue();
}
}
Is it okay to use the first approach to get the stored JSON?
For what cases the second approach should be used? It has far more code than the first one, maybe it has something to do with caching or/and speed/performance? What are the pros and cons of this approach?
The first approach is retrieving the document directly. This is the fastest way to do it.
With databases, though, often you want to retrieve documents based on some feature of the data. To do this you want the ability to create queries about the data. That's what the second approach is for.

How can I map a string and compare with another string?

This question might be silly but I'm not able to achieve this. I have a payment device that I'm connected via bluetooth to my app. For the device to display currency code, I need to pass a string, like this :
String codeForPaymendDevice = "978";
This "978" basically sends "EUR" currency code and displays on the screen of the payment device. (The device's library maps and handles this). In my app, when the user makes a purchase in EUR currency, it should compare with "978(EUR)" and if both matches, it should parse codeForPaymentDevice. I'm not able to do this because I cannot compare "EUR" with 978 (as my code doesn't know 978 is EUR, only the payment device knows 978 is EUR).
What I need to do is, map "978" to "EUR" code and then compare transaction.getCurrencyCode() with the mapped variable and then parse it.
private SaleRequest buildTransactionRequest(Transaction transaction) {
final SaleRequest tr = new SaleRequest();
BigDecimal amount = getAmountPaid();
String codeForPaymentDevice = "978";
String formattedAmount;
try {
if (!transaction.getCurrencyCode().equalsIgnoreCase(CREW_CURRENCY)) {
formattedAmount = AirFiApplication
.getPriceFormatter(AirFiApplication.getCurrency(transaction.getCurrencyCode())).format(amount);
// transaction.getCurrencyCode = EUR
tr.setCurrency(codeForPaymentDevice); // TODO remove hardcoding
}
} catch (MPosValueException e) {
LOG.error("Error while building transaction request due to {}", e.getLocalizedMessage());
}
return tr;
}
You can create a Map with some key (for now I have used currency code) with the value you need to pass as the value.
Map<String, String> map = new HashMap<>();
map.put("EUR", "978");
map.put("INR", "979");
map.put("USD", "980");
map.put("AED", "981");
...
In your code,
tr.setCurrency(map.get(transaction.getCurrencyCode()));

Android - XML or SQLite for static data

I am making Android app for practicing driving licence theory tests. I will have about 3000 questions. Question object would have several atributes (text, category, subcategory, answers, group). I will create them and put in app, so data won't ever change. When user chooses category, app would go througt data, look which question meets requirements (that user selected) and put it in list for displaying. What should I use to store data/questions, XML or SQLite? Thanks in advance.
Edit:
I forgot to mentiont that app won't use internet connection. Also, I planned to make simple java app for entering data. I would copy text from government's website (I don't have access to their database and I have to create mine), so I thought to just put question's image url to java program and it would download it and name it automaticaly. Also, when entering new question's text it would tell me if that question already exist before I enter other data. That would save me time, I wouldn't have to save every picture and name it my self. That is what I thought if using XML. Can I do this for JSON or SQLite?
If you do not have to perform complex queries, I would recommend to store your datas in json since very well integrated in android apps using a lib such as GSON or Jackson.
If you don't want to rebuild your app / redeploy on every question changes. You can imagine to have a small webserver (apache, nginx, tomcat) that serves the json file that you will request on loading of the app. So that you will download the questions when your app is online or use the cached one.
XML is a verbose format for such an usage, and does not bring much functions....
To respond to your last question, you can organise your code like that :
/**
* SOF POST http://stackoverflow.com/posts/37078005
* #author Jean-Emmanuel
* #company RIZZE
*/
public class SOF_37078005 {
#Test
public void test() {
QuestionsBean questions = new QuestionsBean();
//fill you questions
QuestionBean b=buildQuestionExemple();
questions.add(b); // success
questions.add(b); //skipped
System.out.println(questions.toJson()); //toJson
}
private QuestionBean buildQuestionExemple() {
QuestionBean b= new QuestionBean();
b.title="What is the size of your boat?";
b.pictures.add("/res/images/boatSize.jpg");
b.order= 1;
return b;
}
public class QuestionsBean{
private List<QuestionBean> list = new ArrayList<QuestionBean>();
public QuestionsBean add(QuestionBean b ){
if(b!=null && b.title!=null){
for(QuestionBean i : list){
if(i.title.compareToIgnoreCase(b.title)==0){
System.out.println("Question "+b.title+" already exists - skipped & not added");
return this;
}
}
System.out.println("Question "+b.title+" added");
list.add(b);
}
else{
System.out.println("Question was null / not added");
}
return this;
}
public String toJson() {
ObjectMapper m = new ObjectMapper();
m.configure(Feature.ALLOW_SINGLE_QUOTES, true);
String j = null;
try {
j= m.writeValueAsString(list);
} catch (JsonProcessingException e) {
e.printStackTrace();
System.out.println("JSON Format error:"+ e.getMessage());
}
return j;
}
}
public class QuestionBean{
private int order;
private String title;
private List<String> pictures= new ArrayList<String>(); //path to picture
private List<String> responseChoice = new ArrayList<String>(); //list of possible choices
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getPictures() {
return pictures;
}
public void setPictures(List<String> pictures) {
this.pictures = pictures;
}
public List<String> getResponseChoice() {
return responseChoice;
}
public void setResponseChoice(List<String> responseChoice) {
this.responseChoice = responseChoice;
}
}
}
CONSOLE OUTPUT
Question What is the size of your boat? added
Question What is the size of your boat? already exists - skipped & not added
[{"order":1,"title":"What is the size of your boat?","pictures":["/res/images/boatSize.jpg"],"responseChoice":[]}]
GIST :
provides you the complete working code I've made for you
https://gist.github.com/jeorfevre/5d8cbf352784042c7a7b4975fc321466
To conclude, what is a good practice to work with JSON is :
1) create a bean in order to build your json (see my example here)
2) build your json and store it in a file for example
3) Using android load your json from the file to the bean (you have it in andrdoid)
4) use the bean to build your form...etc (and not the json text file) :D
I would recommend a database (SQLite) as it provides superior filtering functionality over xml.
Create the db using DB Browser for SQLite
And then use the library SQLiteAssetHelper in the link-
https://github.com/jgilfelt/android-sqlite-asset-helper
Tutorial on how to use -
http://www.javahelps.com/2015/04/import-and-use-external-database-in.html
You can use Paper https://github.com/pilgr/Paper its a fast NoSQL data storage for Android.
SQLite is the best for your system. because you will have to maintain (text, category, subcategory, answers, group) etc. So if you create db and create table for them. That will be easy to manage and you can relationship with each other which is not possible to XML.

Displaying Country and its calling code, but returning its Abbreviated code using Android XML

I am trying to develop a Registration screen from Android XML. As in every Registration form, I would need to display the list of countries. I am able to do this using string-array in strings.xml file.
The greater part of the problem is, when a user selects a country, the phone number field just below it should be initially filled with its respective country code, which may or may not be editable. Here, my problem is how do I get the country code when the country is selected. Do I need to use a database or is it achievable using xml itself?
Besides that, when user submits the Register form, I would have to send the abbreviated code of the country, not the full country name. Now, again this would require either a database or xml?
My app doesn't use database till now, it would not be so good to use database for this purpose. Moreover, almost any application that uses a registration needs this thing to be done, but I cannot find any resources on how to do this in Android.
Also, I would like to tell you that my minimum sdk is version 7.
Please help.
I was finally able to do it without using database. I'm writing down the steps so that it may help anyone else who needs the same thing to be done.
First I downloaded the CSV available at: https://github.com/mledoze/countries/blob/master/countries.csv
I removed all other fields, except those I needed. It left me with 3 fields: name, abbreviation and calling code.
Next, I downloaded the CSVReader from: http://code.google.com/p/secrets-for-android/source/browse/trunk/src/au/com/bytecode/opencsv/CSVReader.java
Got the items from the CSV as mentioned in How to parse the CSV file in android application? by "Kopfgeldjaeger" as:
String next[] = {};
List<String[]> list = new ArrayList<String[]>();
try {
CSVReader reader = new CSVReader(new InputStreamReader(getAssets().open("countries.csv")));
for(;;) {
next = reader.readNext();
if(next != null) {
list.add(next);
} else {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
Next I added an ArrayList for each of the values like:
ArrayList<String> countryNames = new ArrayList<String>();
ArrayList<String> countryAbber = new ArrayList<String>();
ArrayList<String> countryCodes = new ArrayList<String>();
for(int i=0; i < list.size(); i++)
{
countryNames.add(list.get(i)[0]); // gets name
countryAbber.add(list.get(i)[1]); // gets abbreviation
countryCodes.add(list.get(i)[2]); // gets calling code
}
Then added it to the spinner in the XML layout as:
ArrayAdapter<String> countryAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, countryNames);
spinner.setAdapter(countryAdapter);
// adding event to display codes when country is selected
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int pos, long arg3) {
// display the corresponding country code
TextView tvCountryCode = (TextView) findViewById(R.id.country_code);
tvCountryCode.setText("+"+list.get(pos)[2]);
countryPosition = pos;
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
This way, I was able to display country code in the xml file, when a country was selected from the dropdown list.
Remember to add the setOnItemSelectedListener() to achive that.
I hope this helps somebody in future.
I would advise you to use a database. API level 7 supports SQLite databases (I used them in android 2.1 myself). Create one table that has all the required info:
create table countries (
_id integer primary key autoincrement,
country_code char(2),
country_name varchar,
phone_code char(4),
editable integer
);
Then store your country information into this table. When populating your list of countries, use this table instead of XML; display country names and associate country codes with each corresponding list item. Then on selection, use the country code to get the phone code and the 'editable' flag - and act upon this info.

Categories

Resources