抓取网页数据并解析

这天遇到这样一个需求:这种页面数据可以抓取吗?

抓取网页数据并解析_第1张图片


随后提供了账号、密码和网站地址:

帐号:kytj1    

密码:******************    

登陆地址:http://student.tiaoji.kaoyan.com/tjadm


主要思路:

1、使用Fiddler4分析http请求交互方式,包括数据发送方式(POST或GET),携带参数等,获得返回的数据信息

2、用Android程序模拟HTTP请求

3、用Java解析HTML代码,提取出对应的姓名、报考学校、报考专业、分数、联系电话、发布时间等字段

4、把txt文件导入到Excel里,待进一步处理。


用Fiddle查看数据包


1、打开Fiddler

抓取网页数据并解析_第2张图片

2、打开网站,填入用户名和密码,点击登录

登陆地址:http://student.tiaoji.kaoyan.com/tjadm

抓取网页数据并解析_第3张图片

3、观察Filldder抓到的包

可以看到HOST、URL、POST方式以及明文密码

抓取网页数据并解析_第4张图片

抓取网页数据并解析_第5张图片

4、观察网页数据

登录成功后,网页数据显示为

抓取网页数据并解析_第6张图片

   对应的Filldder抓包数据为抓取网页数据并解析_第7张图片


可以看到请求的HOST以及URL,方式为GET,返回的数据也可以在body体中获取到。

5、HTML代码

返回的HTML页面代码为(选取了部分)






考研调剂中心_考研调剂意向发布系统_考研调剂_考研网(kaoyan.com)







报考专业:
考生调剂信息
退出
姓名 报考学校 报考专业 专业门类 总分 政治 外语 专一 专二 电话 邮箱 调剂意向 发布时间
李*** 天津大学 应用化学 工学 244 58 53 133 0 15********15 希望能调剂到211或者985院校,只要是与化学相关的都服从调剂 2016-03-01
何*** 安徽大学 中国现当代文学 137 71 66 0 0 18********74 服从调剂 2016-03-01
[每页显示:20条/总共:161659条] 上一页 1 2 3 4 5 6 7 8 9 10 ... 8082 8083 下一页


要做的就是从以下格式的HTML代码中解析出需要的数据

抓取网页数据并解析_第8张图片


抓取网页数据并解析_第9张图片

Android程序模拟HTTP请求

经过上述分析,清楚了HTTP的请求地址、请求方式和携带参数格式,所以接下来要开发Android程序编程实现这个过程。
(不一定非要Android实现,在PC上直接实现应该也是可以的。但本人比较熟悉Android上的一个HTTP开发库,所以计划Android平台实现)。
1、打开Eclipse,新建一个工程TestGet,把实现HTTP库的代码拷入工程中,
使用的库 android-async-http
官网源码: https://github.com/loopj/android-async-http
官网教程: http://loopj.com/android-async-http/

这个网络请求库是基于Apache HttpClient库之上的一个异步网络请求处理库,网络处理均基于Android的非UI线程,通过回调方法处理请求结果。

工程目录如下,其中com.loopj.android.http包就是android-async-http的源码
抓取网页数据并解析_第10张图片

2、 新建XcAsyncHttpClientUtil.java,添加请求URL地址,封装AsyncHttpClient的GET和POST请求
package com.example.testget;

import org.apache.http.HttpEntity;

import android.content.Context;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;

public class XcAsyncHttpClientUtil {
	public static final String BASE_URL = "http://ntiaoji.kaoyan.com";
	public static final String LOGIN_URL = "/tjadm/login";
	public static final String INDEX1 = "/tjadm/1.html";
	private static AsyncHttpClient client = new AsyncHttpClient();

	public static void get(String url, RequestParams params,
			AsyncHttpResponseHandler responseHandler) {
		client.get(getAbsoluteUrl(url), params, responseHandler);
	}

	public static void post(String url, RequestParams params,
			AsyncHttpResponseHandler responseHandler) {
		client.post(getAbsoluteUrl(url), params, responseHandler);
	}

	public static void post(Context context, String url, HttpEntity entity,
			AsyncHttpResponseHandler responseHandler) {
		client.post(context, getAbsoluteUrl(url), entity, "", responseHandler);
	}

	public static String getAbsoluteUrl(String relativeUrl) {
		return BASE_URL + relativeUrl;
	}
}

3、编辑activity_main.xml,添加两个按钮,一个登陆,一个获取表格数据


    

    
    
     

效果图如下:
抓取网页数据并解析_第11张图片

