`

动态爬虫jsoup+jdic实现

阅读更多

准备资料

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

jsoup的主要功能如下:

  1. 从一个URL,文件或字符串中解析HTML;
  2. 使用DOM或CSS选择器来查找、取出数据;
  3. 可操作HTML元素、属性、文本;
如果对jsoup不熟悉,请移步http://jsoup.org/


JDIC 全程是 JDesktop Integration Components 目的是构建消除本机应用程序和 Java 等价物之间差距的组件。JDIC 单一的 Java API 允许应用程序接进本机操作系统特性,同时保持跨平台支持。它目前提供了本机 Web 浏览器(Internet Explorer 或 Mozilla) 支持、系统托盘支持、文件扩展集成和其他桌面特性。
svn地址,直接下了看例子吧:https://svn.java.net/svn/jdic~svn



另,刚刚找到的一篇好文章,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都已经执行完了的代码?

  • 大小: 241.3 KB
9
2
分享到:
评论
5 楼 faith789510 2013-03-18  
放到 documentCompleted  里面就完美了
4 楼 laitaiyang520 2011-12-20  
1202 写道
你好,我最近在学习使用jsoup,发现这样一个问题:
我像使用div.div.m_l_hot_im 来选取某个class为m_l_hot_im 的div,可结果是连class为div.m_l_hot_iml及div.m_l_hot_imr的所有的以div.m_l_hot_im 开头的都选择了,请问这是一个bug吗

不是,那相当于是一个模糊查询一样,用div[class=m_l_hot_im]就可以准确的选择到想要的数据模块
3 楼 1202 2011-08-29  
你好,我最近在学习使用jsoup,发现这样一个问题:
我像使用div.div.m_l_hot_im 来选取某个class为m_l_hot_im 的div,可结果是连class为div.m_l_hot_iml及div.m_l_hot_imr的所有的以div.m_l_hot_im 开头的都选择了,请问这是一个bug吗
2 楼 cjw974music_heart 2011-08-25  
(1)用的ie7为默认浏览器
先输出了4行 “isDefaultBrowserMozilla” (红色字体)
在“百度信息”和“智联信息”的输出之间,会输出:
Exception in thread "Thread-8" java.lang.NullPointerException: null pData
at sun.awt.windows.WComponentPeer.hide(Native Method)
at java.awt.Component.removeNotify(Component.java:5979)
at org.jdesktop.jdic.browser.WebBrowser.access$201(Unknown Source)
at org.jdesktop.jdic.browser.WebBrowser$1.run(Unknown Source)
(这个输出后来查了一下,好像是我电脑系统的问题,少了微软的运行环境,
只输出“百度”或“智联”就没有这个Exception )

(2)用火狐(3.6.3)为默认浏览器
先输出了2行“isDefaultBrowserMozilla”
然后是
“Can't execute the native embedded browser. Error message: java.io.IOException: 文件名、目录名或卷标语法不正确。”
下面就终止了。

(3)一个问题 “图片搜索,快速保存所有的结果” 在什么地方体现出来?

学习中 谢谢。
1 楼 rox 2011-07-20  
写的不错,谢谢!
这样说来,JDIC应该也可以实现网页快照图片了。呵呵!

相关推荐

Global site tag (gtag.js) - Google Analytics