java爬虫多线程redis队列(爬取国美网站的商品新闻)mobile.365-838.com

By admin in mobile.365-838.com on 2019年2月4日

心理,那一个妙不可言了。我们习惯认为心绪是非理性的,但荣格认为,心情恰恰和揣摩一样是悟性的,它象征了东西对人的价值,即情绪是一种价值的反映。赋予那种价值,是人的一种重点思想成效,它通过了剖析和判断的长河。电影《大话西游》中有句台词说:喜欢一个人索要理由啊?若是您给予一个人心情价值,觉得他/她对您而言很重点,“认为首要”其实就已经是市值判断了。

面前那篇爬虫文章用的是单线程没有用到其他一些比较提升功用的工具相比较遗憾,所以明日做了一个相比较完美的爬虫。首先谢谢
@天不生自己万古长那位小伙伴的留言,不然还真有点懒了。因为上班所以也不得不拔取星期一的年月来写了。其实本次构思了很久。本来是想爬Tmall的商品音讯,不过蒙受了一个坑就是ssl的证件验证,那里交融了半天终于绕过去了。不过由于天猫的范围比较严,ip直接被限定访问了。我也很无语,如若一致有同伴遇到了https请求的证书验证通过不停,提议去看一下这一篇博客,感觉写的不易。http://blog.csdn.net/u014256984/article/details/73330573
那里主要讲的就是经过java代码获取证书文件,然后将证书文件放入到jdk下边,具体我就不细说了。说一说前些天的主要。

发现有成千上万方可辨认的功效,对于发现的表面世界,荣格说“自家所精晓的表面世界是如此一个系统,它把从环境摄入的实际、材料与发现的的始末相联系,那是一个一定的种类,处理我的感觉成效所给予自己的外表事实,反之,内部领域则是一个处于意识内容与无意识(潜意识)假定进度回见的牵连系统”。荣格把发现的外部成效分为了三种:感觉、思维、心绪、直觉。

率先说一下自家的目的页面。国美的搜索页

Cy Twombly  |  美国

技术点

httpClient Jsoup
那几个都是爬虫最基本的,就不老生常谈了。那里我说一说用的新的技术点,以及新的技术点遭受了什么坑。

  • redis 以及redis的队列应用
    那里用redis首要的效应就是保存须求分析的url
    以及已经解析过的url八个系列。那里我遇上最多的题材,就是用四线程执行的时候出现redis链接重置的标题。网上查了一晃也远非一个合并的答案,我也只是根据决定台出口的错误新闻感觉可能是在二十四线程执行的时候,redis创建了反复接连。为啥会创建多次连接就会冒出重置的标题。我的推测就是因为redis本省是不帮衬windows的,只是微软在打了补丁的情形下才支撑。那可能有某些影响。那上面自身也尚无去探索。我的解决方案就是开创一个redis的单例情势。

  • mongodb
    先是说一下怎么要用mongodb

    1. mongodb是非关系型数据库。
    2. mongodb相对于关系型数据库他的频率要高很多广大。
    3. mongodb存储数据理论上是绝非上限的,当然那是理论。
    4. mongodb4.5未来是天生自带连接池的。
  • 线程池
    在拍卖三十二线程的难题的时候,假诺创立一个线程池管理线程。其实那里的功用是尤其好的。不过好是好用,坑却专门多,一定要专注对于有些数据开展操作的时候要开展约束的操作,为了保险数据的准确性。

说了那般多也感到有点词穷了,如故上代码。

  • redis的体系创立

package com.xdl.redisUtil;

import redis.clients.jedis.Jedis;

/**
 * 
* @ClassName: redisqueue
* redis队列
* @author liangchu
* @date 2018-1-6 上午11:52:44 
*
 */
public class RedisQueue {
    // 这是单例
    private static  Jedis jedis = RedisSingleton.getJedisInstance();

    /*public RedisQueue(){
        //连接本地的 Redis 服务    
        jedis = RedisSingleton.getJedisInstance();

    }*/

    //将未访问的url加入到toVisit表中(使用的是尾插法)
    public static void addToVisit(String url) {
        jedis.rpush("toVisit", url);
    }

    //将未访问的url弹出进行解析
    public static String getToVisit() {
        return jedis.lpop("toVisit");
    }

    //将已经解析过的url添加到已访问队列中
    public static void addVisited(String url) {
        jedis.rpush("visited", url);
    }

