一只垂直的小爬虫

这只垂直的小爬虫,使用如下实现

实现的思路很简单,我从主函数开始简单叙述一下整个运行流程,第一步:收集需要爬取的url地址,容器我选择的是ConcurrentLinkedQueue非阻塞队列,它底层使用Unsafe实现,要的就是它线程安全的特性

主函数代码如下:


    static String url = "http://www.qlu.edu.cn/38/list.htm";
    // 添加url任务
      public static ConcurrentLinkedQueue<String>  add( ConcurrentLinkedQueue<String> queue){
            for (int i=1;i<=19;i++){
                String subString = StringUtils.substringBefore(url, ".htm");
                queue.add(subString+i+".htm");
            }
          return queue;
      }
      
public static void main(String[] args) throws IOException {
        ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue();
        queue.add(url);
        ConcurrentLinkedQueue<String> newQueue = add(queue);
        // 多线程下载解析
        TPoolForDownLoadRootUrl.downLoadRootTaskPool(queue);

    }

第二步:把url列表丢线程池:

我使用的线程池是newCachedThreadPool 根据提交的任务数,动态分配线程

线程池里面干了这么几件事,下载源html

/**
 *  下载html的业务实现
 * @Author: Changwu
 * @Date: 2019/3/24 11:13
 */
public class downLoadHtml {
    public static Logger logger = Logger.getLogger(downLoadHtml.class);
    /**
     * 根据url 下载网页源码
     * @param url
     * @return
     */
    public static String downLoadHtmlByUrl(String url) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        //设置请求头
        httpGet.setHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");

        CloseableHttpResponse response = httpClient.execute(httpGet);
        logger.info("请求"+url+"状态码为"+response.getStatusLine().getStatusCode());
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, "utf-8");
        return  result;
    }

解析rootUrl,目的是拿到新闻主页的url,因为新闻的正文,在那里面,边解析遍封装RootBean


    /**
     * 解析源html.封装成一级Bean对象并返回
     *
     * @param sourceHtml
     * @return
     */
    public static List<RootBean> getRootBeanList(String sourceHtml) {
        LinkedList<RootBean> rootBeanList = new LinkedList<>();
        Document doc = Jsoup.parse(sourceHtml);
        Elements elements = doc.select("#wp_news_w6 ul li");
        String rootUrl = "http://www.qlu.edu.cn";

        for (Element element : elements) {
            RootBean rootBean = new RootBean();
            // 获取url并拼装
            String href = element.child(0).child(0).attr("href");
            // 获取title
            String title = element.text();
            String[] split = title.split("\\s+");
            //封装
            System.out.println(title);

            if (split.length >= 2) {
                String s = element.outerHtml();
                String regex = "class=\"news_meta\">.*";
                Pattern compile = Pattern.compile(regex);
                Matcher matcher = compile.matcher(s);
                if (matcher.find()) {

                    String group = matcher.group(0);
                    String ss = StringUtils.substring(group, 18);
                    ss = StringUtils.substringBefore(ss, "</span> </li>");
                    rootBean.setPostTime(ss);
                }

            }


            rootBean.setTitle(split[0]);
            rootBean.setUrl(rootUrl + href);

            rootBeanList.add(rootBean);
            /*System.out.println();
            System.out.println(split[0]);
            System.out.println();*/
        }
        return rootBeanList;
    }

类似,处理二级任务,这里使用到了正则表达式,原来没好好学,今天用的时候,完全蒙,还好慢慢悠悠整出来了,这块这要是观察源html,根据特性,使用jsoup提供的选择器选择,剪切,拼接出我们想要的内容,然后封装

为啥说是垂直的小爬虫,它只适合爬取我学校新闻,看下面的代码,没办法,只能拼凑剪切,最坑的是,100条新闻中,99条标题放在里面,总有那么一条放在了里面, 这个时候,就不得不去改刚才写好的规则

