继【简单的java采集程序】,这里将完成对整个网站的号码段的采集任务。

【使用预编译+批处理采集网页内容至数据库表中】

 

       在之前我们用statement类来创建sql语句的执行对象,以实现插入字段到数据库的操作,但由于插入的数据量较大,如果继续用statement操作话,会很耗时间,我们用其子类PreparedStatement来进行操作。

     PreparedStatement 可以实现sql语句的预编译,我们只需要通过其setString()方法传参即可,这样不仅效率提高了,而且也会更加安全,可防止SQL注入。推荐相关文章

 

     另外我们还可以调用其addBatch()方法 和 executeBatch()实现批量插入处理。

代码如下,喜欢把数据库链接作为一个单独的类

import java.sql.DriverManager;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
public class database {
public static String driver ="com.mysql.jdbc.Driver"; public static String url="jdbc:mysql://127.0.0.1:3306/tele_dat?autoReconnect=true&characterEncoding=UTF-8";
public static String user ="root";
public static String password = "123456";
public static java.sql.Connection conn = null; //返回一个数据库连接对象
public static Connection ConnectToDataBase(){
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
System.out.println("加载驱动失败");
e.printStackTrace();
}
try {
conn = DriverManager.getConnection(url, user, password);
System.out.println("连接成功");
} catch (SQLException e) {
System.out.println("连接出问题了");
e.printStackTrace();
}
return (Connection) conn;
} //测试连接数据库
public static void main(String args[])
{
database.ConnectToDataBase();
}
}

主体程序

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class Crawl {
private static Connection conn = database.ConnectToDataBase();
static String home_url = "http://www.hiphop8.com"; //网站首页 static String pattern_pro_city = "<DIV class=title><SPAN>(.*?) - (.*?)<\\/SPAN><\\/DIV>"; //匹配省名,市名
static String pattern_number = ">(13\\d{5}|15\\d{5}|18\\d{5}|147\\d{4})<"; //匹配号码段
static String pattern_pro ="\\w{3}\\.\\w{7}\\.\\w{3}\\/\\w{4}\\/\\w+"; //省份URL
static String pattern_city_hz="<LI><A href=\"(.*?)\" target=_blank>"; //城市URL的后缀 //编译预处理相关选项
static String insertSQL = "insert ignore into number_segment(segment,province,city) values(?, ?, ?)";
static PreparedStatement pst = null; static int num_pro = 0;
static int num_city=0;
static int all_num_tele = 0; public static void main(String[] args) throws Exception {
String PreStat = "insert ignore into number_segment(segment,province,city) values (?,?,?) ";
pst = conn.prepareStatement(PreStat.toString()); Matcher mat_home = get(home_url,pattern_pro); long start = System.currentTimeMillis();
while(mat_home.find())
{
num_pro++;
System.out.println("------第"+num_pro+"个省-----");
String city_url_qz = "http://"+mat_home.group()+"/";
int len = city_url_qz.length(); //这里换成StringBuffer来最字符串进行相加处理
StringBuffer city_ur = new StringBuffer();
city_ur.append(city_url_qz);
Matcher mat_city_hz = get(city_url_qz,pattern_city_hz);
while(mat_city_hz.find()) //通过拼接获得 城市的完整url
{
num_city++;
System.out.println("第"+num_city+"个市");
String last_city_url=city_ur.append(mat_city_hz.group(1)).toString();
//String last_city_url = city_url_qz + mat_city_hz.group(1);
int len2 = last_city_url.length(); One_City_Tele_to_DB(last_city_url);
city_ur.delete(len,len2);
}
}
long end = System.currentTimeMillis();
long time = (end-start)/(1000*60);
conn.close();
System.out.println("查询到的电话号码段总数量:"+all_num_tele);
System.out.println("花费的时间是:"+time);
}
public static void One_City_Tele_to_DB(String url) throws Exception {
int this_city_num=0;
String pro = null;
String city = null; Matcher mat_pro_city = get(url,pattern_pro_city); //获取省名字 市名字
while(mat_pro_city.find())
{
String long_pro = mat_pro_city.group(1);
pro = long_pro.substring(0, long_pro.length()-1);
String long_city = mat_pro_city.group(2);
city = long_city.substring(0, long_city.length()-10);
System.out.println("省份:"+pro+" "+"城市:"+city+" 正在插入号码段进数据库");
} Matcher mat_number = get(url,pattern_number); //获取号码段
while(mat_number.find())
{
pst.setString(1,mat_number.group(1));
pst.setString(2, pro);
pst.setString(3, city);
pst.addBatch();
this_city_num++;
all_num_tele++;
}
pst.executeBatch(); //每次批量插入一个城市的号码段
pst.clearBatch();
System.out.println("该市插入的号码段的数量是:"+ this_city_num);
} //正则匹配
public static Matcher get(String str_url, String pattern) throws Exception {
String urlsource = get_Html(str_url);
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(urlsource);
return m;
}
//获取网页内容
public static String get_Html(String str_url) throws IOException{
URL url = new URL(str_url);
String content="";
StringBuffer page = new StringBuffer();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(url
.openStream()));
while((content = in.readLine()) != null){
page.append(content);
}
} catch (IOException e) {
e.printStackTrace();
}
return page.toString();
}
}

      实际运行程序,发现有500多个重复的号码段,因为襄樊市 改成 襄阳市,这两个市的号码段全部一样,而数据库表中是以segment(号码)作为主键,所以要设置,当插入有相同主键的sql语句时,自动忽略跳过,方法就是在insert 后面加上ignore就可以了。

      另外设置id为auto_increment,但如果把数据表里的数据清空之后,id不会从1重新开始,这时只要在mysql命令行下输入 truncate table table_name 就可以实现id从1开始了。