    //判断待访问url队列是否为空
    public static boolean toVisitIsEmpty() {
        Long length = jedis.llen("toVisit");

        if (length == 0) {
            return true;
        } else {
            return false;
        }
    }


}

package com.xdl.redisUtil;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import com.mongodb.MongoClient;

public class MultithreadCrawler {

    /**
     * @throws Exception 
     * @throws InterruptedException 
     * @Title: main
     * @Description: TODO(这里用一句话描述这个方法的作用)
     * @param @param args    参数
     * @return void 返回类型
     * @author  liangchu
     * @date 2018-1-6 下午12:19:53 
     * @throws
     */
    public static void main(String[] args) throws  Exception {

     //拿到种子链接 这里主要从这几个方面抓取数据
        List<String> strings = new ArrayList<String>();
        strings.add("手机");
        strings.add("男装");
        strings.add("女装");
        strings.add("电脑");
        strings.add("相机");
        strings.add("食品");
        //将种子链接写进redis数据库的待抓取列表
        for (String url : strings) {
            RedisQueue.addToVisit("http://search.gome.com.cn/search?question="+url+"&searchType=goods&page=1");
        }
        //创建一个收集线程的列表
        List<Thread> threadList = new ArrayList<Thread>();
        //创建线程的个数
        int threadNum = 5;
        // mongodb连接
        MongoClient mongo = new MongoClient("127.0.0.1", 27017);
        RunThread run = new RunThread();
        run.setThreads(threadNum,mongo);
        //创建5个线程,并对其进行收集
        for (int i = 0; i < threadNum; i++) {
            Thread thread = new Thread(run);
            thread.start();
            threadList.add(thread);
        }
        //main线程需要等待所有子线程退出
        while (threadList.size() > 0) {
            Thread child = threadList.remove(0);
            child.join();
        }
    }   
}
  • run函数

package com.xdl.redisUtil;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;

public class RunThread extends Thread {
    MongoClient mongo = null;
    //线程计数器需要对所有线程可见,是共享变量
    int threads = 0;
    //redis队列的对象,也是所有对象共享的变量
    //创建线程锁
    private static Object lock = new Object();
    public void setThreads(int threads,MongoClient mongo) {
        this.threads = threads;
        this.mongo = mongo;
    }

