關於java實現需要登錄且帶驗證碼的定時網絡爬蟲(爬取的數據存庫)

 博主6月初的時候換了個工作,剛進來的時候什麼事沒有,愣是上班喝茶逛網站渡過了一週。那週週五的boss突然問我會不會爬蟲。
作爲一個才工作一年的javaer表示根本沒接觸過,但是那種情況下你還敢說不會麼,但是當時也不敢說的很絕對,因此就和boss就會一點。
當時就隱隱約約有爬蟲任務了,感覺週末去突擊了一下。果不其然,下週一的時候給我一個賬號和密碼,讓我每隔5分鐘爬取該網站的客戶
信息數據存到自己的數據庫,當時接到任務的時候一懵,和想象的不一樣啊,週末還要登錄,**的還是要驗證碼的登錄,並且還是定時的抓數據,
當時就一臉懵逼,還好咱會碼農最基本的技能--百度。經過幾天的各種百度之後,終於給做出來了。
        由於公司主體框架是基於註解(mybatis也是註解)的SSM框架,數據庫Mysql,因此博主也是採用了這套框架,由於是獨立發到一個服務器上,
不影響其他的代碼,因此博主就盡情發揮了,有些代碼寫的的確很爛,並且還遺留了幾個問題,什麼問題結尾再說,希望大家諒解,也看看能不能碰到大神幫我解決下。
        這裏先介紹下具體思路,寫一個登錄頁面,登錄之後爬蟲系統就自動運行,然後每隔5分鐘自抓取一次數據,一直死循環(想不到其他方法了...)第一個問題是解決怎麼模擬登錄目標網站的問題,
博士採用的是htmlunit這個框架,htmlunit可以模擬出瀏覽器頁面,使用它模擬出目標網站的登錄頁面,然後抓取它的登錄表單,獲取賬號、密碼、驗證碼輸入框和提交按鈕。
這樣就可以實現模擬登錄。對於驗證碼的策略是當你進入登錄頁面的時候就先發一個請求去獲取目標網站的驗證碼(博主的是圖片)存到服務器,並顯示在自己登錄頁的驗證碼框。
然後輸入賬號密碼和驗證碼登錄即可。下面附代碼。

  項目結構

   

  maven依賴

  <dependencies>
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-webmvc</artifactId>
  		<version>3.2.8.RELEASE</version>
  	</dependency>
  	<dependency>
  		<groupId>org.mybatis</groupId>
  		<artifactId>mybatis</artifactId>
  		<version>3.2.8</version>
  	</dependency>
  	<dependency>
  		<groupId>org.mybatis</groupId>
  		<artifactId>mybatis-spring</artifactId>
  		<version>1.2.3</version>
  	</dependency>
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-jdbc</artifactId>
  		<version>3.2.8.RELEASE</version>
  	</dependency> 

  	<dependency>
  		<groupId>commons-dbcp</groupId>
  		<artifactId>commons-dbcp</artifactId>
  		<version>1.4</version>
  	</dependency>
  	<dependency>
  		<groupId>junit</groupId>
  		<artifactId>junit</artifactId>
  		<version>4.9</version>
  	</dependency>
  	 <dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.1.37</version>
  	</dependency>
  	<dependency>
	    <groupId>org.jsoup</groupId>
	    <artifactId>jsoup</artifactId>
	    <version>1.8.2</version>
	</dependency>
	<dependency>
	    <groupId>net.sourceforge.htmlunit</groupId>
	    <artifactId>htmlunit</artifactId>
	    <version>2.16</version>
	</dependency>
	<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.21</version>
	</dependency>
  </dependencies>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
	xmlns:jee="http://www.springframework.org/schema/jee" 
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
		
		
	<!-- 配置組件掃描 -->
	<context:component-scan base-package="com.jds"/>
	
	<!-- 配置mvc註解掃描,識別@RequestMapping -->
	<mvc:annotation-driven/>
	
	<!-- 配置視圖解析器 -->
	 <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
         <!-- 在WEB-INF下 -->
        <property name="prefix" value=""/>
        <property name="suffix" value=".html"/>
    </bean>

	<!-- 讀取db.properties -->
	<util:properties id="jdbc" location="classpath:application.properties" />
	
	<!-- 配置連接池 -->
	<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="#{jdbc.driver}"/>
		<property name="url" value="#{jdbc.url}"/>
		<property name="username" value="#{jdbc.username}"/>
		<property name="password" value="#{jdbc.password}"/>
	</bean>
	
	<!-- 配置SqlSessionFactoryBean -->
	<bean id="ssfb" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 指定連接池 -->
		<property name="dataSource" ref="ds"/>
		<!-- 指定映射文件 
		<property name="mapperLocations" value="classpath:mapper/*.xml "/>
		-->
	</bean>
	
	<!-- 配置MapperScannerConfigurer -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 指定映射器所在包 -->
		<property name="basePackage" value="com/jds/repository"/>
	</bean>
	
	<!-- 配置springmvc的value註解 -->
	<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">  
        <property name="locations">  
            <list>  
                <value>classpath:application.properties</value>  
            </list>  
        </property>  
    </bean>  
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
   		 <property name="properties" ref="configProperties"/>
    </bean>
	