运行结果截图

 

     显然,6分钟的速度还是太慢了,后面试了几次都是在6~8分钟之内(不过相对于不用批处理而直接用statement已经快很多了)。因此还得想办法优化。

【使用智能批量处理+StringBuilder的append()方法】

        在插入mysql数据库表中时,可以以insert ignore into number_segment(segment,province,city) values(?, ?, ?),vaulues(?),values(?,?,?)…的形式进行批量插入,上面使用setString()方法传参还是太慢了,直接用StringBuilder的append()方法吧,我们都知道appedn()方法进行字符串相加处理的效率是很高的,所以我们每次在insert语句后面加入一段“vaulues(?,?,?),”这样形式的字符串,然后批处理插入,这里通过变量all_tele_num进行批量处理的的控制,我们规定每次批量执行2000个数据段插入。

       直接看代码吧

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class SecondCrawl {
private static Connection conn = database.ConnectToDataBase(); //预编译 + StringBuilder
static StringBuilder PreStat = new StringBuilder();
static String Qz = "insert ignore into number_segment(segment,province,city) values";
static String insertSQL = "insert ignore into number_segment(segment,province,city) values(?, ?, ?)";
static int len1 = Qz.length();
static PreparedStatement pst = null; static String home_url = "http://www.hiphop8.com"; static String pattern_pro_city = "<DIV class=title><SPAN>(.*?) - (.*?)<\\/SPAN><\\/DIV>"; //匹配省名,市名
static String pattern_number = ">(13\\d{5}|15\\d{5}|18\\d{5}|147\\d{4})<"; //匹配号码段
static String pattern_pro ="\\w{3}\\.\\w{7}\\.\\w{3}\\/\\w{4}\\/\\w+"; //省份URL
static String pattern_city_hz="<LI><A href=\"(.*?)\" target=_blank>"; //城市URL的后缀 static int num_pro = 0;
static int num_city=0;
static int all_num_tele=0; public static void main(String[] args) throws Exception {
Matcher mat_home = get(home_url,pattern_pro);
conn.setAutoCommit(true);
PreStat.append(Qz);
pst = conn.prepareStatement(insertSQL); //预编译 long start = System.currentTimeMillis();
while(mat_home.find())
{
num_pro++;
System.out.println("------第"+num_pro+"个省-----");
String city_url_qz = "http://"+mat_home.group()+"/";
int len = city_url_qz.length(); StringBuffer city_ur = new StringBuffer();
city_ur.append(city_url_qz);
Matcher mat_city_hz = get(city_url_qz,pattern_city_hz);
while(mat_city_hz.find()) //获得城市的url
{
num_city++;
System.out.println("第"+num_city+"个市");
String city_url=city_ur.append(mat_city_hz.group(1)).toString();
int len2 = city_url.length(); One_City_Tele_to_DB(city_url);
city_ur.delete(len,len2);
}
}
long end = System.currentTimeMillis();
long time = (end-start)/(1000*60);
pst.executeBatch(); //批处理执行最后面剩余的部分 conn.close();
System.out.println("查询到的电话号码段总数量:"+all_num_tele);
System.out.println("花费的时间是:"+time+"分多钟\n"+"以微秒为单位:"+(end-start)+"微秒");
} //一个城市的手机号码段处理函数
public static void One_City_Tele_to_DB(String url) throws Exception {
String city=null;
String pro =null;
int this_city_num = 0;
Matcher mat_pro_city = get(url,pattern_pro_city); while(mat_pro_city.find())
{
String long_pro = mat_pro_city.group(1);
pro = long_pro.substring(0, long_pro.length()-1);
String long_city = mat_pro_city.group(2);
city = long_city.substring(0, long_city.length()-10);
System.out.println("省份:"+pro+" "+"城市:"+city+" 正在插入号码段进数据库...");
} String temp = ",'"+pro+"','"+city+"'),";
Matcher mat_number = get(url,pattern_number);
while(mat_number.find())
{
PreStat.append("("+mat_number.group(1)).append(temp);
this_city_num++;
all_num_tele++;
if(all_num_tele<=208000 && all_num_tele % 2000==0)
{
PreStat.deleteCharAt(PreStat.length()-1); //除去sql语句后的逗号
pst.addBatch(PreStat.toString());
pst.executeBatch();
pst.clearBatch();
PreStat.delete(len1, PreStat.length()); //情况sql语句后面部分以释放空间
}
}
if(all_num_tele>208000) //后面不足2000部分的城市加入批处理中先不执行
{
PreStat.deleteCharAt(PreStat.length()-1);
pst.addBatch(PreStat.toString());
PreStat.delete(len1, PreStat.length());
}
System.out.println("该市插入的号码段的数量是:"+ this_city_num);
} //正则匹配
public static Matcher get(String str_url, String pattern) throws Exception {
String urlsource = get_Html(str_url);
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(urlsource);
return m;
}
//获取网页内容
public static String get_Html(String str_url) throws IOException{
URL url = new URL(str_url);
String content="";
StringBuffer page = new StringBuffer();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(url
.openStream()));
while((content = in.readLine()) != null){
page.append(content);
}
} catch (IOException e) {
e.printStackTrace();
}
return page.toString();
}
}