    @SuppressWarnings("deprecation")
    public void parseToVisitUrltoRedis() throws Exception {
        //用来保存新提取出来的url列表(此变量不应是共享变量,我们把它变为每个线程的私有变量)
        //我们应该知道的是在Java中哪些变量在线程之间是不共享的,参考资料:
        List<String> urlList = new ArrayList<String>();
        boolean flag = true;
        while (flag) {
            //从爬虫队列中取出待抓取的url
            if (!RedisQueue.toVisitIsEmpty()) {
                String url = RedisQueue.getToVisit();
                /**
                 * 对此url进行解析,提取出新的url列表
                 * 解析出来的url顺便就写进urlList中了
                 *
                 * 在这个过程中不要求保证同步,每个线程都负责解析自己所属的url,解析完成
                 * 之后将url写入自己的urlList之中,当在解析过程中发生阻塞,则切换到其他
                 * 线程,保证程序的高并发性。
                 */

             // 创建httpclient实例  
                CloseableHttpClient httpClient = HttpClients.createDefault();  
                // 创建httpget实例  
                HttpGet httpGet = new HttpGet(url); 
                // 执行http get 请求  
                CloseableHttpResponse response = null;  
                response = httpClient.execute(httpGet);  
                HttpEntity entity = response.getEntity();// 获取返回实体  
                // EntityUtils.toString(entity,"utf-8");//获取网页内容,指定编码  
                String html = EntityUtils.toString(entity, "UTF-8");  
                response.close();  
                httpClient.close();
                Document doc = Jsoup.parse(html);                               
                // 获取产品列表信息
                Element elementP =  doc.getElementById("product-box");
                // 获取产品列
                Elements elements = elementP.select("li[class=product-item]")
                        .select("div[class=item-tab-warp]");

                // 下一页的信息就存入redis队列当中 做下一次分析的url链接所用
                // 如果这个没有数据这个线程就退出
                if(elements.size() <=0){
                    flag = false;
                    return ;
                }
                for (Element element : elements) {
                    // 获取产品价格
                    String price = element.select("div[class=item-tab]").select("div[class=item-price-info]")
                            .select("p[class=item-price]")
                            .select("span[class=price asynPrice]").text();
                    // 获取产品名称 和产品链接
                    String producthref = element.select("p[class=item-name]")
                            .select("a[class=emcodeItem item-link]").attr("href");
                    String productTitle = element.select("p[class=item-name]")
                            .select("a[class=emcodeItem item-link]").attr("title");
                    // 评价人数
                    String productStatus = element.select("p[class=item-comment-dispatching]")
                            .select("a[class=comment]").text();
                    // 经营品牌
                    String product = element.select("p[class=item-shop]")
                            .text();
                    // 将这些信息存入mogondb中   
                    DB  db =  mongo.getDB("taobao");
                    DBCollection emp = db.getCollection("productinfo");
                    DBObject obj = new BasicDBObject();
                    obj.put("productTitle", productTitle);
                    obj.put("producthref", producthref);
                    obj.put("productStatus", productStatus);
                    obj.put("product", product);
                    obj.put("price", price);
                    emp.insert(obj);
                    // 这里我也纠结了好久要不要关,如果关了就会报错 所以最后就没关了如果各位有好的解决方案 记得告诉我O(∩_∩)O
                    //mongo.close(); 
                }
                // 这里是获取它的下一页,然后将下一页的连接加入到redis队列当中
               int page = Integer.parseInt(url.substring(url.lastIndexOf("=")+1))+1;
               String redisToVisit = url.substring(0, url.lastIndexOf("=")+1)+page;
               if(page >5){
                   flag = false;
                   return;
               }
                /**
                 * 在此同步块中主要进行提取出来的url的写操作,必须是同步操作,保证一个同
                 * 一时间只有一个线程在对Redis数据库进行写操作。
                 */
                synchronized(lock){
                    // 加入到redis队列中
                    RedisQueue.addToVisit(redisToVisit);
                }
            } else {
                //在改变线程计数器的值的时候必须保证线程的同步性
                synchronized (lock) {
                    //等待线程数的计数器的计数器减1
                    threads--;
                    //如果仍然有其他线程在活动,则通知此线程进行等待
                    if (threads > 0) {
                        /*调用线程的wait方法会将此线程挂起,直到有其他线程调用notify\
                        notifyAll将此线程进行唤醒*/
                        wait();
                        threads++;
                    } else {
                        //如果其他的线程都在等待,说明待抓取队列已空,则通知所有线程进行退出
                        notifyAll();
                        return;
                    }
                }
            }
        }
    }