</beans>







application.properties

#數據庫連接
driver=com.mysql.jdbc.Driver
url=你的數據庫地址
username=你的賬號
password=你的密碼
#連接池
initSize=5
maxSize=10

#延時時間(秒)
delayTimes=300 
#首次讀取頁數
firstPage=3
#除首次每5分鐘讀取頁數
pages=1
#種類
type=AA22001,AA22002,AA22003,AA22004,AA22005,AA22006,AA22007,AA22008,AA22009

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>jds_pachong</display-name>
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:conf/applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

工具類

package com.jds.util;

public class StringUtil {

	/**
	 * 判斷空
	 * @param value
	 * @return
	 */
	 public static boolean isEmpty(String value) {
	        return value == null || "".equals(value.trim());
	    }
}
package com.jds.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

public class TimeUtils {
	
	/**
     * 獲得指定時間的時間戳
     * @param data
     * @return
     */
    public static int getTimeStampByDate(String data,String pattern){
 	   Date date = format(data, pattern);
 	   Long tm = date.getTime()/1000;
 	   return tm.intValue();
    }
    
    private static Date format(String str, String pattern) {

    	DateFormat formatter = new SimpleDateFormat(pattern, Locale.ENGLISH);

		Date date = null;
		try {
			date = (Date) formatter.parse(str);
		} catch (ParseException e) {
			return null;
		}

		return date;
	}
    
    /**
	 * 取得當前系統時間戳
	 * @param pattern eg:yyyy-MM-dd HH:mm:ss,SSS
	 * @return
	 */
    public static int getSysTimeStamp(String pattern) {

        return getTimeStampByDate(formatSysTime(new SimpleDateFormat(pattern)),pattern);
    }
    
    private static String formatSysTime(SimpleDateFormat format) {

        String str = format.format(Calendar.getInstance().getTime());
        return str;
    }
    
	/**
	   *  獲取指定時間 之前或之後 幾分鐘的時間
	   * @param startTime  指定時間
	   * @param minute   分鐘
	   * @param type  -1 之前的時間  ,1 之後的時間
	   * @return
	*/
    public static String getTimeByMinute(String startTime,int minute, String type ) {
    	//String startTime = "2018-05-10 11:10:50";
    	Date format = format(startTime, "yyyy-MM-dd HH:mm:ss");
    	long time = format.getTime();
    	if(type.equals("-1")){
    		time = time - (minute * 60 * 1000);
    	}else{
    		time = time + (minute * 60 * 1000);
    	}
    	String resultTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time);
        return resultTime;

    }
}

關於數據庫操作的實體類和service這裏不不加了,畢竟每個人的業務都不一樣,日誌相關可忽略。下面上核心程序

package com.jds.controller;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlImage;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
import com.jds.model.CoreThirdpartyCusEv;
import com.jds.service.CoreThirdpartyCusService;
import com.jds.service.SysConfigService;
import com.jds.util.TimeUtils;
  
