之前学习j2ee的搭建,基本完成了。
接下来想学习下爬虫技术。要研究一项技术,首先得知道它的原理。
那么网络爬虫的原理是什么呢?
网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的
URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。
接下来我会一边研究网络爬虫的实现,一边记录产生的问题和解决方案。加油吧^_^!!
这里在网上找了个demo,先给大家看看:以下是利用Java模拟的一个程序,提取新浪页面上的链接,存放在一个文件里:
package testspider; /** * Descriptions * * @version 2017年3月31日 * @since JDK1.6 * */ import java.io.BufferedReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.regex.Matcher; import java.util.regex.Pattern; public class WebSpider { public static void main(String[] args) { URL url = null; URLConnection urlconn = null; BufferedReader br = null; PrintWriter pw = null; String regex = "http://[\\w+\\.?/?]+\\.[A-Za-z]+"; Pattern p = Pattern.compile(regex); try { url = new URL("http://www.sina.com.cn/"); urlconn = url.openConnection(); pw = new PrintWriter(new FileWriter("f:/url.txt"), true);//这里我们把收集到的链接存储在了E盘底下的一个叫做url的txt文件中 br = new BufferedReader(new InputStreamReader( urlconn.getInputStream())); String buf = null; while ((buf = br.readLine()) != null) { Matcher buf_m = p.matcher(buf); while (buf_m.find()) { pw.println(buf_m.group()); } } System.out.println("获取成功!"); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); } catch (IOException e) { e.printStackTrace(); } pw.close(); } } }
建立一个java project把代码直接放到里面,运行之后会抓取新浪的所有URL存放在本地的F:/url.txt中
随便选择一条url访问,比如http://beacon.sina.com.cn/a.gif
是可以得到图片的,这只是爬虫的简单实现,接下来我会深入研究它的实现。
网络爬虫:
开发工具:eclipse JDK1.6
从网上找的demo并没有用到服务器。所以我也不用服务器了。也不涉及数据库。把爬到的信息存储在本地目录下。
首先,建一个java工程。第一个类根据URL获取对应网页内容。
package webspilder; import java.io.IOException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; @SuppressWarnings("deprecation") public class DownloadPage { /** * 根据URL抓取网页内容 * * @param url * @return */ public static String getContentFormUrl(String url) { /* 实例化一个HttpClient客户端 */ @SuppressWarnings({"resource"}) HttpClient client = new DefaultHttpClient(); HttpGet getHttp = new HttpGet(url); String content = null; HttpResponse response; try { /*获得信息载体*/ response = client.execute(getHttp); HttpEntity entity = response.getEntity(); VisitedUrlQueue.addElem(url); if (entity != null) { /* 转化为文本信息 */ content = EntityUtils.toString(entity); /* 判断是否符合下载网页源代码到本地的条件 */ if (FunctionUtils.isCreateFile(url)) //&& FunctionUtils.isHasGoalContent(content) != -1 { FunctionUtils.createFile(FunctionUtils .getGoalContent(content), url); } } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { client.getConnectionManager().shutdown(); } return content; } }
第二个类用正则表达式匹配URL,下载文件并保存在本地。如果有数据库,则可以保存在数据库。
package webspilder; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; public class FunctionUtils { /** * 匹配超链接的正则表达式 */ private static String pat = "http://([\\w*\\.]*[\\w*])"; private static Pattern pattern = Pattern.compile(pat); private static BufferedWriter writer = null; /** * 爬虫搜索深度 */ public static int depth = 0; /** * 以"/"来分割URL,获得超链接的元素 * * @param url * @return */ public static String[] divUrl(String url) { return url.split("/"); } /** * 判断是否创建文件 * * @param url * @return */ public static boolean isCreateFile(String url) { Matcher matcher = pattern.matcher(url); return matcher.matches(); } /** * 创建对应文件 * * @param content * @param urlPath */ public static void createFile(String content, String urlPath) { /* 分割url */ String[] elems = divUrl(urlPath); StringBuffer path = new StringBuffer(); File file = null; for (int i = 1; i < elems.length; i++) { if (i != elems.length - 1) { path.append(elems[i]); path.append(File.separator); file = new File("D:" + File.separator + path.toString()); } if (i == elems.length - 1) { Pattern pattern = Pattern.compile("[\\w*\\.]*[\\w*]"); Matcher matcher = pattern.matcher(elems[i]); if ((matcher.matches())) { if (!file.exists()) { file.mkdirs(); } String fileName = elems[i]; file = new File("D:" + File.separator + path.toString() + File.separator + fileName + ".html"); System.out.println("文件存储路径为:"+"D:" + File.separator + path.toString() + fileName + ".html"); try { file.createNewFile(); writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file))); writer.write(content); writer.flush(); writer.close(); System.out.println("创建文件成功"); } catch (IOException e) { e.printStackTrace(); } } } } } /** * 获取页面的超链接并将其转换为正式的A标签 * * @param href * @return */ public static String getHrefOfInOut(String href) { /* 内外部链接最终转化为完整的链接格式 */ String resultHref = null; /* 判断是否为外部链接 */ if (href.startsWith("http://")) { resultHref = href; } else { /* 如果是内部链接,则补充完整的链接地址,其他的格式忽略不处理,如:a href="#" */ if (href.startsWith("/")) { resultHref = "http://www.oschina.net" + href; } } return resultHref; } /** * 截取网页网页源文件的目标内容 * * @param content * @return */ public static String getGoalContent(String content) { int sign = content.indexOf("<html"); String signContent = content.substring(sign); int start = signContent.indexOf("<html"); int end = signContent.indexOf("</html>"); return signContent.substring(start , end+7); } /** * 检查网页源文件中是否有目标文件 * * @param content * @return */ public static int isHasGoalContent(String content) { return content.indexOf("<"); } }
获取该URL取得页面中,其他页面的超链接,用于深度爬虫和广度爬虫。
package webspilder; public class HrefOfPage { /** * 获得页面源代码中超链接 */ public static void getHrefOfContent(String content) { System.out.println("开始"); String[] contents = content.split("<a href=\""); for (int i = 1; i < contents.length; i++) { int endHref = contents[i].indexOf("\""); String aHref = FunctionUtils.getHrefOfInOut(contents[i].substring( 0, endHref)); if (aHref != null) { String href = FunctionUtils.getHrefOfInOut(aHref); if (!UrlQueue.isContains(href) && href.indexOf("/code/explore") != -1 && !VisitedUrlQueue.isContains(href)) { UrlQueue.addElem(href); } } } System.out.println(UrlQueue.size() + "--抓取到的连接数"); System.out.println(VisitedUrlQueue.size() + "--已处理的页面数"); } }
存储未访问过的URL,广度爬虫时避免重复。
package webspilder; public class UrlDataHanding implements Runnable { /** * 下载对应页面并分析出页面对应的URL放在未访问队列中。 * @param url */ public void dataHanding(String url) { HrefOfPage.getHrefOfContent(DownloadPage.getContentFormUrl(url)); } public void run() { while(!UrlQueue.isEmpty()) { dataHanding(UrlQueue.outElem()); } } }
存储访问过的URL,广度爬虫时避免重复。
package webspilder; import java.util.HashSet; /** * 已访问url队列 * @author HHZ * */ public class VisitedUrlQueue { public static HashSet<String> visitedUrlQueue = new HashSet<String>(); public synchronized static void addElem(String url) { visitedUrlQueue.add(url); } public synchronized static boolean isContains(String url) { return visitedUrlQueue.contains(url); } public synchronized static int size() { return visitedUrlQueue.size(); } }------------------2017.12.11补充类start-----------------------------------
package webspilder; /** * Descriptions * * @version 2017年3月31日 * @author * @since JDK1.6 * */ import java.util.LinkedList; public class UrlQueue { /**超链接队列*/ public static LinkedList<String> urlQueue = new LinkedList<String>(); /**队列中对应最多的超链接数量*/ public static final int MAX_SIZE = 10000; public synchronized static void addElem(String url) { urlQueue.add(url); } public synchronized static String outElem() { return urlQueue.removeFirst(); } public synchronized static boolean isEmpty() { return urlQueue.isEmpty(); } public static int size() { return urlQueue.size(); } public static boolean isContains(String url) { return urlQueue.contains(url); } }
------------------2017.12.11补充类end-----------------------------------
主函数,运行此函数,开始爬虫
package webspilder; import java.sql.SQLException; import webspilder.UrlDataHanding; import webspilder.UrlQueue; public class Test { public static void main(String[] args) throws SQLException { String url = "http://baidu.com"; String url1 = "http://www.sina.com.cn"; String url2 = "http://finance.qq.com"; String url3 = "http://www.mi.com"; UrlQueue.addElem(url); UrlQueue.addElem(url1); UrlQueue.addElem(url2); UrlQueue.addElem(url3); UrlDataHanding[] url_Handings = new UrlDataHanding[10]; for(int i = 0 ; i < 10 ; i++) { url_Handings[i] = new UrlDataHanding(); new Thread(url_Handings[i]).start(); } } }
本文爬取百度,新浪,QQ财经和小米的网页。成功后保存在本地的D盘:
运行效果截图:
我保存成html文件了,大家也可以保存成txt文件。然后查看电脑D盘:
发现文件已经保存成功。这其中遇到的问题主要是正则表达式的书写,这个很重要大家要注意。
到这里只是把对应网站的页面抓取了下来,那么怎样从对应页面中获取自己想要的数据呢?
这里使用了java 的jsoup技术。
问题
你有一个HTML文档要从中提取数据,并了解这个HTML文档的结构。
方法
将HTML解析成一个Document之后,就可以使用类似于DOM的方法进行操作。示例代码:
File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");
Element content = doc.getElementById("content");
Elements links = content.getElementsByTag("a");
for (Element link : links) {
String linkHref = link.attr("href");
String linkText = link.text();
}
我们把上面接取的baidu.con.html用jsoup解析成document对象,然后使用DOM的方法接取我们想要的数据。
比如,我们想要网站中<input>标签的内容,那就用DOM方法自己获取把!!
最后用jsoup写了一个简单例子:
package webspilder; import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; /** * Descriptions * * @version 2017年4月1日 * @author * @since JDK1.6 * */ public class Jsouptemp { //从本地文件中获取 public static void getHrefByLocal() { File input = new File("D:\\www.mi.com.html"); Document doc = null; try { doc = Jsoup.parse(input,"UTF-8","http://www.oschina.net/"); //这里后面加了网址是为了解决后面绝对路径和相对路径的问题 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } Elements links = doc.select("a[href]"); for(Element link:links){ String linkHref = link.attr("href"); String linkText = link.text(); System.out.println(linkText+":"+linkHref); } } public static void main(String[] args) { getHrefByLocal(); } }
得到小米官网包含的a标签链接:
基本就到这里啦!拜了个拜!^_^