java爬虫之抓取城市数据




需求:将网址中的城市地址信息抓取出来并持久化。完成三级城市联动查询。




我这里介绍一下省级名称的爬取。  



博主爬出来的四级城市地址信息: 可以做城市的联动选择的功能。



爬虫源码下载:








java 实体toxml java 实体 提取 省份_hibernate




分析网页中的信息




打开调试可以看到如下信息:


java 实体toxml java 实体 提取 省份_bc_02




可知城市信息都在样式为class="provincetr"的<tr>中




下面开工:




工具:




MyEclipse + MySQL + Hibernate + jsoup + c3p0



其中 Hibernate 负责数据的持久化   



jsoup负责解析网页



c3p0数据库连接池,优化对数据库大量更新而引出的问题(乡镇级别将近4万多条数据)





步骤1:新建一个java项目   导入必须的jar包  (我新建了一个文件夹lib,jar包放入后导入的)



截图为整个项目结构



java 实体toxml java 实体 提取 省份_java 实体toxml_03





步骤2:创建解析爬取网页的工具类



import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import .InetAddress;
import .URL;
import .UnknownHostException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class Html {
	//根据url从网络获取网页文本
    public static Document getHtmlTextByUrl(String url) {
        Document doc = null;
        try {
            //doc = Jsoup.connect(url).timeout(5000000).get();
            int i = (int)(Math.random() * 1000); //做一个随机延时,防止网站屏蔽
            while (i != 0) {
                i--;
            }
            doc = Jsoup.connect(url).data("query", "Java").userAgent("Mozilla").cookie("auth", "token").timeout(300000).post();
        } catch (IOException e) {
            e.printStackTrace();
            try {
                doc = Jsoup.connect(url).timeout(5000000).get();
            } catch (IOException e1) { // TODO Auto-generated catch block  
            	e1.printStackTrace(); 
            } }
        return doc;
    }
    
    //根据元素属性获取某个元素内的elements列表
    public static Elements getEleByClass(Document doc, String className) { 
    	//<tr class="provincetr">
        Elements elements = null;
        elements = doc.select(className); //     tr.provincetr
        return elements; //此处返回的就是所有的tr集合
    }
	
    
}


解析打印一级节点(省份)   




由上面分析得到一级省份的节点信息为class="provincetr"  那么所传入的selecType即为:tr.provincetr


public static void ElemOneByURL(String url,String selecType){
    	//得到网站内容
    	Document htmlTextByPath =   Html.getHtmlTextByUrl(url);
    	Elements ebc =  Html.getEleByClass(htmlTextByPath, selecType);
    	for (Element e : ebc) {
    		if (e != null) {
    			for (Element ec: e.children()){ //一个tr的子元素td,td内包含a标签
    				if (ec.children().first() != null) {
                        String ownText = ec.children().first().ownText(); //a标签文本  
                      System.out.println(ownText+"--------------------------------------------------------1级");
                      System.out.println(ec.children().first().attr("abs:href")); //得到的对应的href地址
                        
    				}
    			}
    		}
    	}
    }


上面的这个方法即可将图1中的所有省份信息遍历打印出来,并得到对应的某省份市连接,找到二级市的对应<tr>信息,将该链接再次执行解析即可得到二级所有城市信息。



本着这个思路即可将全国所有城市信息都爬出来。由于时间关系我只爬到的第三级也就是区县。



步骤3:创建Hibernate的工具类  ,Hibernate配置文件Hibernate.cfg.xml , 



