摘要:基于 Jsoup 实现一个 Android 的网络爬虫程序,抓取网页的内容并显示出来。写这个程序的主要目的是抓取海投网的宣讲会信息(公司、时间、地点)并在移动端显示,这样就可以随时随地的浏览在学校举办的宣讲会信息了。

一、Jsoup简介

Jsoup 是一个 Java 的开源HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常方便的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

Jsoup主要有以下功能:

  • 从一个URL,文件或字符串中解析HTML;

  • 使用DOM或CSS选择器来查找、取出数据;

  • 对HTML元素、属性、文本进行操作;

  • 清除不受信任的HTML (来防止XSS攻击)

好了,下面写几段代码来说明 Jsoup 是如何优雅的进行 HTML 文档处理的。首先,我们需要去Jsoup官网 下载Jsoup的jar包,然后加入项目的依赖库中。

1) HTML解析

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,应作适当处理。

2) 数据提取

将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()

3) 数据修改

在解析一个 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程序中

开发 Android 程序,你需要搭建开发环境,很简单:先安装Java的JDK(最好不低于1.6),然后去Android官网下载并安装 Android Studio 就行了。

在Android程序中使用 Jsoup 需要注意两点:

  • 在AndroidManifest.xml文件中添加网络访问权限android.permission.INTERNET

  • Android在4.0之后,不允许在主线程里执行网络(http)请求,也就是说 Jsoup 的代码需要写在子线程里。

1) 多线程

4.0 版本以后,如果你在主线程里尝试进行网络操作,会报android.os.NetworkOnMainThreadException 的异常。所以我们需要开辟子线程进行异步加载,用到ThreadRunnableHandler这三个类:

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
*/
// 当收到消息时就会执行这个方法
}
}

2) 判断网络连接是否可用

如果在没有可用网络的情况下执行网络爬虫程序,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

个人站点:http://songlee24.github.com

Android网络爬虫程序(基于Jsoup)的更多相关文章

  1. R语言网络爬虫学习 基于rvest包

    R语言网络爬虫学习 基于rvest包 龙君蛋君:2015年3月26日 1.背景介绍: 前几天看到有人写了一篇用R爬虫的文章,感兴趣,于是自己学习了.好吧,其实我和那篇文章R语言爬虫初尝试-基于RVES ...

  2. 一只简单的网络爬虫(基于linux C/C++)————开篇

    最近学习开发linux下的爬虫,主要是参考了该博客及其他一些网上的资料.网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息 ...

  3. 为编写网络爬虫程序安装Python3.5

    1. 下载Python3.5.1安装包1.1 进入python官网,点击menu->downloads,网址:https://www.python.org/downloads/ 1.2 根据系统 ...

  4. 使用Python写的第一个网络爬虫程序

    今天尝试使用python写一个网络爬虫代码,主要是想訪问某个站点,从中选取感兴趣的信息,并将信息依照一定的格式保存早Excel中. 此代码中主要使用到了python的以下几个功能,因为对python不 ...

  5. 一只简单的网络爬虫(基于linux C/C++)————浅谈并发(IO复用)模型

    Linux常用的并发模型 Linux 下设计并发网络程序,有典型的 Apache 模型( Process Per Connection ,简称 PPC ), TPC ( Thread Per Conn ...

  6. Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

    学习Android有几个月了,最近喜欢上了网络编程,于是想通过Android写一些一个小程序用于连接外网.在这里非常感谢雪夜圣诞的支持,非常感谢,给我打开新的一扇门. 1.声明,本程序只能用于西南大学 ...

  7. 一只简单的网络爬虫(基于linux C/C++)————socket相关及HTTP

    socket相关 建立连接 网络通信中少不了socket,该爬虫没有使用现成的一些库,而是自己封装了socket的相关操作,因为爬虫属于客户端,建立套接字和发起连接都封装在build_connect中 ...

  8. Python 网络爬虫程序详解

    #!/usr/bin/python #调用python from sys import argv #导入sys是导入python解释器和他环境相关的参数 from os import makedirs ...

  9. 一只简单的网络爬虫(基于linux C/C++)————主事件流程

    该爬虫的主事件流程大致如下: 1.获取命令行参数,执行相应操作 2.读取配置文件,解析得到各种设置 3.载入各种模块 4.种子入队,开启DNS解析线程(原始队列不为空时解析) 5.创建epoll,开启 ...

随机推荐

  1. Discuz3.1登录QQ互联显示redirect uri is illegal(100010)的解决

    QQ互联最近也是BUG不断,引起了很多用户的不满,从早前关闭群组功能,到发布2.0接口标准,引发系列站长用户的连锁反应.而Discuz从X3开始,为了应对QQ互联的2.0接口标准,采用了云平台来整合Q ...

  2. 为什么java String是固定的 为什么字符串是不可变的

    String类不可变的好处 String是所有语言中最常用的一个类.我们知道在Java中,String是不可变的.final的.Java在运行时也保存了一个字符串池(String pool),这使得S ...

  3. 09Windows编程

    Windows编程 2.1      窗口 Windows应用程序一般都有一个窗口,窗口是运行程序与外界交换信息的界面.一个典型的窗口包括标题栏.最小化按钮.最大/还原按钮.关闭按钮.系统菜单图标.菜 ...

  4. ubuntu apt-update NO_PUBKEY 40976EAF437D05B5 NO_PUBKEY 3B4FE6ACC0B21F32

    Fetched 28.1 MB in 11s (2344 kB/s) W: GPG error: http://archive.canonical.com xenial Release: The fo ...

  5. 【VScode】使用VScode 来写markdown时序图

    准备工作 在VScode中下载插件Markdown Preview Enhanced插件 创建一个.md文件 在VScode中打开文件,界面内点击右键可以看到Open preview to the s ...

  6. 用PHP怎么删除某目录下指定的一个文件

    举个tp框架的例子 $User = M("message"); $a = $User->]['url']; unlink($url); $User->delete($i ...

  7. css hack解决方案

    现在大家做项目的时候估计很多都已经不怎么考虑兼容问题,大多数的公司都已经舍弃ie7.8了,都是从ie9+开始,所以说不会有那么多的兼容问题需要去解决了,但是由于本人力求完美的工作习惯,做项目的时候还是 ...

  8. BZOJ 2502 Luogu P4843 清理雪道 最小流

    题意: 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定时清理雪道.你们拥有一架直升飞机 ...

  9. nginx网站标准配置

    #nginx开启的进程数worker_processes   4;     #4核CPU   #定义全局错误日志定义类型,[debug|info|notice|warn|crit]error_log  ...

  10. ubuntu14.04 Google Chrome can not be run as root

    问题如下图: