准备资料
jsoup 是一款 Java 的HTML 解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于JQuery的操作方法来取出和操作数据。
jsoup的主要功能如下:
- 从一个URL,文件或字符串中解析HTML;
- 使用DOM或CSS选择器来查找、取出数据;
- 可操作HTML元素、属性、文本;
如果对jsoup不熟悉,请移步
http://jsoup.org/
JDIC 全程是 JDesktop Integration Components 目的是构建消除本机应用程序和 Java 等价物之间差距的组件。JDIC 单一的 Java API 允许应用程序接进本机操作系统特性,同时保持跨平台支持。它目前提供了本机 Web 浏览器(Internet Explorer 或 Mozilla) 支持、系统托盘支持、文件扩展集成和其他桌面特性。
另,刚刚找到的一篇好文章,Java 网页浏览器组件介绍:
http://www.ibm.com/developerworks/cn/java/j-lo-browser/index.html
动态核心
jsoup 的解析很好用,但是jsoup不能解析动态的代码,于是就有了
JDIC调用ie内核,然后执行
String jscript = "function getAllHtml() {"+
"var a='';" +
"a = '<html><head><title>';" +
"a += document.title;"+
"a += '</title></head>';"+
"a += document.body.outerHTML;"+
"a += '</html>';"+
"return a;"+
"}"+
"getAllHtml();";
String result = webBrowser.executeScript(jscript);
这段代码得到当前的html,然后交由jsoup 进行解析
Document doc=Jsoup.parse(result);
例子
示例目标
:
招聘网站,希望出来的结果按照公司规模来排序
购物网站,希望商品按照评论的多少来排序
图片搜索,快速保存所有的结果
。。。。。。
下面的演示实现了找出标题,百度图片搜索的前2页,智联招聘的前6页
package ins1000.main;
import ins1000.dialect.DefiniteUrl;
import ins1000.dialect.impl.CopyOfDefiniteUrl_zhilianzhaoping;
import ins1000.dialect.impl.DefiniteUrl_baiduMap;
import ins1000.util.BrowserReadHtml;
import java.util.ArrayList;
import java.util.List;
/**
* 以网页翻页为例子
* @author Administrator
*
*/
public class Main{
static List<DefiniteUrl> definiteUrls=new ArrayList<DefiniteUrl>();
static{
definiteUrls.add(new DefiniteUrl_baiduMap());
definiteUrls.add(new CopyOfDefiniteUrl_zhilianzhaoping());
}
public static void main(String[] args) throws Exception {
for(DefiniteUrl du:definiteUrls){
BrowserReadHtml brh= new BrowserReadHtml(du);
brh.begin();
}
}
public static void finish(DefiniteUrl du) {
definiteUrls.remove(du);
if(definiteUrls.size()==0){
System.exit(0);
}
}
}
主类,扩展的时候直接添加
definiteUrls.add(new xxx());即可
package ins1000.util;
import ins1000.dialect.DefiniteUrl;
import java.awt.BorderLayout;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jdesktop.jdic.browser.WebBrowser;
import org.jdesktop.jdic.browser.WebBrowserEvent;
import org.jdesktop.jdic.browser.WebBrowserListener;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
/**
* 动态读取页面的html
* @author ckf
*
*/
public class BrowserReadHtml {
private DefiniteUrl definiteUrl;
public BrowserReadHtml(DefiniteUrl definiteUrl) {
this.definiteUrl=definiteUrl;
}
private JFrame frame;
private JPanel panel_name=new JPanel();
private WebBrowser webBrowser = new WebBrowser();
public void begin() throws Exception{
initwebBrowser();
frame = new JFrame("Browser Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(webBrowser);
frame.pack();
frame.setSize(900,500);
frame.setLocation((int)(100*Math.random()), (int)(100*Math.random()));
frame.setVisible(definiteUrl.isVisible());
}
int begincount;
private void initwebBrowser() throws Exception{
panel_name.add(webBrowser, BorderLayout.CENTER);
webBrowser .setURL(new URL(definiteUrl.getUrl()));
webBrowser .addWebBrowserListener(new WebBrowserListener() {
public void documentCompleted(WebBrowserEvent event) {
if(begincount==0){
getThisPageResult();
}
begincount++;
}
public void downloadStarted(WebBrowserEvent event) {}
public void downloadCompleted(WebBrowserEvent event) { }
public void downloadProgress(WebBrowserEvent event) { }
public void downloadError(WebBrowserEvent event) { }
public void titleChange(WebBrowserEvent event) { }
public void statusTextChange(WebBrowserEvent event) { }
public void windowClose(WebBrowserEvent arg0) { }
});
}
private void callback(String result) {
Document doc=Jsoup.parse(result);
definiteUrl.page(doc);
if(definiteUrl.isEndPage(doc)){
frame.dispose();
definiteUrl.finish();
}else{
webBrowser.executeScript(definiteUrl.getNextPageJavaScript(doc));
getThisPageResult();
}
}
private void getThisPageResult() {
Timer timer = new Timer(false);
timer.schedule(new AllTask(), 1 * 1000);
}
class AllTask extends TimerTask {
public void run() {
String jscript = "function getAllHtml() {"+
"var a='';" +
"a = '<html><head><title>';" +
"a += document.title;"+
"a += '</title></head>';"+
"a += document.body.outerHTML;"+
"a += '</html>';"+
"return a;"+
"}"+
"getAllHtml();";
String result = webBrowser.executeScript(jscript);
callback(result);
}
}
}
这个类是调用ie浏览器,执行javascript代码,可以不管
package ins1000.dialect;
import ins1000.main.Main;
import org.jsoup.nodes.Document;
/**
* 抽象类,每个具体的网站都要继承此类
* @author Administrator
*
*/
public abstract class DefiniteUrl{
private String url;
private boolean visible;
public DefiniteUrl(String url){
this.url=url;
visible=true;
}
public abstract String getNextPageJavaScript(Document doc);
public abstract boolean isEndPage(Document doc);
public abstract void page(Document doc);
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public void finish() {
Main.finish(this);
}
}
这个是抽象类,每个网站要实现对应的方法
getNextPageJavaScript下一页javascript代码,可以是点击下一页按钮,也可以是直接换url
isEndPage是否为最后一页
page当前页面最终的html代码(动态的html代码)
//以下为具体的实现类,扩展的时候直接继承一个DefiniteUrl
package ins1000.dialect.impl;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import ins1000.dialect.DefiniteUrl;
public class DefiniteUrl_baiduMap extends DefiniteUrl{
public DefiniteUrl_baiduMap() {
//设置网站入口地址
super("http://image.baidu.com/i?ct=201326592&cl=2&lm=-1&tn=baiduimage&istype=2&fm=index&pv=&z=0&word=%C2%E3%BB%E9%CA%B1%B4%FA&s=0");
//设置窗口是否可视化,默认为true
//setVisible(false);
}
private int count=1;
@Override
public String getNextPageJavaScript(Document doc) {
count++;
Element el= doc.select("#pgw").select("a").last();
return el.attr("onclick");
}
@Override
public boolean isEndPage(Document doc) {
if(count>=2){
return true;
}else{
return false;
}
}
@Override
public void page(Document doc) {
Elements els= doc.select("#imgid").select("dl");
for(Element el:els){
Element img=el.select("img").first();
Element link=el.select("dt").select("a").first();
System.out.println(link.text()+"=======>"+img.absUrl("src"));
}
}
}
package ins1000.dialect.impl;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import ins1000.dialect.DefiniteUrl;
public class CopyOfDefiniteUrl_zhilianzhaoping extends DefiniteUrl{
public CopyOfDefiniteUrl_zhilianzhaoping() {
//设置网站入口地址
super("http://sou.zhaopin.com/jobs/jobsearch_jobtype.aspx?in=210500&jl=%E6%B7%B1%E5%9C%B3&kw=java&sm=1&p=1");
//设置窗口是否可视化,默认为true
//setVisible(false);
}
private int count=1;
@Override
public String getNextPageJavaScript(Document doc) {
String url="http://sou.zhaopin.com/jobs/jobsearch_jobtype.aspx?in=210500&jl=%E6%B7%B1%E5%9C%B3&kw=java&sm=1&p="+count;
String next="window.location.href='"+url+"';";
count++;
return next;
}
@Override
public boolean isEndPage(Document doc) {
if(count>=6){
return true;
}else{
return false;
}
}
@Override
public void page(Document doc) {
Elements els= doc.select("#joblist").select("[class=trW2]");
for(Element el:els){
try {
System.out.print(el.select("a").first().text());
System.out.print("==========>");
System.out.println(el.select("a").eq(1).text());
} catch (Exception e) {
}
}
}
}
有图有真相
附件中有源码,eclipse导出
遗留问题
private void getThisPageResult() {
Timer timer = new Timer(false);
timer.schedule(new AllTask(), 1 * 1000);
}
class AllTask extends TimerTask {
public void run() {
String jscript = "function getAllHtml() {"+
"var a='';" +
"a = '<html><head><title>';" +
"a += document.title;"+
"a += '</title></head>';"+
"a += document.body.outerHTML;"+
"a += '</html>';"+
"return a;"+
"}"+
"getAllHtml();";
String result = webBrowser.executeScript(jscript);
callback(result);
}
上面是当前代码,求更好的解决方案
现在取得当前的html使用了定时器,1秒后执行,感觉很不精确,有没有什么更好的方式,比如判断当前页面所有的内容都已经加载完了,其它的javascript都已经执行完了的代码?