@Controller
public class PaChongController {  
	/** 日誌*/
	private static final Logger logger = LoggerFactory.getLogger(PaChongController.class);
	final private static String PATTERN = "yyyy-MM-dd HH:mm:ss";
	final private static int FiveMinutes = 300;
	
	/** 模擬頁面*/
	//private HtmlPage page;
	
	/** 模擬瀏覽器*/
	//private WebClient webClient;
	
	/** 延時時間(秒)*/
	@Value("${delayTimes}")
	private int delayTimes;
	
	/** 首次讀取頁數*/
	@Value("${firstPage}")
	private int firstPage;
	
	/** 除首次每10分鐘讀取頁數*/
	@Value("${pages}")
	private int pages;
	
	/** 種類*/
	@Value("${type}")
	private String[] type;
	
	/** 保存數據service*/
	@Autowired
	private CoreThirdpartyCusService coreThirdpartyCusEvService;
	@Autowired
	private SysConfigService sysConfigService;
	
	@RequestMapping(value = "/login")//進入自己登錄頁面的控制器
	public String login(HttpServletRequest request , HttpServletResponse response){
		logger.info("進入.................");
        //創建一個webclient,指定火狐
		WebClient webClient = new WebClient(BrowserVersion.FIREFOX_31); 
		request.getSession().setAttribute("webClient", webClient);
        //參數設置  
        // 1 啓動JS  
        webClient.getOptions().setJavaScriptEnabled(true);  
        // 2 禁用Css,可避免自動二次請求CSS進行渲染  
        webClient.getOptions().setCssEnabled(false);  
        //3 啓動客戶端重定向  
        webClient.getOptions().setRedirectEnabled(true);  
        // 4 運行錯誤時,是否拋出異常  
        webClient.getOptions().setThrowExceptionOnScriptError(false);  
        // 5 設置超時  
        webClient.getOptions().setTimeout(600000);  
        //6 設置忽略證書  
        //webClient.getOptions().setUseInsecureSSL(true);  
        //7 設置Ajax  
        webClient.setAjaxController(new NicelyResynchronizingAjaxController());  
        //8設置cookie  
        webClient.getCookieManager().setCookiesEnabled(true); 
        //設置js超時時間
        webClient.setJavaScriptTimeout(30000);
        try {
        	//先判斷是否已經登錄 已經登錄跳錯誤頁面
        	String path = request.getServletContext().getRealPath("/");
        	HtmlPage page = webClient.getPage("你的目標網站登錄頁面");
        	request.getSession().setAttribute("page", page);
			File file = new File(path+"image/yzm.png");//你的服務器圖片地址
			file.createNewFile();
			HtmlImage vaCode=(HtmlImage) page.getElementById("captchaImage");//驗證碼圖片
	        //保存圖片
	        vaCode.saveAs(file);
		} catch (IOException e) {
			e.printStackTrace();
		} 
        return "html/login";
	}
	
