热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【java爬虫】爬虫+基于接口的网络爬虫

爬虫+基于接口的网络爬虫上一篇讲了【java爬虫】爬虫+jsoup轻松爬博客,该方式有个很大的局限性,就是你通过jsoup爬虫只适合爬静态网页,所以只能爬当前页面的所有新闻。如

爬虫+基于接口的网络爬虫

         上一篇讲了【java爬虫】---爬虫+jsoup轻松爬博客,该方式有个很大的局限性,就是你通过jsoup爬虫只适合爬静态网页,所以只能爬当前页面的所有新闻。如果需要爬一个网站所有信息,就得通过接口,通过改变参数反复调该网站的接口,爬到该网站的所有数据信息。

   本博客以爬金色财经新闻信息为对象,去爬取该网站从建站以来发表的所有新闻信息。下面会一步一步讲解。这里重点重点讲思路,最后我会提供完整源码。

 

第一步:找接口

     你要获得该网站所有新闻数据,第一步当然是获得接口,通过接口来获取所有信息。

  F12-->Network-->all,找到接口:https://api.jinse.com/v4/information/listcatelogue_key=news&limit=23&information_id=56630&flag=down&version=9.9.9

    对这三个参数做个说明:

     limit=23   代表每次调用该接口返回23条数据。

     information_id=56630  代表下面返回的23条数据是通过大于56630或者小于56630这个ID指来返回数据。

     flag=down   代表向下翻页 这里也就是指ID小于56630的23条数据。

 通过postMan测试

  输入:https://api.jinse.com/v4/information/list?catelogue_key=news&limit=2&information_id=0&flag=down&version=9.9.9(这里返回两条,id=0这里代表最新的两条数据)

返回json数据格式:

{
    "news": 2,
    "count": 2,
    "total": null,
    "top_id": 58300,
    "bottom_id": 58325,
    "list": [
        {
            "id": 58300,
            "title": "跨越牛熊的摆渡人:看金融IT服务如何助力加密货币交易",
            "short_title": "当传统金融IT服务商进入加密货币时代",
            "type": 1,
            "order": 0,
            "is_top": false,
            "extra": {
                "version": "9.9.9",
                "summary": "存量资金与投资者日渐枯竭,如何获取新用户和新资金入场,成为大小交易所都在考虑的问题。而交易深度有限、流动性和行情稳定性不佳,也成为横亘在牛熊之间的一道障碍。",
                "published_at": 1532855806,
                "author": "临渊",
                "author_avatar": "https://img.jinse.com/753430_image20.png",
                "author_id": 127939,
                "author_level": 1,
                "read_number": 27064,
                "read_number_yuan": "2.7万",
                "thumbnail_pic": "https://img.jinse.com/996033_image1.png",
                "thumbnails_pics": [
                    "https://img.jinse.com/996033"
                ],
                "thumbnail_type": 1,
                "source": "金色财经",
                "topic_url": "https://m.jinse.com/news/blockchain/219916.html",
                "attribute_exclusive": "",
                "attribute_depth": "深度",
                "attribute_spread": ""
            }
        },
        {
            "id": 58325,
            "title": "各路大佬怎样看待区块链:技术新武器应寻找新战场",
            "short_title": "各路大佬怎样看待区块链:技术新武器应寻找新战场",
            "type": 1,
            "order": 0,
            "is_top": false,
            "extra": {
                "version": "9.9.9",
                "summary": "今年年初由区块链社区引发的讨论热潮,成为全民一时热议的话题,罕有一项技术,能像区块链这样——在其应用还未大范围铺开、被大众直观感知时,就搅起舆论风暴,扰动民众情绪。",
                "published_at": 1532853425,
                "author": "新浪财经",
                "author_avatar": "https://img.jinse.com/581794_image20.png",
                "author_id": 94556,
                "author_level": 5,
                "read_number": 33453,
                "read_number_yuan": "3.3万",
                "thumbnail_pic": "https://img.jinse.com/995994_image1.png",
                "thumbnails_pics": [
                    "https://img.jinse.com/995994"
                ],
                "thumbnail_type": 1,
                "source": "新浪财经",
                "topic_url": "https://m.jinse.com/blockchain/219934.html",
                "attribute_exclusive": "",
                "attribute_depth": "",
                "attribute_spread": ""
            }
        }
    ]
}
接口返回信息

 

第二步:通过定时任务开启爬虫工作

@Slf4j
@Component
public class SchedulePressTrigger {

    @Autowired
    private CrawlerJinSeLivePressService crawlerJinSeLivePressService;

