Parsing HTML sử dụng volley và jsoup

Công Nghệ
Parsing HTML sử dụng volley và jsoup
Bài viết được sự cho phép của tác giả Trần Thị Thu Hà Hôm nay là ngày thứ 7, cuối tuần, tôi mới có thời gian thoải mái đắm chìm trong một vài bài nhạc Trịnh du dương,ngọt ngào cùng nhấm nháp thứ chất lỏng đen sì , chát đắng...

Bài viết được sự cho phép của tác giả Trần Thị Thu Hà

Hôm nay là ngày thứ 7, cuối tuần, tôi mới có thời gian thoải mái đắm chìm trong một vài bài nhạc Trịnh du dương,ngọt ngào  cùng nhấm nháp thứ chất lỏng đen sì , chát đắng  sặc mùi hóa chất mà người ta vẫn hay kháo nhau bằng cái tên rất mĩ miều “cafe” .Chết lảm nhảm rồi!!!

Đây là một bài chia sẻ. Chuẩn đấy! Mặc dù nó được tôi viết (chính xác là đánh máy), nhưng nó chỉ là một bài chia sẻ . Chắc chắn không phải là một giáo án hay đại loại thế, đừng nhầm! Giáo án thuộc một phạm trù gì đó dành cho nhà giáo đáng kính , hay các expert cao quý .Còn tôi, đơn thuần là một anh chàng đang trong tuổi ăn , tuổi học loay hoay tập code kể lại mấy hoạt động  testing với cái gọi là parsing HTML. Thế nên, xin nhắc lại lần nữa: Đây, là một bài chia sẻ :)) .Ấy lại lảm nhảm rồi

Nhưng bắt đầu từ đâu đây? Htmljsoup

Thực ra thì cái nào cũng quan trọng với bài này, khi bạn click vào link này thì chắc là bạn có lướt qua một trong những khái niệm trên rồi nhỉ .Nếu không thì các bạn vào từng cái để tìm hiểu đã nhé

Thôi , có vẻ hơi lan man . Chúng ta vào phần chính, à mà quên. nhược điểm của việc bóc tách theo dạng này là rất tốn băng thông nhé vì phải download cả file html, và app rất dễ tèo (app phụ thuộc hoàn toàn vào web theo một cách hoàn toàn bị động)

Thôi zô nào

Cấu trúc của project :

1   Interfaces

2  IHTMLParser

3  IAsyncCallback

Tiếp theo là thư viện sử dụng , các bạn nhớ add đầy đủ nhé

Chúng ta sẽ xử lý Networking thông qua thư viện volley.Nói về volley thì các bạn cũng biết ưu điểm của nó là gì rồi,Các bạn vào đây tìm hiểu thêm

Màn khởi động thế là ổn rồi :).Chúng ta đến với class đầu tiên

BaseApplication

ở đây tôi sử dụng Singleton Pattern mục đích là đảm bảo tại mỗi thời điểm nó được gọi thì chỉ có mỗi nó được tạo ra

public class BaseApplication extends Application {

private static BaseApplication sInstance;
private RequestQueue mRequestQueue;

public synchronized static BaseApplication getInstance() {
return sInstance;
}

public RequestQueue getRequestQueue() {
return mRequestQueue;
}

@Override
public void onCreate() {
super.onCreate();
sInstance = this;
mRequestQueue = Volley.newRequestQueue(this);
}
}

Tiếp theo là 2 Interface IHTMLParser và IAsyncCallback 

package vn.com.vdc.myapplication.webrequesthandler;

public interface IAsyncCallback {
     void onComplete(WebResponse responseContent);
     void onError(String errorData);
}
package vn.com.vdc.myapplication.webrequesthandler;

import vn.com.vdc.myapplication.entities.BaseObject;

public interface IHTMLParser {
BaseObject parseHTML(String htmlToParse);
}

Chúng ta đến với class BaseHttpRequest

ở class này tôi xử lí các response.Các bạn chú ý đoạn xử lý dữ liệu này với AsyncTask nhé

HTMLParseAsyncTask task = new HTMLParseAsyncTask();
                task.setCurrentRequest(BaseHttpRequest.this);
                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, response);

toàn bộ code class BaseHttpRequest

package vn.com.vdc.myapplication.webrequesthandler;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;

import java.io.PrintWriter;
import java.io.StringWriter;

public class BaseHttpRequest {

ProgressDialog progressDialog;
int responseCode;
IAsyncCallback callback;
Response.Listener stringResponseListener;
private IHTMLParser htmlParser;
private String url;

//Nếu giá trị trả về lỗi
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
StringWriter errors = new StringWriter();
error.printStackTrace(new PrintWriter(errors));
dismissProgressDialog();
callback.onError(error.toString());
}
};
private Context context;