/**
     * 解析封装二级任务
     *
     * @param htmlSouce
     * @return
     */
    public static List<PojoBean> getPojoBeanByHtmlSource(String htmlSouce, RootBean bean) {

        LinkedList<PojoBean> list = new LinkedList<>();
        PojoBean pojoBean = new PojoBean();

        // 解析
        Document doc = Jsoup.parse(htmlSouce);

        // 编辑
        Elements elements1 = doc.select(".arti_metas");

        for (Element element : elements1) {

            String text = element.text();

            // 编辑
            String regex = "(责任编辑:.*)";
            Pattern compile = Pattern.compile(regex);
            Matcher matcher = compile.matcher(text);
            String editor = null;
            if (matcher.find()) {
                //System.out.println(matcher.group(group));
                editor = matcher.group(1);
                editor = StringUtils.substring(editor, 5);
                //System.out.println(editor);
            }

            // 作者
            regex = "(作者:.*出处)";
            compile = Pattern.compile(regex);
            matcher = compile.matcher(text);
            String author = null;
            if (matcher.find()) {
                //System.out.println(matcher.group(group));
                author = matcher.group(1);
                author = StringUtils.substring(author, 3);
                author = StringUtils.substringBefore(author, "出处");
                //System.out.println(author);
            }

            // 出处
            regex = "(出处:.*责任编辑)";
            compile = Pattern.compile(regex);
            matcher = compile.matcher(text);
            String source = null;
            if (matcher.find()) {
                source = matcher.group(1);
                source = StringUtils.substring(source, 3);
                source = StringUtils.substringBefore(source, "责任编辑");
                //  System.out.println(source);
            }

            // 正文
            Elements EBody = doc.select(".wp_articlecontent");
            String body = EBody.first().text();
            // System.out.println(body);

            // 封装
            pojoBean.setAuthor(author);
            pojoBean.setBody(body);
            pojoBean.setEditor(editor);
            pojoBean.setSource(source);
            pojoBean.setUrl(bean.getUrl());
            pojoBean.setPostTime(bean.getPostTime());
            pojoBean.setTitle(bean.getTitle());
            list.add(pojoBean);
        }
        return list;
    }
}

持久化,使用的是底册的JDBC

/**
     * 持久化单个pojo
     * @param pojo
     */
    public static void insertOnePojo(PojoBean pojo) throws ClassNotFoundException, SQLException {
        // 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spider", "root", "root");
        String sql = "insert into qluspider (title,url,post_time,insert_time,author,source,editor,body) values (?,?,?,?,?,?,?,?)";
        PreparedStatement ps = connection.prepareStatement(sql);
        // 填充sql
        ps.setString(1,pojo.getTitle());
        ps.setString(2,pojo.getUrl());
        // 把字符串转换成日期
        ps.setTimestamp(3,new java.sql.Timestamp(SpiderUtil.stringToDate(pojo.getPostTime()).getTime()));
        ps.setTimestamp(4,new java.sql.Timestamp(new Date().getTime()));
        ps.setString(5,pojo.getAuthor());
        ps.setString(6,pojo.getSource());
        ps.setString(7,pojo.getEditor());
        ps.setString(8,pojo.getBody());

        ps.execute();

        connection.close();

    }

拿到的新的url称作是二级


    public static Logger logger = Logger.getLogger(TPoolForDownLoadRootUrl.class);

    /**
     * 下载,解析 根url的线程池
     */
    public static void downLoadRootTaskPool(ConcurrentLinkedQueue queue) {
        ExecutorService executor = Executors.newCachedThreadPool();
        //ExecutorService executor = Executors.newFixedThreadPool(5);
        for (  int i=1;i<=queue.size();i++)
        {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        logger.info("1号线程池开启,将要下载解析root任务");
                        // 获取根任务url
                        String url = (String) queue.poll();

                        logger.info("根URL==" + url);
                        if (StringUtils.isNotBlank(url)) {
                            // 下载当前url对应的rootHtml
                            String sourceHtml = downLoadHtml.downLoadHtmlByUrl(url);
                            // 解析rootHtml里面所有的RootBean对象
                            List<RootBean> rootBeanList = parseHtmlByJsoup.getRootBeanList(sourceHtml);
                            // 二级任务开始
                            for (RootBean rootBean : rootBeanList) {
                                logger.info(this + "进入二级任务");
                                String subUrl = rootBean.getUrl();
                                // 下载二级任务 html
                                String htmlSouce = downLoadHtml.downLoadHtmlByUrl(subUrl);
                                // 解析封装
                                List<PojoBean> pojoList = parseHtmlByJsoup.getPojoBeanByHtmlSource(htmlSouce, rootBean);
                                // 持久化
                                logger.info(this + "将持久化" + subUrl + "中的二级任务");
                                Persistence.insertPojoListToDB(pojoList);
                                logger.info("持久化完成.......");
                            }
                        }
                    } catch (IOException e) {
                        System.out.println();
                        e.printStackTrace();
                    }

                }
            });

        }
posted @ 2019-07-08 13:13  赐我白日梦  阅读(284)  评论(0编辑  收藏  举报