4、编辑MainActivity.java,添加按钮点击动作,dologin()用来实现登陆,doGetData()用来获取表格数据,参数page用来构建请求的URL,初始化值为1,可自增,获取其他页面的数据
@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn = (Button) findViewById(R.id.btn);
		btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				dologin();
			}
		});
		btn1 = (Button) findViewById(R.id.btn1);
		btn1.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				doGetData();
			}
		});
	}
	
	private void dologin() {
		RequestParams params = new RequestParams();
		params.put("username", "kytj1");
		params.put("password", "***********");
		XcAsyncHttpClientUtil.post(XcAsyncHttpClientUtil.LOGIN_URL, params,
				new AsyncHttpResponseHandler() {
					@Override
					public void onSuccess(int statusCode, Header[] headers,
							byte[] responseBody) {
						try {
							String jsonString = new String(responseBody,
									"UTF-8");
							Log.e("TAG", jsonString);

						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}
					}

					@Override
					public void onFailure(int statusCode, Header[] headers,
							byte[] responseBody, Throwable error) {
						Log.e("Login", "onFailure");
					}
				});
	}
	protected void doGetData() {
		RequestParams params = new RequestParams();
		XcAsyncHttpClientUtil.get("/tjadm/" + page + ".html", params,
				new AsyncHttpResponseHandler() {
					@Override
					public void onSuccess(int statusCode, Header[] headers,
							byte[] responseBody) {
						try {
							String jsonString = new String(responseBody,
									"UTF-8");
							parse(jsonString);
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}
					}

					@Override
					public void onFailure(int statusCode, Header[] headers,
							byte[] responseBody, Throwable error) {
					}
				});
	}
取到网页数据内容还不算完,还得解析出所需数据。
doGetData()取到的respoBody由UTF-8编码,解码后得到字符串格式数据
交由parse()解析。

Java解析HTML数据

之前没有做过如何解析HTML数据,开始还有点头疼,不知道如何下手,在网上搜索解决办法。然后发现了这个库
jsoup

jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
官方网站: http://jsoup.org/

点击下载jsoup库

把下载到的jsoup-1.8.3.jar库添加到Android工程libs文件夹下

解析如下HTML数据
抓取网页数据并解析_第12张图片 抓取网页数据并解析_第13张图片

原数据里table下有21条数据,第1条为表格title信息,如姓名、报考学校、报考专业等字段,第2-21条为实际的学生信息。
Java代码如下:
protected void parse(String html) {
		Document doc = Jsoup.parse(html);
		Element tiaojiTab = doc.select("table.tiaoji-tab").first();
		Elements lists = tiaojiTab.getElementsByTag("tr");
		int size = lists.size();
		for (int i = 1; i < size; i++) {
			Element item = lists.get(i);
			Elements els = item.getElementsByTag("td");
			String all = "";
			for (int j = 0; j < els.size(); j++) {
				Element value = els.get(j);
				String text = value.text();
				all = all + text + "#";
			}
			initData(all);
			Log.e("tag", all);
		}
		page++;
		if (page < totalsize + 1) {
			doGetData();
		} else {
			page = 1;
		}
	}
doc.select("table.tiaoji-tab").first();
从整个HTML文档里取出要解析的内容信息,根据“tr”取得元素组,从第2条开始取数据,调用for循环。
对于单个字段间添加“#”号,以方便后续在Excel中处理数据。
initData(all)把单独html页上取得的数据写到txt文件里。
page++;
		if (page < totalsize + 1) {
			doGetData();
		} else {
			page = 1;
		}
继续取下一页,设置的totalSize=200,即每运行一次程序,抓取200页数据。

