本文由博主原创,转载请注明出处html
知乎爬虫系列文章:mysql
github爬虫项目(源码)地址(已完成,关注和star在哪~):https://github.com/MatrixSeven/ZhihuSpiderjquery
附赠以前爬取的数据一份(mysql): 连接:https://github.com/MatrixSeven/ZhihuSpider 只下载不点赞,不star,差评差评~蓝瘦香菇)git
什么,爬虫爬起来数据太慢了,怎么办?你那固然是开启多线程了。那么多线程是什么我就不介绍了。若是还不知道的,请左移多线程百度百科。
恩,知道了多线程,可是多线程若是本身控制的话,会很很差控制,因此我们还须要两个线程池,一个负责拿到我的信息,一个负责获取用户的token。接下来让我们以前写的ParserBase类实现Runnable,而后在ParserFollower里和ParserUserInfo里分别实现run方法,其实也很简单了,就是把以前的爬去逻辑,丢到run方法里。而后我们就开启了多线程之旅。
可是在存取数据的时候会遇到不少问题,好比数据会重复,这就出现了脏数据,那么我们就是适当的加锁。来保证数据的干净。github
若是说数据重复的问题解决的,那么我们还有一个大问题,由于两次爬到的多是同一我的,可是一份在数据库里,一份在正在跑的内存里,怎么办,固然是要链接下数据库,而后判断是数据是否已经在数据库中存在了,那么在多线程且获取速度很快的状况下,那么将会频繁访问数据库,形成速度缓慢,数据库连接数过多,cpu使用率过大,那我们怎么除处理这个问题呢?
首先你们都必定知道,在如今内存很是大的今天,我们彻底能够把一部分数据直接缓存下载,并且程序访问内存的代价要比访问数据库的代价要小的太多。
所以,我们能够用一个list把我们已经有的token存下来,而后每次去这个list里去作验证,这样,就减小了数据库的访问了频率。web
如上所说,用一个list的确是起到了减小数据库访问的目的,可是效果好像不如想象那么好,由于数据库里100k的数据,我们不可能把全部数据都缓存下载,那么怎么才能在优化下呢?
这时候就要合理的处理这个list了,本身实现一个LRU缓存,来调高命中率。
那么lru是什么?y一句话LRU是Least Recently Used 近期最少使用算法,也叫淘汰算法。
具体实现和思想可见缓存淘汰算法–LRU算法。
按照这个思想,其实还有个优化,那就是根据碰撞次数和上次碰撞时间进行移除。我们只是实现简单的,具体实如今MatrixSeven/ZhihuSpider算法
我们上一篇分析了知乎的登录请求和如何拿到粉丝/关注的请求,那么我们这篇就来研究下如何拿利用Jsoup到我们想要的数据。
那么我们说下,首先请求关注者和粉丝者是pcweb版本的,可是获取页面的是手机页面的。
好,正题:sql
jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套很是省力的API,可经过DOM,CSS以及相似于jQuery的操做方法来取出和操做数据。chrome
HttpClient 是 Apache Jakarta Common 下的子项目,能够用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,而且它支持 HTTP 协议最新的版本和建议。数据库
首先模拟手机浏览器的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 选择器
直接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里来得到全部粉丝数:
流程:
一套下来,就能拿到一个用户的全部粉丝了。