运行截图

 

        测试了几次,运行时间是在2分钟左右,又提速了不少,不过还是有很大的提升空间的,因为自己在测试时,如果程序仅仅是插入20多万的sql语句,可以在几秒钟内完成。

      说道再优化,自己的思路是把网站url采集和插入数据库使用多线程进行并发操作,现在正在学习java的多线程,也在尝试用多线程的方法写采集程序,如果大家还有更好的方法,也可以给我留言,愿意和大家一起交流进步。

简单的java采集程序二的更多相关文章

  1. 简单java采集程序一

    [目标任务]通过该网站采集全国的手机号码段至数据库表中 [完成过程] 1.初涉正则表达式,学会写简单的正则表达式 2.获取单个网页内容,学会java中基本的IO流 3.将获取数据插入mysql数据库表 ...

  2. 【Java】一个简单的Java应用程序

    简单记录,Java 核心技术卷I 基础知识(原书第10 版) 一个简单的Java应用程序"Hello, World!" Hello, World! Goodbye,World! 一 ...

  3. 一个简单的Java应用程序

    目录 一个简单的Java应用程序 首次运行结果 程序示例 运行结果 修改大小写之后的运行结果 程序示例 运行结果 关键字public 关键字class 类名及其命名规则 类名必须以字母开头 不能使用J ...

  4. 3.1 一个简单的Java应用程序

        如下一个最简单的应用程序,它将只发送一条消息到控制台窗口中: package myjavapp;   public class FirstSample {       public stati ...

  5. Java:一个简单的Java应用程序

    /** * 这是文档注释 * @version  * @author  */ /* 这之间的内容也是注释 */ //这是行注释 public class FirstSample { public st ...

  6. 简单的网页采集程序(ASP.NET MVC4)

    因为懒人太多,造成现在网页数据采集非常的流行,我也来写个简单的记录一下. 之前写了MVC的基本框架的搭建随笔,后面因为公司太忙,个人感情问题:(,导致不想写了,就写了两篇给删除了,现在就搁浅了, 本人 ...

  7. IntelliJ IDEA创建一个简单的Java Project(二)

    1.  选择要创建的项目类型,同时配置本地的JDK 2. 是否使用模板创建项目 3. 选择项目在本地的存储位置 4. 点击Finish,完成一个简单的Java工程的创建.

  8. Servlet视频-开发第一个java web(最简单的java web程序)(二)

    web项目有目录结构要求 WEB-INFO 文件夹 是一个Servlet规范,必须要这么命名,在换个文件夹里面如果创建一个jsp文件是不能直接访问的,在WEB-INfO文件夹之外创建的jsp可以直接访 ...

  9. 2015.11.27初识java一集简单的java小程序

    JAVA配置环境变量方法: 1.打开我的电脑--属性--高级--环境变量 2.新建系统变量JAVA_HOME 和CLASSPATH 变量名:JAVA_HOME变量值:D:\Java\jdk1.7.0_ ...

随机推荐

  1. djano-模板层基础知识

    ########模板层######## 模板层其实就是templates文件夹里的html文件 其实这里的每个html不是真正意义的上html代码,只有经过模板渲染过后才算的上真正的html页面. 一 ...

  2. 第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--E-回旋星空

    链接:https://www.nowcoder.com/acm/contest/90/E 来源:牛客网 1.题目描述 曾经有两个来自吉尔尼斯的人(A和C)恋爱了,他们晚上经常在一起看头上的那片名为假的 ...

  3. 爬虫——正则表达式re模块

    为什么要学习正则表达式 实际上爬虫一共就四个主要步骤: 明确目标:需清楚目标网站 爬:将所有的目标网站的内容全部爬下来 取:在爬下来的网站内容中去掉对我们没有用处的数据,只留取我们需要的数据 处理数据 ...

  4. python的字典数据类型及常用操作

    字典的定义与特性 字典是Python语言中唯一的映射类型. 定义:{key1: value1, key2: value2} 1.键与值用冒号“:”分开: 2.项与项用逗号“,”分开: 特性: 1.ke ...

  5. Asp.Net Core 使用Docker进行容器化部署(一)

    前几篇文章介绍了Liunx下的环境搭建,今天来分享一下.Net Core在Liunx上的部署. 我采用的方案是使用Dokcer作为运行虚拟机,Nginx作为Http服务器来进行反向代理,你可以理解为D ...

  6. Java源码解析——集合框架(一)——ArrayList

    ArrayList源码分析 ArrayList就是动态数组,是Array的复杂版本,它提供了动态的增加和减少元素.灵活的设置数组的大小. 一.类声明 public class ArrayList< ...

  7. 21.1 XMLHttpRequest 对象【JavaScript高级程序设计第三版】

    IE5 是第一款引入XHR 对象的浏览器.在IE5 中,XHR 对象是通过MSXML 库中的一个ActiveX对象实现的.因此,在IE 中可能会遇到三种不同版本的XHR 对象,即MSXML2.XMLH ...

  8. git克隆出错 github clone Permission denied (publickey) fatal Could not read from remote repo

    原文网址:http://blog.csdn.net/feeling450/article/details/53067563 github clone "Permission denied ( ...

  9. Leecode刷题之旅-C语言/python-66加一

    /* * @lc app=leetcode.cn id=66 lang=c * * [66] 加一 * * https://leetcode-cn.com/problems/plus-one/desc ...

  10. go学习笔记-结构体

    结构体 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合 定义 格式 type struct_variable_type struct { member definition; member ...