把数据写入到本地txt文件里代码:
private void initData(String msg) {
		String filePath = "/sdcard/Test/";
		String fileName = "tiaoji.txt";
		makeFilePath(filePath, fileName);
		writeTxtToFile(msg, filePath, fileName);
	}

	// 将字符串写入到文本文件中
	public void writeTxtToFile(String strcontent, String filePath,
			String fileName) {
		// 生成文件夹之后,再生成文件,不然会出错
		String strFilePath = filePath + fileName;
		// 每次写入时,都换行写
		String strContent = strcontent + "\r\n";
		try {
			File file = new File(strFilePath);
			if (!file.exists()) {
				Log.d("TestFile", "Create the file:" + strFilePath);
				file.getParentFile().mkdirs();
				file.createNewFile();
			}
			RandomAccessFile raf = new RandomAccessFile(file, "rwd");
			raf.seek(file.length());
			raf.write(strContent.getBytes());
			raf.close();
		} catch (Exception e) {
			Log.e("TestFile", "Error on write File:" + e);
		}
	}

	// 生成文件
	public File makeFilePath(String filePath, String fileName) {
		File file = null;
		makeRootDirectory(filePath);
		try {
			file = new File(filePath + fileName);
			if (!file.exists()) {
				file.createNewFile();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return file;
	}

	// 生成文件夹
	public static void makeRootDirectory(String filePath) {
		File file = null;
		try {
			file = new File(filePath);
			if (!file.exists()) {
				file.mkdir();
			}
		} catch (Exception e) {
			Log.i("error:", e + "");
		}
	}


导入txt到Excel

1、连接手机到电脑,打开应用宝,工具箱里选择文件管理,把txt文件导入到电脑上
2、打开Excel,选择数据-自文本
抓取网页数据并解析_第14张图片

按照提示,选择导出的txt文件,第2步中分隔符号选择其他,填入“#”,再完成

这样,就把数据成功的在Excel中显示了。


完整MainActivity.java如下:
package com.example.testget;

import java.io.File;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;

import org.apache.http.Header;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;

public class MainActivity extends Activity {
	private Button btn, btn1;
	private int page = 1;
	private static final int totalsize = 200;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn = (Button) findViewById(R.id.btn);
		btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				dologin();
			}
		});
		btn1 = (Button) findViewById(R.id.btn1);
		btn1.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				doGetData();
			}
		});
	}
	
	private void dologin() {
		RequestParams params = new RequestParams();
		params.put("username", "kytj1");
		params.put("password", "************");
		XcAsyncHttpClientUtil.post(XcAsyncHttpClientUtil.LOGIN_URL, params,
				new AsyncHttpResponseHandler() {
					@Override
					public void onSuccess(int statusCode, Header[] headers,
							byte[] responseBody) {
						try {
							String jsonString = new String(responseBody,
									"UTF-8");
							Log.e("TAG", jsonString);

						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}
					}

					@Override
					public void onFailure(int statusCode, Header[] headers,
							byte[] responseBody, Throwable error) {
						Log.e("Login", "onFailure");
					}
				});
	}
	protected void doGetData() {
		RequestParams params = new RequestParams();
		XcAsyncHttpClientUtil.get("/tjadm/" + page + ".html", params,
				new AsyncHttpResponseHandler() {
					@Override
					public void onSuccess(int statusCode, Header[] headers,
							byte[] responseBody) {
						try {
							String jsonString = new String(responseBody,
									"UTF-8");
							parse(jsonString);
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}
					}

					@Override
					public void onFailure(int statusCode, Header[] headers,
							byte[] responseBody, Throwable error) {
					}
				});
	}

	protected void parse(String html) {
		Document doc = Jsoup.parse(html);
		Element tiaojiTab = doc.select("table.tiaoji-tab").first();
		Elements lists = tiaojiTab.getElementsByTag("tr");
		int size = lists.size();
		for (int i = 1; i < size; i++) {
			Element item = lists.get(i);
			Elements els = item.getElementsByTag("td");
			String all = "";
			for (int j = 0; j < els.size(); j++) {
				Element value = els.get(j);
				String text = value.text();
				all = all + text + "#";
			}
			initData(all);
			Log.e("tag", all);
		}
		page++;
		if (page < totalsize + 1) {
			doGetData();
		} else {
			page = 1;
		}
	}

	private void initData(String msg) {
		String filePath = "/sdcard/Test/";
		String fileName = "tiaoji.txt";
		makeFilePath(filePath, fileName);
		writeTxtToFile(msg, filePath, fileName);
	}

	// 将字符串写入到文本文件中
	public void writeTxtToFile(String strcontent, String filePath,
			String fileName) {
		// 生成文件夹之后,再生成文件,不然会出错
		String strFilePath = filePath + fileName;
		// 每次写入时,都换行写
		String strContent = strcontent + "\r\n";
		try {
			File file = new File(strFilePath);
			if (!file.exists()) {
				Log.d("TestFile", "Create the file:" + strFilePath);
				file.getParentFile().mkdirs();
				file.createNewFile();
			}
			RandomAccessFile raf = new RandomAccessFile(file, "rwd");
			raf.seek(file.length());
			raf.write(strContent.getBytes());
			raf.close();
		} catch (Exception e) {
			Log.e("TestFile", "Error on write File:" + e);
		}
	}

	// 生成文件
	public File makeFilePath(String filePath, String fileName) {
		File file = null;
		makeRootDirectory(filePath);
		try {
			file = new File(filePath + fileName);
			if (!file.exists()) {
				file.createNewFile();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return file;
	}

	// 生成文件夹
	public static void makeRootDirectory(String filePath) {
		File file = null;
		try {
			file = new File(filePath);
			if (!file.exists()) {
				file.mkdir();
			}
		} catch (Exception e) {
			Log.i("error:", e + "");
		}
	}

}






哈哈哈哈哈哈~

你可能感兴趣的:(Android开发(原创))