java网络爬虫-总结
来源:互联网 发布:快速排序算法 图解 编辑:程序博客网 时间:2024/05/17 20:13
从今年的三月份,正式接手项目。主要工作涉及到网页抓取,一直想花时间总结一下。但是博主太懒了..........
要写一篇博客很难的....思路稍有不畅,就没有办法继续写不下去了。网站上高手有的是,总不能拿一篇狗屁不通的文章糊弄大家吧!
我擦咧,把自己心里想的话不小心说出来了!
反正我是鄙视那些花哨的空洞的东西!可以说深恶痛绝。......咳咳.......感觉压力越来越大,装逼的感觉很爽哒.......
看来我多虑了,写的文章不知道有没有人看呢。没人看也罢,权当备忘。(当然希望有人看,有人评论了),每每看到那些大牛写的文章,我都羡慕的口水直流。
围观(●>∀<●): 我擦咧,废话够没?滚粗....... 额,好吧 ....我还没有思路马上就来。
其实我想对网站的的抓取做一个类型区分<主要是根据我抓取的网站的经验>:
1 普通的网站,主要是要抓取的数据直接可以通过dom的解析获取。这类网站的抓取比较容易,关键点dom的解析。用正则获取想要的数据会很复杂,有更好的方式解决这个问题。这类可以用jsoup(注: jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提 供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操数据),一个简单易用的java解析框架。
2 用js封装数据的网站。这类网站如果直接获取html的话,你要的数据根本得不到。其实一般情况下在浏览器中看到的页面,是渲染过的页面。dom的部分的呈现用js控制的,数据是放js对象中的。针对这种网站,关键是js的解析。由于java本身在1.5版本的时候就已经集成了,一个Rhino的纯java实现的js引擎。可以利用Rhino解释javaScript脚本。又因为js的数据本身的封装的特点,我更喜欢把js对象转化为json处理。(用jackson这个框架和方便)。
3 反爬的网站。有些网站有反扒的策略,这种情况是最麻烦的。需要仔细的分析和琢磨。
要抓取网站上的有用数据,还有一个很重要,抓取的效率,这个问题的解决用多线程和任务队列的东西。
涉及到时候,我会慢慢讲。
基本的页面抓取----主要是jsoup,通常情况下接手到一个新的东西,我会直接在官网上看文档。这其中的原因
只有经历过的人才能明白。胡乱的在网上搜的话,会有各种问题。每个人理解的层次都不一样,这样就有可能曲解原作者的
意图。这不仅仅是因为不同的人的水平参差不齐,更重的要原因是不同的人面对情景不同.我花了很久才明白这个道理。
jsoup结构很简洁。在官网上下载的zip文件中由源码。
1 获取执行的url的document内容: http://www.open-open.com/jsoup/selector-syntax.htm 这里有详细的介绍。
Document doc = Jsoup.connect(url).get();
2 然后你就可以通过提供的一堆的选择器获取你想要的数据:
File input = new File("/tmp/input.html");Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");Elements links = doc.select("a[href]"); //带有href属性的a元素Elements pngs = doc.select("img[src$=.png]"); //扩展名为.png的图片Element masthead = doc.select("div.masthead").first(); //class等于masthead的div标签Elements resultLinks = doc.select("h3.r > a"); //在h3元素之后的a元素
js封装的页面抓取:
1 首先要获取js的内容。用httpClient读取js内容(代码)
String jsContent = null; /* //HttpClient httpClient = new HttpClient(); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); HttpClient httpClient = new HttpClient(connectionManager); GetMethod getMethod= new GetMethod(url); //是设置系统参数 HttpClientParams httpClientParams = new HttpClientParams(); // 设置读取内容时间超时 httpClientParams.setSoTimeout(10000); // 设置系统提供的默认恢复策略,在发生异常时候将自动重试3次 httpClientParams.setParameter( HttpMethodParams.RETRY_HANDLER, //重复操作控制 new DefaultHttpMethodRetryHandler() ); */ HttpClient httpClient = new HttpClient(); GetMethod getMethod = new GetMethod(url); getMethod.addRequestHeader("Content-Type", "text/html; charset="+encode); getMethod.addRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 5.1; rv:28.0) Gecko/20100101 Firefox/28.0"); try { int statusCode = httpClient.executeMethod(getMethod); if(statusCode == 200){ byte[] responseBody = getMethod.getResponseBody(); jsContent = new String(responseBody,encode); } } catch (IOException e) { throw e; }finally{ getMethod.releaseConnection(); }
2 分析数据对象,加载引擎并执行js,获取数据对象json,jackson封装成java对象。
Context cx = Context.enter(); try { Scriptable scope = cx.initStandardObjects(); Object rs1 = cx.evaluateString(scope, response, "arr", 1, null);//执行就脚本 Object arr = scope.get("arr", scope); if (arr == Scriptable.NOT_FOUND) { logger.error("js数据解析失败---"+url); }else{ String groupData = (String)NativeJSON.stringify(cx, scope, arr, null, null); JSONArray groups = JSONArray.fromObject(groupData); } } }catch(Exception e){ e.printStackTrace(); }finally{ cx.exit(); }
4 保存数据。
针对反扒网站的抓取:
1 限制单位时间内ip的访问次数,即是访问的频率。解决方案,可以用代理,在某些时候只需要简单的延时操作。
方式一 :网上有给出用拨号获取ip的方式解决,很多时候这种方式是不可行的。不过还是要贴一下代码:
import java.io.BufferedReader; import java.io.InputStreamReader; import org.apache.log4j.Logger; /** * * ADSL拨号上网 * Windwos操作系统需要是GBK编码 * @author yijianfeng * */ public class ConnectAdslNet { static Logger logger = Logger.getLogger(ConnectAdslNet.class); /** * 执行CMD命令,并返回String字符串 */ public static String executeCmd(String strCmd) throws Exception { Process p = Runtime.getRuntime().exec("cmd /c " + strCmd); StringBuilder sbCmd = new StringBuilder(); //注意编码 GBK BufferedReader br = new BufferedReader(new InputStreamReader(p .getInputStream(),"GBK")); String line; while ((line = br.readLine()) != null) { sbCmd.append(line + "\n"); } return sbCmd.toString(); } /** * 连接ADSL */ public static boolean connectAdsl(String adslTitle, String adslName, String adslPass) throws Exception { System.out.println("正在建立连接."); String adslCmd = "rasdial " + adslTitle + " " + adslName + " " + adslPass; String tempCmd = executeCmd(adslCmd); // 判断是否连接成功 if (tempCmd.indexOf("已连接") > 0) { System.out.println("已成功建立连接."); return true; } else { System.out.println(tempCmd); System.out.println("建立连接失败"); return false; } } /** * 断开ADSL */ public static boolean disconnectAdsl(String adslTitle) throws Exception { String disconnectAdsl = "rasdial " + adslTitle + " /disconnect"; String result = executeCmd(disconnectAdsl); if (result.indexOf("没有连接")!=-1){ System.out.println(adslTitle + "连接不存在!"); return false; } else { System.out.println("连接已断开"); return true; } } /** * adsl重新拨号,支持失败不断重拨 * @param args * @throws Exception */ public static boolean reconnectAdsl(String adslTitle, String adslName, String adslPass){ boolean bAdsl = false; try { disconnectAdsl(adslTitle); Thread.sleep(3000); bAdsl = connectAdsl(adslTitle,adslName,adslPass); Thread.sleep(3000); int i = 0; while (!bAdsl){ disconnectAdsl(adslTitle); Thread.sleep(3000); bAdsl = connectAdsl(adslTitle,adslName,adslPass); Thread.sleep(3000); if(i>5){ break; } } }catch(Exception e){ logger.error("ADSL拨号异常:", e); } return bAdsl; } public static void main(String[] args) throws Exception { // reconnectAdsl("宽带","adsl账号","密码"); } }
方式二:用代理
// 创建 HttpClient 的实例HttpClient httpClient = new HttpClient();// 代理的主机(实际上是一个代理的数组,网上有很多提供代理服务器的)// 推荐:http://www.xici.net.co/ 动态爬去这个网站的代理ip,然后使用,也可以购买// 我并没有试过,不过应该是可行的。主要是提供一种解决问题的思路ProxyHost proxy = new ProxyHost("117.59.224.62", 80);// 使用代理httpClient.getHostConfiguration().setProxyHost(proxy);// 创建Get连接方法的实例HttpMethod getMethod = new GetMethod("http://vip.bet007.com/OverDown_n.aspx?id=279403");// 使用系统提供的默认的恢复策略getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,new DefaultHttpMethodRetryHandler());try {// 请求URISystem.out.println("executing request " + getMethod.getURI());// 执行getMethodint status = httpClient.executeMethod(getMethod);System.out.println("status:" + status);// 连接返回的状态码if (HttpStatus.SC_OK == status) {System.out.println("Connection to " + getMethod.getURI()+ " Success!");// 获取到的内容//String responseBody = getMethod.getResponseBodyAsString(); byte[] responseBody = getMethod.getResponseBody(); String html=new String(responseBody,"gbk");System.out.println("==="+html);}} catch (Exception e) {e.printStackTrace();} finally {// 释放连接getMethod.abort();}
多线程多任务并发执行的爬虫:
public class Grab_Team {public static void main(String[] args){ // 构造一个线程池 /* ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 5,//corePoolSize 10,//maxPoolSize 60l,//线程活跃时间 60s TimeUnit.SECONDS, //活跃时间的度量 new LinkedBlockingDeque<Runnable>(),//无界队列 同步 ,优先执行,核心线程全部占用时增加新线程到最大值 //此时如果所有的线程被占用,加入执行队列(队列无界)。 new ThreadPoolExecutor.CallerRunsPolicy() //由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序 //此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。 //如果该线程池尚未终止,则立即执行该队列的run方法 );*/ ThreadPoolExecutor threadPool = new ThreadPoolExecutor(20, 40, 60l, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()); //ExecutorService threadPool = Executors. newSingleThreadExecutor();//单线程调试 for(int i = 0;i < teamIds.size();i++){threadPool.execute(new GrabTeam(teamIds.get(i))); }}}class GrabTeam implements Runnable, Serializable{private Logger logger = Logger.getLogger(GrabTeam.class); public void run() { //doSomeThing }}
- java网络爬虫-总结
- Java网络爬虫-总结
- 网络爬虫技术总结
- [Python]网络爬虫总结
- Java的网络爬虫
- java网络爬虫
- Java网络爬虫
- java---网络爬虫讲解
- java网络爬虫程序
- Java学习: 网络爬虫
- Java网络爬虫初级
- java网络爬虫
- Java网络爬虫
- Java网络爬虫
- java网络爬虫:
- java 网络爬虫框架
- Java 网络爬虫技术
- java---网络爬虫讲解
- spring组件扫描<context:component-scan/>使用详解
- C语言快速排序算法代码分析
- 先进先出队列(链表实现)
- Java初始化的例子分析
- SequenceInputStream类(对包含多个对象内的文件连续进行读取)
- java网络爬虫-总结
- hibernate4.2关于sessionFactory的一点变化
- 16/32位MD5加密工具类
- copy_to_user和copy_from_user
- JS实现随机显示一个数组中的数值;
- 大数据Lambda架构
- Android开发大牛们的博客地址
- JAVA 控制台打印三角形
- TCP/IP&HTTP&Socket原理总结