public BaseHttpRequest(Activity localActivity,
String url) {

context = localActivity;
this.url = url;
setListeners();
}
public IHTMLParser getHtmlParser() {
return htmlParser;
}

public void setHtmlParser(IHTMLParser htmlParser) {
this.htmlParser = htmlParser;
}

public IAsyncCallback getCallback() {
return callback;
}

public Context getContext() {
return context;
}

private void setListeners() {
stringResponseListener = new Response.Listener() {
@Override
public void onResponse(String response) {
HTMLParseAsyncTask task = new HTMLParseAsyncTask();
task.setCurrentRequest(BaseHttpRequest.this);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, response);
}
};
}

public void dismissProgressDialog() {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}

public void execute(IAsyncCallback callback, RequestQueue requestQueue) {
this.callback = callback;
progressDialog = new ProgressDialog(getContext());
progressDialog.setCancelable(false);
progressDialog.setMessage("Please wait...");
dismissProgressDialog();
progressDialog.show();
addToRequestQueue(requestQueue, getStringRequest());
}

public  void addToRequestQueue(RequestQueue requestQueue, Request req) {
requestQueue.add(req);
}

Request getStringRequest() {
return new StringRequest(Request.Method.GET, url, stringResponseListener, errorListener);
}
}

Ok, Tiếp theo là class HTMLParseAsyncTask 

Xử lý đa luồng , tôi dùng AsyncTask ,gọn gàng và sạch sẽ :))

Nó sẽ thực hiện parsing dữ liệu thông qua param truyền vào

baseObject = getCurrentRequest().getHtmlParser().parseHTML(params[0]);

Toàn bộ class HTMLParseAsyncTask 

package vn.com.vdc.myapplication.webrequesthandler;

import android.os.AsyncTask;

import vn.com.vdc.myapplication.entities.BaseObject;

public class HTMLParseAsyncTask extends AsyncTask<String, Void, Void> {

private BaseObject baseObject;
private BaseHttpRequest currentRequest;

public BaseHttpRequest getCurrentRequest() {
return currentRequest;
}

public void setCurrentRequest(BaseHttpRequest currentRequest) {
this.currentRequest = currentRequest;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected Void doInBackground(String... params) {
if (getCurrentRequest().getHtmlParser() != null) {
baseObject = getCurrentRequest().getHtmlParser().parseHTML(params[0]);
}
return null;
}

@Override
protected void onPostExecute(Void result) {
getCurrentRequest().dismissProgressDialog();
try {
WebResponse webResponse = new WebResponse(baseObject);
getCurrentRequest().getCallback().onComplete(webResponse);

} catch (Exception exception) {
exception.printStackTrace();
}
}
}

Với class  SmartjobParser

ở class này chúng ta thao tác trực tiếp với Jsoup .Các bạn chú ý hàm này

doc.getElementsByTag("title");

Nó sẽ trả về một chuỗi có tag name là “tittle”,ngoài ra các bạn có thể thay đổi các hàm khác để test

Tiếp theo thì tôi đưa text vừa get về được vào array và add url vào cho phần tử đó

for (Element anchor : anchors) {
post = new Smartjop();
post.setURL(anchor.attr("href"));
post.setText(anchor.text());
response.getPosts().add(post);
}

Toàn bộ class SmartjobParser 

package vn.com.vdc.myapplication.parsers;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.util.ArrayList;

import vn.com.vdc.myapplication.entities.Smartjop;
import vn.com.vdc.myapplication.webrequesthandler.IHTMLParser;

public class SmartjobParser implements IHTMLParser {
@Override
public BaseObject parseHTML(String htmlToParse) {
BlogResponse response = new BlogResponse();
try {
Document doc = Jsoup.parse(htmlToParse);
response.setPosts(new ArrayList());
Smartjop post;
Elements anchors = doc.getElementsByTag("title");
for (Element anchor : anchors) {
post = new Smartjop();
post.setURL(anchor.attr("href"));
post.setText(anchor.text());
response.getPosts().add(post);
}
} catch (Exception exception) {
exception.printStackTrace();
}
return response;
}
}

Cuối cùng là MainActivity

ở đây tôi sử dụng listview để show dữ liệu .Các bạn chưa rõ về listview ,adapter có thể vào đây ,một bài viết khá đầy đủ của một dev nữ rất xinh gái :)) để tham khảo nhé

Tôi sử dụng url để parsing “http://bigidol.vn/su-kien/57f74bd1a56da1e02756a9f2.html”

