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 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? Html, jsoup…
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:
- [Python cơ bản thường dùng trong công việc] Phần 8 : Xử lý file XML
- Định dạng chuẩn và quy ước viết code trong HTML5
- HTML5 khác HTML như thế nào?
Xem thêm IT Jobs Developer hấp dẫn trên Station D