import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
	private static Configuration cfg=null;
	private static SessionFactory factory=null;
	private static Session session = null;
	static{
		cfg = new Configuration().configure();
		factory = cfg.buildSessionFactory(new StandardServiceRegistryBuilder()
		.applySettings(cfg.getProperties()).build());
	}
	public static Session getCurrentSession(){
		return session = factory.getCurrentSession();
	}
	public static Session getSession(){
		return session = factory.openSession();
	}
	public static void closeSession(){
		if(session!=null&&session.isOpen())
			session.close();
	}
}



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
    <!--C3P0配置 -->
    <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
    <property name="hibernate.c3p0.max_size">20</property>
    <property name="hibernate.c3p0.min_size">5</property>
    <property name="hibernate.c3p0.timeout">120</property>
    <property name="automaticTestTable">Test</property>
    <property name="hibernate.c3p0.max_statements">100</property>
    <property name="hibernate.c3p0.idle_test_period">120</property>
    <property name="hibernate.c3p0.acquire_increment">1</property>
    <property name="c3p0.testConnectionOnCheckout">true</property>
    <property name="c3p0.idleConnectionTestPeriod">18000</property>
    <property name="c3p0.maxIdleTime">25000</property>
    <property name="c3p0.idle_test_period">120</property>
	<!-- 数据库连接 四项 -->
	<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
	<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
	<property name="hibernate.connection.username">root</property>
	<property name="hibernate.connection.password">root</property>
	<!-- 数据库方言 -->
	<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
	<!-- 显示sql语句 -->
	<property name="hibernate.show_sql">false</property>
	<!-- 格式化sql语句 -->
	<property name="hibernate.format_sql">false</property>
	<!-- 指定session与谁绑定thread:session与当前线程绑定-->
	<property name="hibernate.current_session_context_class">thread</property>
	<!-- 自动生成表策略 
		create(不常用):自动创建表结构,如果表结构存在,删除重建
		create-drop(不常用):自动创建表结构,hibernate关闭删除表结构。
		update(常用):自动创建表结构,如果表结构发生改变,自动更新表结构
		validate(不常用):不会创建表结构,不会删除结构。
	-->
	<property name="hibernate.hbm2ddl.auto">update</property>
	<!-- 引入ORM元数据      相对路径:相对于src-->
	<mapping resource="com/lcx/pojo/Province.hbm.xml" />	
</session-factory>
</hibernate-configuration>





步骤4:创建对应的pojo实体类  



public class Province {
	
	private int id;     	//省份ID
	private String name;    //省名
	private String remarks; //备注
	private Set<City> city = new HashSet<City>(); //省份------市     一对多关系
	
	public Province() {}
	public Province(String name, String remarks) {
		 = name;
		this.remarks = remarks;
	}
	public Set<City> getCity() {
		return city;
	}
	public void setCity(Set<City> city) {
		this.city = city;
	}
	public String getRemarks() {
		return remarks;
	}
	public void setRemarks(String remarks) {
		this.remarks = remarks;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		 = name;
	}

}


步骤5:创建对应的实体映射文件



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
<hibernate-mapping  package="com.lcx.pojo">
	<class name="Province" >
		<id name="id">
			<generator class="native"></generator>
		</id>
		<property name="name"></property>
		<property name="remarks"></property> 
		<set name="city"  cascade="all"  lazy="true"  inverse="true">
            <key column="province_id" /><!-- 确定关联的外键列 -->
            <one-to-many class="City" />
        </set>
	</class>
</hibernate-mapping>





步骤6:创建测试类



public class Test {
	public static void main(String[] args) {
		String url = "http:///tjsj/tjbz/tjyqhdmhcxhfdm/2015/index.html";
		String selectType="tr.provincetr";
		Set<Province> pro = ElemOneByURL(url,selectType);
		for (Province province : pro) {
			Session  session = HibernateUtil.getSession();
			session.save(province);
			HibernateUtil.closeSession();
		}
	}
//---------------------------------------------------------------------
	

    //解析打印一级节点(省份)
    public static Set<Province> ElemOneByURL(String url,String selecType){
    	Set<Province> proSet = new HashSet<Province>();
    	Province pro =null;
    	//得到网站内容
    	Document htmlTextByPath =   Html.getHtmlTextByUrl(url);
    	Elements ebc =  Html.getEleByClass(htmlTextByPath, selecType);
    	for (Element e : ebc) {
    		if (e != null) {
    			for (Element ec: e.children()){ //一个tr的子元素td,td内包含a标签
    				if (ec.children().first() != null) {
                        String ownText = ec.children().first().ownText(); //a标签文本 
                   
                        //打印二级
                        pro = new Province(ownText, "省");
                     
                        proSet.add(pro);
    				}
    			}
    		}
    	}
		return proSet;
    }


}




程序执行结果:




java 实体toxml java 实体 提取 省份_java 实体toxml_04




java 实体toxml java 实体 提取 省份_java 实体toxml_05