package vn.com.vdc.myapplication;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

import vn.com.vdc.myapplication.common.BaseApplication;
import vn.com.vdc.myapplication.entities.BlogResponse;
import vn.com.vdc.myapplication.entities.Smartjop;
import vn.com.vdc.myapplication.parsers.SmartjobParser;
import vn.com.vdc.myapplication.webrequesthandler.BaseHttpRequest;
import vn.com.vdc.myapplication.webrequesthandler.IAsyncCallback;
import vn.com.vdc.myapplication.webrequesthandler.WebResponse;

public class MainActivity extends FragmentActivity {

Button button;
ListView listView;
BlogResponse blogPosts;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
listView = (ListView) findViewById(R.id.listView);

button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
BaseHttpRequest request = new BaseHttpRequest(MainActivity.this, "http://bigidol.vn/su-kien/57f74bd1a56da1e02756a9f2.html");
request.setHtmlParser(new SmartjobParser());
IAsyncCallback callback = new IAsyncCallback() {
@Override
public void onComplete(WebResponse responseContent) {
blogPosts = (BlogResponse) responseContent.getTypedObject();
listView.setAdapter(new SmartjobAdapter(MainActivity.this));
}

@Override
public void onError(String errorData) {
Toast.makeText(MainActivity.this, errorData, Toast.LENGTH_SHORT).show();
}
};
request.execute(callback, BaseApplication.getInstance().getRequestQueue());
} catch (Exception e) {
Toast.makeText(MainActivity.this, "Some error occurred.", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
});

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
try {
MainActivity.this.startActivity(new Intent(Intent.ACTION_VIEW, Uri
.parse(blogPosts.getPosts().get(i).getURL())));
} catch (Exception exception) {
exception.printStackTrace();
}
}
});
}

static class ViewHolder {
TextView post;
}

public class SmartjobAdapter extends BaseAdapter {
Activity context;
ArrayList arrayList;
LayoutInflater inflater;

public SmartjobAdapter(Activity context) {
super();
this.context = context;
this.arrayList = blogPosts.getPosts();
inflater = context.getLayoutInflater();
}

public int getCount() {
return arrayList.size();
}

public Smartjop getItem(int position) {
return arrayList.get(position);
}

public long getItemId(int position) {
return 0;
}

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
Smartjop metaData = getItem(position);
if (convertView == null) {
convertView = inflater.inflate(R.layout.r_blogpost, parent, false);
holder = new ViewHolder();

holder.post = (TextView) convertView
.findViewById(R.id.post);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}

holder.post.setText(metaData.getText());
return convertView;
}

}
}

Kết quả hoàn thành

Toàn bộ source code, các bạn có thể download ở đây

Phần body chủ yếu code hơi ít chữ  , các bạn thông cảm nhé.Bài tiếp theo chúng ta cùng thảo luận về Fragment và Activity. Sử dụng sao cho hợp lý :))

Cảm ơn các bạn quan tâm

Bài viết gốc được đăng tải tại smartjob.vn

Có thể bạn quan tâm:

Xem thêm IT Jobs Developer hấp dẫn trên Station D

Bài viết liên quan

Ngành IT: Làm việc “trên mây” kiếm nhiều tiền nhất hiện nay

Ngành IT: Làm việc “trên mây” kiếm nhiều tiền nhất hiện nay

Kết quả từ cuộc khảo sát đầu năm của Station D về lương bổng của lập trình viên cho thấy nhiều thay đổi đã và đang diễn ra trong ngành IT – cuộc khảo sát tập trung vào các câu hỏi về khối lượng công việc, triển vọng cũng như...

By stationd
Đâu chỉ mỗi Bitcoin, công nghệ Blockchain còn nhiều ứng dụng hơn thế!

Đâu chỉ mỗi Bitcoin, công nghệ Blockchain còn nhiều ứng dụng hơn thế!

Khi nhắc đến blockchain , lập tức mọi người thường nghĩ ngay đến các loại tiền mã hóa, chẳng hạn như bitcoin. Tuy nhiên, blockchain lại là công nghệ tạo ra tiền mã hóa nhưng bản thân công nghệ này không phải là tiền mã hóa như cách mà chúng...

By stationd
Mock phương thức static trong Unit Test sử dụng PowerMock

Mock phương thức static trong Unit Test sử dụng PowerMock

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh Trong bài viết này, mình sẽ hướng dẫn các bạn Mock các phương thức static trong Unit Test các bạn nhé! Nếu bạn nào chưa biết về Mock trong Unit Test thì mình có thể nói sơ qua...

By stationd