	@RequestMapping(value = "/index")//點擊登錄的控制器
	public String index(HttpServletRequest request , HttpServletResponse response){
    	try {
    		request.setCharacterEncoding("utf-8");
    		WebClient webClient = (WebClient) request.getSession().getAttribute("webClient");
    		HtmlPage page = (HtmlPage) request.getSession().getAttribute("page");
            HtmlForm form = (HtmlForm) page.getElementById("loginForm");//登錄表單 
            HtmlInput username = page.getHtmlElementById("username");//用戶名
            HtmlInput pwd = page.getHtmlElementById("password");//密碼
            HtmlInput captcha = page.getHtmlElementById("captcha");//驗證碼輸入框
            HtmlSubmitInput btn = form.getInputByValue("登錄");//登錄按鈕
            username.setAttribute("value", request.getParameter("username"));//賬號
            pwd.setAttribute("value", request.getParameter("password"));//密碼
            captcha.setAttribute("value", request.getParameter("captcha"));//驗證碼
            // 等待JS驅動dom完成獲得還原後的網頁  
            HtmlPage page2 = btn.click();//登錄之後的頁面
            webClient.waitForBackgroundJavaScriptStartingBefore(20000);//設置js加載時間
            DomNodeList<DomElement> Anchors = page2.getElementsByTagName("a");
            HtmlAnchor btn2 = (HtmlAnchor) Anchors.get(7);//客戶檔案按鈕
            System.out.println("登錄成功................."+"時間:"+new Date());
            HtmlPage page3 = btn2.click();//點擊客戶檔案後的頁面
            webClient.waitForBackgroundJavaScript(10000); 
            DomNodeList<DomElement> Anchors2 = page3.getElementsByTagName("a");

            boolean flag = true;
            //處理數據
            List<Element> lists = new ArrayList<Element>();//第一次
            List<Element> lists2 = new ArrayList<Element>();//增量
            List<Element> listsTemp2 = new ArrayList<Element>();//臨時數據
            ArrayList<CoreThirdpartyCusEv> cs = null;
            ArrayList<CoreThirdpartyCusEv> cs2 = null;
            while(true){
            	List<Element> listsTemp = new ArrayList<Element>(listsTemp2);//臨時數據
                int count = flag==true?firstPage:pages;
                for(int i=0;i<count;i++){//循環保存數據
                	String info = null;
                	info = page3.asXml();
                  	if(flag){
                  		dealInfo(info,lists);//解析成html格式
                  	}else{
                  		dealInfo(info,lists2);//解析成html格式
                  	}
                  	if(i<count-1){
                    	HtmlAnchor btn3 = (HtmlAnchor)Anchors2.get(Anchors2.size()-2);//">"按鈕
                    	btn3.click();
                    	webClient.waitForBackgroundJavaScriptStartingBefore(15000);
                  	}
                }
                listsTemp2 = new ArrayList<Element>(lists2);//臨時數據
                CoreThirdpartyCusEv c = null;//信息實體
                //循環的時候去重
                if(lists2!=null && lists2.size()!=0){
                	for(Element e : listsTemp.size()!=0?listsTemp:lists){
 	                  	String id = e.child(0).text();//id
                      	for(int i=0;i<lists2.size();i++){
                      		if(id.equals(lists2.get(i).child(0).text())){
                      			lists2.remove(i);
                      		}
                      	}
 	                }
                }
//                System.out.println("和上個頁面去重後數據(tr)"+lists2.size());
//                if(lists2.size()>0){
//                	for(Element e :lists2){
//                		System.out.println(e);
//                	}
//                	 
//                }
                cs = new ArrayList<CoreThirdpartyCusEv>();//信息實體數據集合 第一次
                cs2 = new ArrayList<CoreThirdpartyCusEv>();//增量數據
                cs2 = flag == true ? dealList(lists,c,cs):dealList(lists2,c,cs);
                System.out.println("當前時間段查詢數量(對象)"+cs2.size()+"時間:"+new Date());
                if(cs2!=null && cs2.size()!=0){
                    //重啓的時候去重
                    if(flag){
                    	ArrayList<CoreThirdpartyCusEv> CoreThirdpartyCusEvList = coreThirdpartyCusEvService.findThirdpartyCusList();
                    	System.out.println("存量數據"+CoreThirdpartyCusEvList.size()+"時間:"+new Date());
                    	if(CoreThirdpartyCusEvList !=null && CoreThirdpartyCusEvList.size()!=0){
                    		for(CoreThirdpartyCusEv coreThirdpartyCus : CoreThirdpartyCusEvList){
                        		for(int i=0;i<cs2.size();i++){
                        			if(cs2.get(i).getCusName().equals(coreThirdpartyCus.getCusName()) && cs2.get(i).getCusMobile().equals(coreThirdpartyCus.getCusMobile()) && cs2.get(i).getCreateTime().equals(coreThirdpartyCus.getCreateTime())
                        					&& cs2.get(i).getCusIdcard().equals(coreThirdpartyCus.getCusIdcard())){
                        				cs2.remove(i);
                        			}
                        		}
                        	}
                    	}
                    }
                   System.out.println("保存數據"+cs2.size()+"條"); 
            	   for(CoreThirdpartyCusEv cc : cs2){
                    //存數據庫
            		coreThirdpartyCusEvService.insertThirdpartyCus(cc);
            	   }
                   System.out.println("保存成功...");
                }
                System.out.println("開始休眠"+delayTimes * 1000/60000+"分鐘");
            	Thread.sleep(delayTimes * 1000);//休眠5分鐘
            	flag = false;
            	page3 = btn2.click();//返回客戶檔案頁面
            	webClient.waitForBackgroundJavaScriptStartingBefore(15000);
            }
         }catch (Exception e) {
        	 	logger.info("程序出錯----------------------------------------"+new Date());
        	 	System.out.println("登錄失敗"+new Date());
            	e.printStackTrace();
	        	return "html/error2";
         }  
    }
    