    /**
    * 定时抓取金色财经的新闻
    */
    @Scheduled(initialDelay = 1000, fixedRate = 600 * 1000)
    public void doCrawlJinSeLivePress() {

      //  log.info("开始抓取金色财经新闻, time:" + new Date());
        try {
            crawlerJinSeLivePressService.start();
        } catch (Exception e) {
          //  log.error("本次抓取金色财经新闻异常", e);
        }
      //  log.info("结束抓取金色财经新闻, time:" + new Date());
    }
}

 

第三步:主要实现类

/**
 * 抓取金色财经快讯
 * @author xub
 * @since 2018/6/29
 */
@Slf4j
@Service
public class CrawlerJinSeLivePressServiceImpl extends AbstractCrawlLivePressService implements
        CrawlerJinSeLivePressService {

    //这个参数代表每一次请求获得多少个数据
    private static final int PAGE_SIZE = 15;

    //这个是真正翻页参数,每一次找id比它小的15个数据(有写接口是通过page=1,2来进行翻页所以比较好理解一点,其实它们性质一样)
    private long bottomId;


    //这个这里没有用到,但是如果有数据层,就需要用到,这里我只是把它答应到控制台
    @Autowired
    private LivePressService livePressService;
    
    
    
    //定时任务运行这个方法,doTask没有被重写,所有运行父类的方法
    @Override
    public void start() {
        try {
            doTask(CoinPressConsts.CHAIN_FOR_LIVE_PRESS_DATA_URL_FORMAT);
        } catch (IOException e) {
          //  log.error("抓取金色财经新闻异常", e);
        }
    }


    @Override
    protected List crawlPage(int pageNum) throws IOException {
        // 最多抓取100页,多抓取也没有特别大的意思。
        if (pageNum >= 100) {
            return Collections.emptyList();
        }
        // 格式化翻页参数(第一次bottomId为0,第二次就是这次爬到的最小bottomId值)
        String requestUrl = String.format(CoinPressConsts.CHAIN_FOR_LIVE_PRESS_DATA_URL_FORMAT, PAGE_SIZE, bottomId);
Response response
= OkHttp.singleton().newCall( new Request.Builder().url(requestUrl).addHeader("referer", CoinPressConsts.CHAIN_FOR_LIVE_URL).get().build()) .execute(); if (response.isRedirect()) { // 如果请求发生了跳转,说明请求不是原来的地址了,返回空数据。 return Collections.emptyList(); } //先获得json数据格式 String respOnseText= response.body().string(); //在通过工具类进行数据赋值 JinSePressResult jinSepressResult = JsonUtils.objectFromJson(responseText, JinSePressResult.class); if (null == jinSepressResult) { // 反序列化失败 System.out.println("抓取金色财经新闻列表反序列化异常"); return Collections.emptyList(); } // 取金色财经最小的记录id,来进行翻页 bottomId = jinSepressResult.getBottomId(); //这个是谷歌提供了guava包里的工具类,Lists这个集合工具,对list集合操作做了些优化提升。 List pageListPresss = Lists.newArrayListWithExpectedSize(PAGE_SIZE); for (JinSePressResult.DayData dayData : jinSepressResult.getList()) { JinSePressData data = dayData.getExtra(); //新闻发布时间(时间戳格式)这里可以来判断只爬多久时间以内的新闻 long createTime = data.getPublishedAt() * 1000; Long timemill=System.currentTimeMillis(); // if (System.currentTimeMillis() - createTime > CoinPressConsts.MAX_CRAWLER_TIME) { // // 快讯过老了,放弃 // continue; // } SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String sd = sdf.format(new Date(createTime)); // 时间戳转换成时间 Date newsCreateTime=new Date(); try { //获得新闻发布时间 newsCreateTime = sdf.parse(sd); } catch (ParseException e) { e.printStackTrace(); } //具体文章页面路径(这里可以通过这个路径+jsoup就可以爬新闻正文所有信息了) String href = data.getTopicUrl(); //新闻摘要 String summary = data.getSummary(); //新闻阅读数量 String pres-s-readcount = data.getReadNumber(); //新闻标题 String title = dayData.getTitle(); pageListPresss.add(new PageListPress(href,title, Integer.parseInt(pres-s-readcount), newsCreateTime , summary)); } return pageListPresss; } }

AbstractCrawlLivePressService 类

 public abstract class AbstractCrawlLivePressService {
    String url;
    public void doTask(String url) throws IOException {
        this.url = url;
        int pageNum = 1;

        //通过 while (true)会一直循环调取接口,直到数据为空或者时间过老跳出循环
        while (true) {
            List newsList = crawlPage(pageNum++);
            // 抓取不到新的内容本次抓取结束
            if (CollectionUtils.isEmpty(newsList)) {
                break;
            }    
            //这里并没有把数据放到数据库,而是直接从控制台输出
            for (int i = newsList.size() - 1; i >= 0; i--) {
                PageListPress pageListNews = newsList.get(i);
                System.out.println(pageListNews.toString());
          
            }
        }
    }
    //这个由具体实现类实现
    protected abstract List crawlPage(int pageNum) throws IOException;

@Data
@AllArgsConstructor
@NoArgsConstructor
    public static class PageListPress {

        //新闻详情页面url
         private String href;
       //新闻标题
        private String title;
        //新闻阅读数量
        private int readCounts;
        //新闻发布时间
        private Date createTime;
        //新闻摘要
        private String summary;
    
        }
 }

JinSePressResult 

/**
 *在创建对象的时候一定要分析好json格式的类型
 *金色新闻的返回格式就是第一层有普通属性和一个list集合
 *在list集合中又有普通属性和一个extra的对象。
 */
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class JinSePressResult {

    private int news;
    private int count;
    @JsonProperty("top_id")
    private long topId;
    @JsonProperty("bottom_id")
    private long bottomId;
    //list的名字也要和json数据的list名字一致,否则无用
    private List list;

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class DayData {

        private String title;
        //这里对象的属性名extra也要和json的extra名字一致
        private JinSePressData extra;
        @JsonProperty("topic_url")
        private String topicUrl;
    }
}

这里需要注意两点

   (1)在创建对象时一定要先搞清楚json格式类型是对象里含有集合,或者集合中还有对象等等。

   (2) 你可以只定义你需要的属性字段,当你不能和json的属性名一致但类型不一致。比如上面你改成 List extra 这个时候序列化就会失败,因为json的extra明显是一个对象,而这边接受的确实一个集合。关键是属

性名一致 所以在赋值的时候就会报错,序列化失败。

 

第四步:看运行结果

   这里只是截取控制台输出的部分信息,通过这种方式可以获得该网站的所有新闻信息。同时我们已经获得具体新闻的URL,那么我们就可以通过JSOup来获取该新闻的所有具体信息。(完美)

 

第五步:数据库去重思路

   因为你不可能每一次爬取玩数据都直接放到数据库中,肯定要比较该条新闻数据库中是否已经存在,不存在才放到数据库中。思路如下:

   (1)数据库表中添加一个能辨别该新闻唯一的属性字段,比如jinse+bottomId组成唯一属性,或者该新闻具体页面路径URI组成唯一属性。

   (2)创建map集合,通过URI看数据库是否存在,有就,没有

   (3)在存储之前通过map.get(URI)如果为false则存储数据库中。

 

Git源码

 首先说明下,源码本人是通过Idea测试运行通过,这里用了lombok,你需要现在idea或者eclipse配置Lombok。

 源码地址:https://github.com/yudiandemingzi/panjiekou

 

 

想太多,做太少,中间的落差就是烦恼。想没有烦恼,要么别想,要么多做。中校【9】 


推荐阅读
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了腾讯最近开源的BERT推理模型TurboTransformers,该模型在推理速度上比PyTorch快1~4倍。TurboTransformers采用了分层设计的思想,通过简化问题和加速开发,实现了快速推理能力。同时,文章还探讨了PyTorch在中间层延迟和深度神经网络中存在的问题,并提出了合并计算的解决方案。 ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • {moduleinfo:{card_count:[{count_phone:1,count:1}],search_count:[{count_phone:4 ... [详细]
  • 装饰模式(Deocrator)     动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。    所谓装饰,就是一些对象给主题 ... [详细]
  • postman下载安装教程
    Postman是一款强大网页接口调试工具,我们在平时开发过程中经常会使用到,一般使用最多的是postman的客户端,实际上postman在谷歌浏览器上也提供了插件,可以不必要安装客 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • angular.element使用方法及总结
    2019独角兽企业重金招聘Python工程师标准在线查询:http:each.sinaapp.comangularapielement.html使用方法 ... [详细]
  • 软件测试工程师,需要达到什么水平才能顺利拿到 20k+ 无压力?
    前言最近看到很多应届生晒offer,稍有名气点的公司给出的价格都是一年30多W或者月薪20几k,相比之下工作几年的自己薪资确实很寒酸.根据我自己找工作经历,二线城市一般小公司招聘 ... [详细]
  • 范式转移:构建超级应用——胖应用 + 胖协议
    范式转移:构建超级应用——胖应用 + 胖协议 ... [详细]
author-avatar
张-苡_278
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有