java爬虫之抓取城市数据
需求:将网址中的城市地址信息抓取出来并持久化。完成三级城市联动查询。
我这里介绍一下省级名称的爬取。
博主爬出来的四级城市地址信息: 可以做城市的联动选择的功能。
爬虫源码下载:

分析网页中的信息
打开调试可以看到如下信息:

可知城市信息都在样式为class="provincetr"的<tr>中
下面开工:
工具:
MyEclipse + MySQL + Hibernate + jsoup + c3p0
其中 Hibernate 负责数据的持久化
jsoup负责解析网页
c3p0数据库连接池,优化对数据库大量更新而引出的问题(乡镇级别将近4万多条数据)
步骤1:新建一个java项目 导入必须的jar包 (我新建了一个文件夹lib,jar包放入后导入的)
截图为整个项目结构

步骤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;
}
}
程序执行结果:


