    public void run() {
        //虽然run方法不能抛出异常,但是可以在run方法中进行try,catch
        try {
            parseToVisitUrltoRedis();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 主函数

package com.xdl.redisUtil;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import com.mongodb.MongoClient;

public class MultithreadCrawler {

    /**
     * @throws Exception 
     * @throws InterruptedException 
     * @Title: main
     * @Description: TODO(这里用一句话描述这个方法的作用)
     * @param @param args    参数
     * @return void 返回类型
     * @author  liangchu
     * @date 2018-1-6 下午12:19:53 
     * @throws
     */
    public static void main(String[] args) throws  Exception {

        //拿到种子链接 这里主要从 手机 服饰 电器 食品 这几个大的方面来抓取
        List<String> strings = new ArrayList<String>();
        strings.add("手机");
        strings.add("男装");
        strings.add("女装");
        strings.add("电脑");
        strings.add("相机");
        strings.add("食品");
        //将种子链接写进redis数据库的待抓取列表
        for (String url : strings) {
            RedisQueue.addToVisit("http://search.gome.com.cn/search?question="+url+"&searchType=goods&page=1");
        }
        //创建一个收集线程的列表
        List<Thread> threadList = new ArrayList<Thread>();
        //创建线程的个数
        int threadNum = 1;
        MongoClient mongo = new MongoClient("127.0.0.1", 27017);
        RunThread run = new RunThread();
        run.setThreads(threadNum,mongo);
        //创建5个线程,并对其进行收集
        for (int i = 0; i < threadNum; i++) {
            Thread thread = new Thread(run);
            thread.start();
            threadList.add(thread);
        }
        //main线程需要等待所有子线程退出
        while (threadList.size() > 0) {
            Thread child = threadList.remove(0);
            child.join();
        }
    }   
}

商品新闻列表.png

察觉有很二种作用,荣格认为那么些功能把发现划分成了左右多个精神领域。本文紧要介绍他对发现的外部功效的论述,卓殊有趣,它们提供了有的精辟的辨析人的角度,我以为它不仅仅对感情学家和精神分析专家的话有意义,也能让普通人更明白自己。

国美找寻页面.png

感觉到告诉咱们一个事物的留存。思维告诉大家相当东西是何等,心情则告诉那些东西对于我们的市值。其它还是可以有怎么着吧?”

总结

只能说进入了redis队列和mongodb存储数据
功用几乎要起飞了。15s不到就抓了1200条商品信息。因为有了上次的教训不敢抓得太久,所以只抓取了1200条。如若有就是封的同伙可以尝试,当然后果是自负。O(∩∩)O,终于弄完了全方位一天。下次投入quartz定时任务,那样获取股票,天气,航班什么的都足以赢得实时的了。若是有须求的同伴可以留言,有时间一定形成。good
night!!(\
^_^\mobile.365-838.com,) 嘻嘻

自我想开许多法学文章中描写的那么些心潮澎湃的读书人,情绪丰富的院校教书,还有历史上露脸的英才故事,相当能印证上边的讲述。

招来列表页.png

Cy Twombly  |  美国

各样人都有协调的偏好。有的人喜好动脑筋善于思考,有的人具有良好的情义成效并擅长交往,有的人善于观察事物的细小细节,有的人则易在一体化上把握事物的上进走向。

荣格的思想类型理论,是她的首要性学术观点,他把思想分成两种典型项目,并不是为了给人贴标签,而是为了能在进展思想分析和精神分析的经过中,更深切的摸底对方的想法、立场和落脚点,他以为在大家身上相对“低级”的效果与我们身上的古老人格相互换,“在大家的可被识别出的功能中,我们是文明人并具备自由意志,但当提到低级作用时,是一直谈不到自由意志的。在那种状态下,有的只是裸露的口子或至少是敞开的流派,任何东西都可能从中进入。”

在种种功用中,思维和心理是相互争辨的一对、感觉和直觉是互相争持的一对,人的一定偏好使得其中某项功效占据主导地位,那必然导致其相对成效不容许以相同不偏不党的水平存在。如若说一个人是思维型,并不是她指没有感情,荣格认为那类人平日兼有无可争辨的心境,简单被心境控制并不难激动,“假使你想尽量明白知识分子在家庭的行为,那就去问他的婆姨,她必然能告诉你多多东西”。

Cy Twombly  |  美国

思维型的人惊讶被心思控制,荣格认为相对于思考,这类人的情义“有一种古老的特性,在那一点上她像一个汉朝人——他是上下一心心态的被害者”。一个独立的有理智的人平时会害怕坠入情网,因为她也许会因为被爱意战胜而做出笨拙的行动,“因为她的真情实意只对远古或危险的女孩子起影响,那就是为何很多理智型的人赞同于与比他们智力低下的人联姻的由来。”思维型的人,或者说理智型的人,他们惶恐不安情绪,在理智上从不人得以克制他们,但他俩简单在心境方面被潜移默化。“为此,绝不要把一个人逼向心思借使她属于理智型的话。有理智的人用暴力控制心理,因为心境对于她相当危急。”

直觉,那是发现的多个外表作用中最奇妙的作用,它是一种预感的力量,每人都有,只是不自然人人会用到。“直觉那种效应使你看见实际上还看不见的事物”,“直觉是一种在正规景况下不会用到的效用”,而当股票交易员在操作股票、医师在认清病情、或是在原始森林中徒步,直觉便是使人时常得到赞助的功能了。“在您不能不处理陌生意况而又无既定的价值规范或现成的观念可遵守的时候,你就会借助直觉那种意义”。荣格认为直觉是通过人的下意识运作的,但实际是怎么样工作的,比如一个人是怎么知道她本来不应该知道的某种东西、为何会做预知性的梦,背后原理无从知晓。

Cy Twombly  |  美国

我们的“三观”,即人生观、世界观、价值观,都基于对社会风气的认识,那一个对外表的认识、对协调的回味,都是“意识”。每个人的觉察的视角差距,是何许导致了这个差别,那都是心境学家和振奋分析学家们感兴趣的难点。

沉凝,即对事物给出的称号以及概念
。感觉——感到事物存在;思维——判断事物是怎么着。

如此那般逻辑,若换成其余三体系型的人,便足以知晓,心绪型简单被考虑苦恼、被考虑纠缠;直觉型的人被实际苦恼,不可以脚踏实地;感觉型的人执着于现实生活,认为只有实在的东西才是真的。

感到是最简单精通的,它只告诉大家这几个事物在此地或在那边,属于感官带给我们的认识。

*
*

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 mobile.365-838.com 版权所有