一只垂直的小爬虫

news/2024/7/19 10:30:16 标签: 爬虫, 数据库, java

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

  • HttpClient 点击进入官方文档
  • Jsoup 点击进入官方文档
  • 多线程
  • jdbc

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

主函数代码如下:

java">
    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

java">/**
 *  下载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

java">
    /**
     * 解析源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条标题放在里面,总有那么一条放在了里面, 这个时候,就不得不去改刚才写好的规则

java">/**
     * 解析封装二级任务
     *
     * @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

java">/**
     * 持久化单个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称作是二级

java">
    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();
                    }

                }
            });

        }

转载于:https://www.cnblogs.com/ZhuChangwu/p/11150580.html


http://www.niftyadmin.cn/n/910014.html

相关文章

ms06-055漏洞-HeapSpray实战

ms06-055漏洞 漏洞介绍 微软的 IE5.0 以上的版本会支持一种向量标记语言&#xff08;VML&#xff09;来绘制图形。IE在解析畸形的VML 的时候会产生堆栈溢出的错误。如果利用者精心构造一个含有这样畸形的 VML 语句的网页&#xff0c;并骗取用户点击这样的网页&#xff0c;就可以…

一般线性秩统计量的方差函数 及其 极限分布

秩 及 线性秩统计量的概念见参考书1的P105或参考书2的P21-P23 问题引入 参考书2-P23-定理4.1下的&#xff08;4.5&#xff09;式给出了线性秩统计量的方差函数 而对于Wilcoxon符号秩和统计量 将其带入上式发现 这仍然是一个随机变量&#xff0c;显然是与方差的性质矛盾的。 我在…

转--简单工厂模式 Simple Factory

简单工厂模式的作用就是定义一个用于创建对象的接口 在简单工厂模式中,一个工厂类处于对产品类实例化调用的中心位置上,它决定那一个产品类应当被实例化。 先来看看它的组成&#xff1a;1) 工厂类角色&#xff1a;这是本模式的核心&#xff0c;含有一定的商业逻辑和判断逻辑。在…

Spark Streaming 概述

1.Spark Streaming 概述 1.1 Spark Streaming 是什么 Spark Streaming 用于流式数据的处理。Spark Streaming 支持的数据输入源很多&#xff0c;例如&#xff1a;Kafka、Flume、Twitter、ZeroMQ 和简单的 TCP 套接字等等。数据输入后可以用 Spark 的高度抽象原语如&#xff1a;…

Lafortune Paper In RenderMan

简单的尝试了下用Lafortune BRDF渲染纸张。为了公平起见统一把Ka设为0&#xff0c;Kd设为1.0&#xff0c;没有Specular项。使用3Delight的免费Licence和学习版maya 8.5&#xff0c;所以没有版权问题&#xff0c;但没有出阴影&#xff0c;所以看起来比较奇怪。效果对比如下&…

矩阵(矢量)求导的链式法则及应用

矢量对矢量求导的定义 链式求导法则 链式法则的应用1&#xff1a;Jacobi矩阵的逆 链式法则的应用2&#xff1a;标量对矩阵的求导

2014年百度之星程序设计大赛 - 资格赛 1002 Disk Schedule(双调欧几里得旅行商问题)...

Problem Description 有非常多从磁盘读取数据的需求&#xff0c;包含顺序读取、随机读取。为了提高效率&#xff0c;须要人为安排磁盘读取。然而&#xff0c;在现实中&#xff0c;这样的做法非常复杂。我们考虑一个相对简单的场景。磁盘有很多轨道&#xff0c;每一个轨道有很多…