zoukankan      html  css  js  c++  java
  • 知乎爬虫之5:爬虫优化

    本文由博主原创,转载请注明出处

    知乎爬虫系列文章:

    1. 知乎爬虫之1:开篇序言
    2. 知乎爬虫之2:爬虫流程设计
    3. 知乎爬虫之3:请求分析
    4. 知乎爬虫之4:抓取页面数据
    5. 知乎爬虫之5:爬虫优化

    github爬虫项目(源码)地址(已完成,关注和star在哪~):

    附赠之前爬取的数据一份(mysql): 链接: 只下载不点赞,不star,差评差评~蓝瘦香菇)

    1. 使用多线程加速

    什么,爬虫爬起来数据太慢了,怎么办?你那当然是开启多线程了。那么多线程是什么我就不介绍了。如果还不知道的,请左移多线程百度百科
    恩,知道了多线程,但是多线程如果自己控制的话,会很不好控制,所以咱们还需要两个线程池,一个负责拿到个人信息,一个负责获取用户的token。接下来让咱们之前写的ParserBase类实现Runnable,然后在ParserFollower里和ParserUserInfo里分别实现run方法,其实也很简单了,就是把之前的爬去逻辑,丢到run方法里。然后咱们就开启了多线程之旅。
    但是在存取数据的时候会遇到很多问题,比如数据会重复,这就出现了脏数据,那么咱们就是适当的加锁。来保证数据的干净。

    2. 使用队列减少数据库访问

    如果说数据重复的问题解决的,那么咱们还有一个大问题,因为两次爬到的可能是同一个人,但是一份在数据库里,一份在正在跑的内存里,怎么办,当然是要连接下数据库,然后判断是数据是否已经在数据库中存在了,那么在多线程且获取速度很快的情况下,那么将会频繁访问数据库,造成速度缓慢,数据库链接数过多,cpu使用率过大,那咱们怎么除处理这个问题呢?
    首先大家都一定知道,在现在内存非常大的今天,咱们完全可以把一部分数据直接缓存下载,而且程序访问内存的代价要比访问数据库的代价要小的太多。
    因此,咱们可以用一个list把咱们已经有的token存下来,然后每次去这个list里去做验证,这样,就减少了数据库的访问了频率。

    3. 实现LRU提高缓存命中率

    如上所说,用一个list的确是起到了减少数据库访问的目的,但是效果好像不如想象那么好,因为数据库里100k的数据,咱们不可能把所有数据都缓存下载,那么怎么才能在优化下呢?
    这时候就要合理的处理这个list了,自己实现一个LRU缓存,来调高命中率。
    那么lru是什么?y一句话LRU是Least Recently Used 近期最少使用算法,也叫淘汰算法。
    具体实现和思想可见缓存淘汰算法–LRU算法
    按照这个思想,其实还有个优化,那就是根据碰撞次数和上次碰撞时间进行移除。咱们只是实现简单的,具体实现在MatrixSeven/ZhihuSpider


    ========关于获取页面数据=======

    咱们上一篇分析了知乎的登陆请求和如何拿到粉丝/关注的请求,那么咱们这篇就来研究下如何拿利用Jsoup到咱们想要的数据。
    那么咱们说下,首先请求关注者和粉丝者是pcweb版本的,但是获取页面的是手机页面的。
    好,正题:

    1.什么是Jsoup

    jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

    2. HttpClient请求模拟

    HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

    3.走起页面

    首先模拟手机浏览器的UA。就是让咱们打开的页面返回的是移动端的页面效果,那么最应该怎么怎么做呢?其实服务器判定你是ie还是chrome还是firefox是根据请求头里面的UA实现的,因此咱们要找一个手机浏览器的UA。。
    咱们可以某度一下或者直接在浏览器里面直接f12,模拟移动端,然后看请求参数:

    User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36
    

    妥妥的没问题:

    那咱们如何将这句体现到程序里面呢?
    简单,在咱们拿到get对象后直接设置:

     httpGet.setHeader("User-Agent", "Mozilla/5.0 
            (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 
            (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36");
    

    就ok了,然后咱们就可以用jsoup来拿咱们想要的元素了,jsoup的语法和jq如出一辙。
    咱们直接对着页面,右击咱们想要的元素,选择审查元素,然后用jq的选择器选出来就好了。
    可以参考jQuery 选择器

    4.拿到关注者

    直接get咱们之前分析的请求地址

    https://www.zhihu.com/api/v4/members/Sweets07/followers?per_page=10&
    include=data%5B%2A%5D.employments%2Ccover_url%2Callow_message%2Canswer_coun
    t%2Carticles_count%2Cfavorite_count%2Cfollower_count%2Cgender%2Cis_followe
    d%2Cmessage_thread_token%2Cis_following%2Cbadge%5B%3F%28type%3Dbest_answerer
    %29%5D.topics&limit=10&offset=30
    

    不过要记得替换用户名字和在请求头里加入cookie的最后一段zc_0
    然后请求数据返回的是json

    {
      "paging": {
        "is_end": false,
        "next": "https://www.zhihu.com/api/v4/members/Sweets07/followers?per_page=10&include=data%5B%2A%5D.answer_count%2Carticles_count%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F%28type%3Dbest_answerer%29%5D.topics&limit=10&offset=20",
        "previous": "https://www.zhihu.com/api/v4/members/Sweets07/followers?per_page=10&include=data%5B%2A%5D.answer_count%2Carticles_count%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F%28type%3Dbest_answerer%29%5D.topics&limit=10&offset=0",
        "is_start": false,
        "totals": 398
      },
      "data": [
        {
          "is_followed": true,
          "avatar_url_template": "https://pic1.zhimg.com/da8e974dc_{size}.jpg",
          "name": "陈晓峰",
          "url": "",
          "type": "people",
          "user_type": "people",
          "answer_count": 0,
          "url_token": "chen-xiao-feng-84",
          "headline": "阿里巴巴,分布式数据库,",
          "avatar_url": "https://pic1.zhimg.com/da8e974dc_is.jpg",
          "is_following": false,
          "is_org": false,
          "follower_count": 14,
          "badge": [],
          "id": "ff02ea0544901a9ddfcb7ba60c73b673",
          "articles_count": 0
        }
      ]
    }
    

    这个数据包括了下次请求地址,上次请求地址,时候是开始,时候是结束,共有多少粉丝,关注人基本信息,
    因此咱们可以在一个while里来获得所有粉丝数:
    流程:

    1. 第一次获取数据
    2. 获取is_end字段
    3. 判断is_end时候为true
    4. 根据is_end判断是否继续循环
    5. 如果循环,更新is_end,更新下次请求连接

    一套下来,就能拿到一个用户的所有粉丝了。

    吾爱Java(QQ群):170936712

  • 相关阅读:
    mysql命令集锦
    linux 删除文件名带括号的文件
    linux下的cron定时任务
    struts2文件下载的实现
    贴一贴自己写的文件监控代码python
    Service Unavailable on IIS6 Win2003 x64
    'style.cssText' is null or not an object
    "the current fsmo could not be contacted" when change rid role
    远程激活程序
    新浪图片病毒
  • 原文地址:https://www.cnblogs.com/seven007/p/6248578.html
Copyright © 2011-2022 走看看