	/**
     * 保存數據
     * @param e
     * @param c
     * @param cs
     * @return
     * @throws ParseException 
     */
    public ArrayList<CoreThirdpartyCusEv> dealList(List<Element> lists,CoreThirdpartyCusEv c, ArrayList<CoreThirdpartyCusEv> cs) throws ParseException{
    	if(lists.size() == 0){
    		return cs;
    	}
    	for(Element e : lists){
    	   	c = new CoreThirdpartyCusEv();
	    	String createTime = e.child(1).text();//創建時間 
	    	createTime = TimeUtils.getTimeByMinute(createTime , 25 , "1");
	    	String nameAndMobile = e.child(2).text();
	    	String[] strs = nameAndMobile.split("\\s+");//根據空格拆分
	    	//拆分名字和手機號,如果沒有名字則放棄這條數據
	    	if(strs.length > 1){
	    		String name = strs[0];//
	    		if("-".equals(name)){
	    			continue;
	    		}
	    		String mobile = strs[1];//
	    		c.setCusName(name);//
	        	c.setCusMobile(mobile);//
	    	}else{
	    		continue;
	    	}
	    	String idcard = e.child(3).text();//
	    	Date date = null;
			SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
			date = sdf.parse(createTime);
	    	c.setCreateTime(date);//
	    	c.setCusIdcard(idcard);//
	    	c.setStatus("0");//
	    	c.setThiridpartyCode(getRandomType(type));//
	    	c.setZhimaScore(zhimaScore());
	    	cs.add(c);
    	}
    	return cs;
    }
    
    /**
     * 解析、封裝數據
     * @param info
     */
    public void dealInfo(String info,List<Element> lists){
    	Document document = Jsoup.parse(info);//轉成DOM格式方便解析
    	Element table = document.getElementById("query-table");//數據節點id
    	Element tbody = table.select("tbody").first();
    	Elements trs = tbody.select("tr");
    	for(Element tr : trs){
    		if((TimeUtils.getSysTimeStamp(PATTERN)-TimeUtils.getTimeStampByDate(tr.child(1).text(), PATTERN))>FiveMinutes){
    			lists.add(tr);
    		}
    	}
    }
    
    /**
     * 隨機生成600-700的整數
     * @return
     */
    public int zhimaScore(){
    	Random r = new Random();
    	int score = r.nextInt(100)+601;
    	return score;
    }
    
    /**
     * 生成隨機的種類({"AA22001","AA22002","AA22003","AA22004","AA22005","AA22006","AA22007","AA22008","AA22009"})
     * @return
     */
    public String getRandomType(String[] type){
    	Random r = new Random();
		int num = r.nextInt(type.length);
    	return type[num];
    }
}

請大家忽略有關數據庫操作的一切代碼。

        大體就是這樣的邏輯了,登錄頁面還是自己寫吧。最後說下存在的問題,第一個就是死循環的問題,這樣會導致內存一直增加(不過到現在運行了1個多月了似乎還沒出現),第二個就是作用域的問題,如果重複登錄的話會導致服務器運行2個該程序,導致原本5分鐘的循環時間會縮減(5分鐘內爬存2次)。有沒有大神能提點下。。。

         這篇也是博主的第一個個人博客,上面關於html對象的選擇器還能優化下,建議去看看htmlunit對於html節點對象的選擇器,其中很多地方寫的不好(個人感覺就思路可以借鑑下,代碼看看就好),敬請見諒。PS:如果需要的人多的話我就改改代碼發個能運行的代碼包。比較不能泄露公司機密嘛。

        如有出數據庫操作之外的遺漏請通知。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章