一、网络爬虫
是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。
二、目的
需要把别人网站上发布的信息,采集放到自己数据库中,这里先放到自定义bean中(这里采集网站http://www.szhfpc.gov.cn/xxgk/zcfggfxwj/mybh_5 政策法规则内容)
1. 传入需要采集的页面获取整个页面的内容
编写 AnalysisUtils.java 工具类
public static String SendGet(String url) {
// 定义一个字符串用来存储网页内容
String result = "";
// 定义一个缓冲字符输入流
BufferedReader in = null;
try {
// 将string转成url对象
URL realUrl = new URL(url);
// 初始化一个链接到那个url的连接
URLConnection connection = realUrl.openConnection();
// 设置欺骗服务器访问
connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
// 开始实际的连接
connection.connect();
// 初始化 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(
connection.getInputStream(), "UTF-8"));
// 用来临时存储抓取到的每一行的数据
String line;
while ((line = in.readLine()) != null) {
// 遍历抓取到的每一行并将其存储到result里面
result += line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
注:如果不设置connection.setRequestProperty属性,多测试几次会报403错误
2. 解析整个页面内容
编写网站解析AnalysisSzhfpcWebsite类,支持分页解析,支持附件下载到本地
private static final String LOCAL_PATH = "D:/tempfile"; // 存放本地文件路径
/**
* http://www.szhfpc.gov.cn/ 采集
* @param url 路径
* 例
* 采集政策法规
* http://www.szhfpc.gov.cn/xxgk/zcfggfxwj/mybh_5
* 注 传入URL最后一位不要加 /
*/
public static List<ZcfgEntity> szhfpc(String url) {
List<ZcfgEntity> result = new ArrayList<ZcfgEntity>();
String mybhContent = AnalysisUtils.SendGet(url);
result.addAll(parseLevel(mybhContent,url));
// 获得左侧链接只执行一次
String g1LeftDiv = "<div class=\"gl-left\">(.*?)</div>";
Pattern g1LeftPt = Pattern.compile(g1LeftDiv);
Matcher g1LeftMt = g1LeftPt.matcher(mybhContent);
if(g1LeftMt.find()){
String g1LeftContent = g1LeftMt.group(1);
// 先DIV 下 A 链接
String g1Href = "href=\"([^\"]*)";
Pattern g1HrefPt = Pattern.compile(g1Href);
Matcher g1HrefMt = g1HrefPt.matcher(g1LeftContent);
while(g1HrefMt.find()){
String g1LeftHrefContent = g1HrefMt.group(1);
System.out.println("链接:"+g1LeftHrefContent);
int index = url.lastIndexOf("/");
String tempUrl = url.substring(0, index);
System.out.println("tempUrl:"+tempUrl);
int startIndex = g1LeftHrefContent.indexOf("/");
int beginIndex = g1LeftHrefContent.lastIndexOf("/");
String temp = g1LeftHrefContent.substring(startIndex, beginIndex);
System.out.println("temp:"+temp);
if(!temp.trim().equals("")){
String u = tempUrl+temp;
String c = AnalysisUtils.SendGet(u);
result.addAll(parseLevel(c,u));
}
}
}
return result;
}
/**
* 二级页面内容解析
* @param content
*/
public static List<ZcfgEntity> parseLevel(String mybhContent,String url){
List<ZcfgEntity> result = new ArrayList<ZcfgEntity>();
String type = "";
String typeItem = "";
// 获取位置信息
String curmbDiv = "<div class=\"curmb\">(.*?)</div>";
Pattern curmbPt = Pattern.compile(curmbDiv);
Matcher curmbMt = curmbPt.matcher(mybhContent);
if(curmbMt.find()){
String curmbContent = curmbMt.group(1);
System.out.println("curmbMt:"+curmbMt.group(1));
// 先DIV 下 A 链接
String curmbHref = "<a[^>]*>(.*?)</a>";
Pattern curmbHrefPt = Pattern.compile(curmbHref);
Matcher curmbHrefMt = curmbHrefPt.matcher(curmbContent);
// 取最后二项
List<String> tempList = new ArrayList<String>();
while(curmbHrefMt.find()){
tempList.add(curmbHrefMt.group(1));
}
if(tempList.size()>0){
int size = tempList.size();
typeItem = tempList.get(size-1); // 最后一项
type = tempList.get(size-2); // 最后第二项
}
}
// 先取<div class="gl-right">
String g1Div = "<div class=\"gl-right\">(.*?)</div>";
Pattern g1Pt = Pattern.compile(g1Div);
Matcher g1Mt = g1Pt.matcher(mybhContent);
if(g1Mt.find()){
String g1Cotnent = g1Mt.group(1);
System.out.println("g1Div:"+g1Cotnent);
// 先DIV 下 A 链接
String g1Href = "href=\"([^\"]*)";
Pattern g1HrefPt = Pattern.compile(g1Href);
Matcher g1HrefMt = g1HrefPt.matcher(g1Cotnent);
ZcfgEntity entity = null;
while(g1HrefMt.find()){
String g1HrefContent = g1HrefMt.group(1);
System.out.println("链接:"+g1HrefContent);
entity = new ZcfgEntity();
entity.setType(type);
entity.setTypeItem(typeItem);
// 详细页面解析 与附件下载
parseDetail(url+g1HrefContent.substring(1, g1HrefContent.length()),entity);
result.add(entity);
}
}
// 获取分页数
String pageDiv = "<div class=\"page mt20\">(.*?)</div>";
Pattern pagePt = Pattern.compile(pageDiv);
Matcher pageMt = pagePt.matcher(mybhContent);
if(pageMt.find()){
String pageContent = pageMt.group(1);
System.out.println("pageContent:"+pageContent);
int startIndex = pageContent.indexOf("(")+1;
int beginIndex = pageContent.indexOf(",");
String pageStr = pageContent.substring(startIndex, beginIndex);
//System.out.println("页数:"+pageStr);
int page = Integer.parseInt(pageStr);
// 分页循环读取
for (int i = 1; i < page; i++) {
System.out.println("第"+(i+1)+"页数据如下:");
String s = url + "/index_" + i + ".htm";
String secondLevelContent = AnalysisUtils.SendGet(s);
g1Mt = g1Pt.matcher(secondLevelContent);
if(g1Mt.find()){
String g1Cotnent = g1Mt.group(1);
//System.out.println("g1Div:"+g1Cotnent);
// 先DIV 下 A 链接
String g1Href = "href=\"([^\"]*)";
Pattern g1HrefPt = Pattern.compile(g1Href);
Matcher g1HrefMt = g1HrefPt.matcher(g1Cotnent);
ZcfgEntity entity = null;
while(g1HrefMt.find()){
String g1HrefContent = g1HrefMt.group(1);
System.out.println("链接:"+g1HrefContent);
entity = new ZcfgEntity();
entity.setType(type);
entity.setTypeItem(typeItem);
// 详细页面解析 与附件下载
parseDetail(url+g1HrefContent.substring(1, g1HrefContent.length()),entity);
result.add(entity);
}
}
}
}
return result;
}
/**
* 解析http://www.szhfpc.gov.cn/xxgk/zcfggfxwj 网络下 详细页面
* @param url 页面地址
*/
private static void parseDetail(String url,ZcfgEntity entity){
String content = AnalysisUtils.SendGet(url);
System.out.println("content:"+content);
// 获得class=main的DIV
String div = "<div class=.+main.+?>(.*?)</div>";
Pattern mainPt = Pattern.compile(div);
Matcher mainMt = mainPt.matcher(content);
if(mainMt.find()){
System.out.println("内容:"+mainMt.group(1));
String group = mainMt.group(1);
String h4 = "<h4>(.*?)</h4>";
Pattern h4Pt = Pattern.compile(h4);
Matcher h4Mt = h4Pt.matcher(group);
if(h4Mt.find()){ // 标题
String title = h4Mt.group(1);
entity.setTitle(title);
}
// 发布机构
String mechanism = "<label>发布机构:(.*?)</label>";
Pattern pt1 = Pattern.compile(mechanism);
Matcher mt1 = pt1.matcher(group);
if(mt1.find()){ // 发布机构
String sendMechanism = mt1.group(1);
entity.setSendMechanism(sendMechanism);
}
// 发布时间
String date = "<label>发布日期:(.*?)</label>";
Pattern pt2 = Pattern.compile(date);
Matcher mt2 = pt2.matcher(group);
if(mt2.find()){ // 发布日期
String sendDate = mt2.group(1);
entity.setSendDate(sendDate);
}
// 信息索取号
String info = "<label>信息索取号:(.*?)</label>";
Pattern pt3 = Pattern.compile(info);
Matcher mt3 = pt3.matcher(group);
if(mt3.find()){ // 信息索取号
String infoNumber = mt3.group(1);
entity.setInfoNumber(infoNumber);
}
// 内容
String nrDiv = "<div class=\"nr\">(.*?)</div>";
Pattern nrPt = Pattern.compile(nrDiv);
Matcher nrMt = nrPt.matcher(content);
if(nrMt.find()){ // 内容
String t = nrMt.group(1);
entity.setContent(t);
}
// 附件
boolean isFile = false; //是否有文件名
String fileDesc = "filedesc=\"(.*?)\";"; // 文件名
Pattern fileDescPt = Pattern.compile(fileDesc);
Matcher fileDescMt = fileDescPt.matcher(content);
String[] fileDescs = null;
if(fileDescMt.find()){
if(fileDescMt.group(1)!=null && !fileDescMt.group(1).trim().equals("")){ // 判断是否存在
isFile = true;
System.out.println("文件名:"+fileDescMt.group(1));
// 可能存在多个用;分割
fileDescs = fileDescMt.group(1).split(";");
}
}
if(isFile){
List<FileEntity> fileList = new ArrayList<FileEntity>();
String fileUrl = "fileurl=\"(.*?)\";"; // 路径
Pattern fileUrlPt = Pattern.compile(fileUrl);
Matcher fileUrlMt = fileUrlPt.matcher(content);
if(fileUrlMt.find()){
System.out.println("文件路径:"+fileUrlMt.group(1));
// 可能存在多个用;分割
String fileName = fileUrlMt.group(1);
String[] fileNames = fileName.split(";");
FileEntity fileEntity = null;
for (int i = 0; i < fileNames.length; i++) {
fileEntity = new FileEntity();
String tempFileName = fileNames[i].substring(1, fileNames[i].length());
// 文件下载到本地
fileEntity.setFileName(fileDescs[i]); // 显示文件名
fileEntity.setFilePath(tempFileName); // 下载别名
int lastIndex = url.lastIndexOf("/");
String path = url.substring(0, lastIndex);
String netUrl = path+tempFileName;
String localUrl = LOCAL_PATH+tempFileName;
try{
downloadNet(netUrl,localUrl);
}catch(Exception e){
e.printStackTrace();
}
fileList.add(fileEntity); //存放到集合中
}
}
entity.setFileList(fileList);
}
}
}
/**
* 网络文件下载
* @param netUrl 网络路径
* @param lcalUrl 本地路径
* @throws Exception
*/
private static void downloadNet(String netUrl,String lcalUrl) throws Exception{
int byteread = 0;
InputStream inStream = null;
FileOutputStream fs = null;
URL url = new URL(netUrl);
URLConnection conn = url.openConnection();
inStream = conn.getInputStream();
fs = new FileOutputStream(lcalUrl);
byte[] buffer = new byte[1024];
while ((byteread = inStream.read(buffer)) != -1) {
fs.write(buffer, 0, byteread);
}
if(fs!=null){
fs.close();
}
if(inStream!=null){
inStream.close();
}
}
实体类:ZcfgEntity
<span style="white-space:pre"> </span>private String type; // 类型
private String typeItem; // 子类
private String title; // 标题
private String sendMechanism; // 发布机构
private String sendDate; // 发布时间
private String infoNumber; // 信息索取号
private String content; // 内容
private List<FileEntity> fileList; // 附件
省略get、set方法
附件类FileEntity
private String fileName;
private String filePath;
省略get、set方法
编写测试类:Test.java
long startTime = System.currentTimeMillis();
// 政策法规
String zcfgUrl = "http://www.szhfpc.gov.cn/xxgk/zcfggfxwj/mybh_5";
List<ZcfgEntity> zcfgList = AnalysisSzhfpcWebsite.szhfpc(zcfgUrl);
System.out.println("政策法规条数:"+zcfgList.size());
long endTime = System.currentTimeMillis();
System.out.println("总共耗时:"+(endTime-startTime) +" S");
三、 碰到的问题与后续工作
1. 刚开始忘记设置connection.setRequestProperty属性,多运行几次就禁止访问了!
2. 主要工作查看原HTML代码,去分析自己需要的数据在什么链接下,使用正则表达式去获取链接解析。
3. 页面左侧链接只解析一次,如每次解析数据重复。
4. 分页只有在第一次进入列表页面获取分页数,此后不再获取。
忧化
1. 可以使用多线程去读取,解析!(可能会被禁IP)
2. 过滤重复数据读取