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

  • 相关阅读:
    centOS 6.5下升级mysql,从5.1升级到5.6
    利用PHP脚本辅助MySQL数据库管理5-检查异常数据
    利用PHP脚本辅助MySQL数据库管理4-两个库表结构差异比较
    利用PHP脚本辅助MySQL数据库管理3-删除重复表索引
    利用PHP脚本辅助MySQL数据库管理2-表主键表索引
    利用PHP脚本辅助MySQL数据库管理1-表结构
    Linux驱动设计——内存与IO访问
    Linux驱动设计—— 驱动调试技术
    Linux系统编程@终端IO
    Linux驱动设计—— 内外存访问
  • 原文地址:https://www.cnblogs.com/seven007/p/6248578.html
Copyright © 2011-2022 走看看