摘要:基于 Jsoup 实现一个 Android 的网络爬虫程序,抓取网页的内容并显示出来。写这个程序的主要目的是抓取海投网的宣讲会信息(公司、时间、地点)并在移动端显示,这样就能够随时随地的浏览在学校举办的宣讲会信息了。html
Jsoup 是一个 Java 的开源HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套很是方便的API,可经过DOM,CSS以及相似于jQuery的操做方法来取出和操做数据。java
Jsoup主要有如下功能:node
从一个URL,文件或字符串中解析HTML;android
使用DOM或CSS选择器来查找、取出数据;git
对HTML元素、属性、文本进行操做;github
清除不受信任的HTML (来防止XSS攻击)浏览器
好了,下面写几段代码来讲明 Jsoup 是如何优雅的进行 HTML 文档处理的。首先,咱们须要去Jsoup官网 下载Jsoup的jar
包,而后加入项目的依赖库中。网络
Jsoup 能够从一个字符串、文件或者一个 URL 中解析HTML,解析的目的主要是为了获得一个干净完整的解析结果,并生成 Document 对象实例。多线程
// Parse a document from a String String html = "<html><head><title>神奕的博客</title></head>" +"<body><p>搭个博客写学习笔记!!</p></body></html>"; Document doc = Jsoup.parse(html); // Load a Document from a File File input = new File("D://a.html"); Document doc = Jsoup.parse(input, "UTF-8"); // Load a Document from a URL Document doc = Jsoup.connect("http://example.com/").get();
当加载和解析一个本地的HTML文件时,若是在加载文件的时候发生错误,将抛出 IOException,应做适当处理。dom
将HTML解析成一个Document
以后,就可使用传统的 DOM 方法进行数据抽取。例如:
// 海投网 String url = "http://xjh.haitou.cc/wh/uni-1/after/hold/page-1/"; Document doc = Jsoup.connect(url).get(); Elements elements = doc.getElementsByTag("company"); for(Element e : elements) { System.out.println(e.text()); }
Document 对象和 Elements 对象提供了一系列相似于DOM的方法来查找元素,好比 getElementById(String id)、getElementsByTag(String tag) 等等。更多方法请看《Jsoup Cookbook》。
另外,还可使用 Selector 选择器(相似于CSS或jQuery语法)来查找元素。以下:
// 海投网 String url = "http://xjh.haitou.cc/wh/uni-1/after/hold/page-1/"; Document doc = Jsoup.connect(url).get(); // 经过标签company查找元素 Elements company = doc.select("company"); // 带有href属性的a元素 Elements links = doc.select("a[href]"); // 扩展名为.png的图片 Elements pngs = doc.select("img[src$=.png]"); // class等于content的div标签 Element content = doc.select("div.content").first();
选择器实现了很是强大和灵活的查找功能。select
方法在Document、Element 或 Elements 对象中均可以使用,且是上下文相关的,所以可实现指定元素的过滤或者链式选择访问。select
方法将返回一个Elements集合,并提供一组方法来抽取和处理结果。
经过 DOM 方法或者 Selector 方法查找到一些 Elements 元素以后,咱们须要从这些元素中取得数据,下面是几个经常使用的方法:
取得一个属性的值,可使用Node.attr(String key)
方法;
取得一个元素中的文本,可使用Element.text()
方法;
取得元素或属性中的HTML内容,可用Element.html()
或Node.outerHtml()
方法
取得一个元素的 id :Element.id()
取得一个元素的标签名:Element.tagName()
取得一个元素的类名:Element.className()
在解析一个 Document 以后可能想修改其中的某些属性值、HTML或文本内容,而后再保存到磁盘或都输出到前台页面。例如:咱们能够为文档中的全部图片增长可点击连接、修改连接地址或者是修改文本等。Jsoup 提供了不少方法用来进行修改,这里就不列举了,请移步 Jsoup Cookbook。
海投网是一个为高校毕业生服务的招聘信息网,创始人是华中科技大学的毕业生。如今我要抓取在华中科技大学举办的宣讲会的信息,网页以下图:
查看网页源代码,以下图:
能够看出,公司名是在一个 company 标签内,宣讲会时间是在一个类名为 text-center 的 td 标签内,学校的具体地点则是在一个类名为 preach-tbody-addre 的 td 标签内。这么一分析,要提取华中科技大学的宣讲会信息就变得挺简单了。
Java代码以下:
import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { String url = "http://xjh.haitou.cc/wh/uni-1/after/hold/page-1/"; Connection conn = Jsoup.connect(url); // 修改http包中的header,假装成浏览器进行抓取 conn.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:32.0) Gecko/ 20100101 Firefox/32.0"); Document doc = conn.get(); // 获取tbody元素下的全部tr元素 Elements elements = doc.select("tbody tr"); for(Element element : elements) { String companyName = element.getElementsByTag("company").text(); String time = element.select("td.text-center").first().text(); String address = element.getElementsByClass("preach-tbody-addre").text(); System.out.println("公司:"+companyName); System.out.println("宣讲时间:"+time); System.out.println("宣讲学校:华中科技大学"); System.out.println("具体地点:"+address); System.out.println("---------------------------------"); } } }
某些网站禁止爬虫,不能抓取或者抓取必定数量后封IP。这时候咱们须要假装成浏览器进行抓取,这能够经过修改http包中的header来实现(设置User-Agent)。运行上面的程序获得输出结果:
公司:瑞声科技(常州)有限公司 宣讲时间:2015-03-07 19:00(周六) 宣讲学校:华中科技大学 具体地点:大学生活动中心305阶梯教室 --------------------------------- 公司:普联技术有限公司 宣讲时间:2015-03-08 19:00(周日) 宣讲学校:华中科技大学 具体地点:大学生活动中心305阶梯教室 --------------------------------- 公司:大联大投资控股股份有限公司 宣讲时间:2015-03-09 09:30(周一) 宣讲学校:华中科技大学 具体地点:大学生活动中心305阶梯教室 --------------------------------- ......
开发 Android 程序,你须要搭建开发环境,很简单:先安装Java的JDK(最好不低于1.6),而后去Android官网下载并安装 Android Studio 就好了。
在Android程序中使用 Jsoup 须要注意两点:
在AndroidManifest.xml文件中添加网络访问权限android.permission.INTERNET
Android在4.0以后,不容许在主线程里执行网络(http)请求,也就是说 Jsoup 的代码须要写在子线程里。
4.0 版本之后,若是你在主线程里尝试进行网络操做,会报android.os.NetworkOnMainThreadException 的异常。因此咱们须要开辟子线程进行异步加载,用到Thread
、Runnable
、Handler
这三个类:
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.share_mblog_view); // 开辟一个线程 new Thread(runnable).start(); } Runnable runnable = new Runnable(){ @Override public void run() { /** * 要执行的操做 */ // 执行完毕后给handler发送一个空消息 handler.sendEmptyMessage(0); } } Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); /** * 处理UI */ // 当收到消息时就会执行这个方法 } }
若是在没有可用网络的状况下执行网络爬虫程序,App将会报错。因此在每次执行以前都应该先判断网络是否可用。大体步骤以下:
① 获取ConnectivityManager对象
Context context = activity.getApplicationContext(); // 获取手机全部链接管理对象(包括对wi-fi,net等链接的管理) ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);② 获取NetworkInfo对象
NetworkInfo info = cm.getActiveNetworkInfo();③ 判断网络类型,Android的网络分为两大类:WIFI 和 手机网络
// WIFI 断定条件 info != null && info.getType() == ConnectivityManager.TYPE_WIFI // 手机网络 断定条件 info !=null && info.getType() == ConnectivityManager.TYPE_MOBILE
而手机网络具体又分为不少类,好比移动3G、移动2G、联通2G等等。这里就不说了,自行Google。
④ 判断网络链接是否可用(包括全部网络类型)
public boolean isNetworkAvailable(Activity activity) { Context context = activity.getApplicationContext(); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (cm == null) return false; else { // 获取全部NetworkInfo对象 NetworkInfo[] networkInfo = cm.getAllNetworkInfo(); if (networkInfo != null && networkInfo.length > 0) { for (int i = 0; i < networkInfo.length; i++) if (networkInfo[i].getState() == NetworkInfo.State.CONNECTED) return true; // 存在可用的网络链接 } } return false; }注意,上述操做须要在 AndroidManifest.xml 文件中添加访问网络状态的权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
另外,本程序在 UI 界面开发上涉及到 Android 中的 ListView(显示)、PopupWindow(菜单)、ProgressDialog(加载)、AlertDialog(提示)等控件的使用。由于本文并非讨论 Android 控件的使用,在这里就不赘述了。
源码下载:https://github.com/SongLee24/android-crawler