从零实现一个高性能网络爬虫(一)网络请求分析及代码实现

摘要

从零实现一个高性能网络爬虫系列教程第一篇,后续会有关于url去重、如何反爬虫、如何提高抓取效率、分布式爬虫系列文章。以我写的一个知乎爬虫为Demo讲解,github地址 (https://github.com/wycm/zhihu-crawler) ,有兴趣的朋友可以star下。网络请求的分析是写网络爬虫非常关键且重要的一个步骤。这篇文章以知乎网站为例,从网络请求分析到代码(java)实现。

目的

获取某个知乎用户的所有关注用户的个人资料

请求分析

  1. 就目前的大部分网页来说,网页上能看到的数据大多都是直接在网站后台生成好数据(有的网页是在网站前端通过js代码处理后显示,如数据混淆、加密等)直接在前台显示。
  2. 虽然很多网站采用了ajax异步加载,但是归根结底它还是一个http请求。只要能够分析出对应数据的请求来源,那么就很容易的拿到你想要的数据了。以下步骤讲解如何分析http请求。
  3. 以我的知乎账户为例,获取我的所有关注用户资料。首先打开我的关注列表,可以看到主面板就是我的关注用户列表,我一共关注233个用户,现在目的是就是要获取这233个用户的个人资料信息。打开F12->NetWork,勾选上Preserve log和Disable cache(如下图)。


  4. 下拉滚动条,点击下一页获取对应请求(在翻页的过程会有很多无关的请求),待页面加载完成后,在请求列表中右键->Save as HAR with content,这个文件是把当前请求(request)列表保存为json格式文本,保存后使用chrome打开这个文件,搜索(Ctrl+F)页面出现的关键字,要注意这里中文采用了unicode编码,我这里直接搜索5032(李博杰的关注者数,见下图)。这一步骤的目的是获取我们想要数据(关注用户的个人资料)的请求来源。


  5. 由步骤2搜索得出,关注用户的资料数据来自以下请求(如下图),url解码后为https://www.zhihu.com/api/v4/members/wo-yan-chen-mo/followees?include=data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics&offset=20&limit=20(url1),从这里可以看出关注列表的数据并不是从(url2)同步加载而来的,而是直接通过ajax异步请求url1来获得关注用户数据,然后通过js代码填充数据。这里要注意用红色矩形圈住的authorization request header,在代码实现的时候必须加上这个header。这个数据并不是动态改变的,通过步骤2的方式可以发现它是来自一个js文件。该步骤注意的是,我写该文章的时候是2017-04-27,随着时间推移,知乎可能会更新相关api接口的url,也就是说通过步骤2得出的url有可能并不是我上面的url1,但是具体分析的方法还是通用的。
  6. 多测试几次可以得出以上url1的参数含义如下
参数名 类型 必填 说明
include String data[*]answer_count,articles_count 需要返回的字段(这个值可以改根据需要增加一些字段
offset int 0 偏移量(通过调整这个值可以获取到一个用户的所有关注用户资料)
limit int 20 返回用户数(最大20,超过20无效)
  1. 关于如何测试请求,我常用的以下三种方式

    • 原生chrome浏览器。可以做一些简单的GET请求测试,这种方式有很大的局限性,不能编辑http header。如果直接(未登录知乎)通过浏览器访问url1,会得到401的response code。因为它没有带上authorization request header。所以这种方式能测试一些简单且没有特殊request header的GET请求。


    • chrome插件Postman。一个很强大的http请求测试工具,可以直接编辑request header(包括cookies)。如果可以FQ的话,强烈推荐。GET、POST、PUT等都是支持的,几乎可以发送任意类型的http请求,测试的url1如下图。通过修改它参数的值,来看服务器响应数据的变化来确定参数含义


    • intellij idea ultimate版自带的工具。打开方式 Tools->Test RESTful Web Service。也是可以直接编辑http header(包括cookies)请求发送,GET、POST、PUT等请求方式也都是支持的。
  2. response是一段json格式的数据,中文是采用的unicode编码,解码后数据内容如下图。


代码实现

  • 代码采用的Java HttpClient4.x,关于HttpClient4.x的使用我这里不过多讲解,要注意的是HttpClient4.x和3.x API有很大的差异。
package com.cnblogs.wycm;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

/**
 * 获取wo-yan-chen-mo关注的所有知乎用户信息
 * 只是把用户资料打印出来,没有具体解析(关于解析出详细数据可以采用正则表达式、json库、jsonpath等方式)
 */
public class Demo {
    public static void main(String[] args) throws IOException {
        //创建http客户端
        CloseableHttpClient httpClient = HttpClients.createDefault();

        String url = "https://www.zhihu.com/api/v4/members/wo-yan-chen-mo/followees?include=data%5B*%5D.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics&offset=0&limit=20";

        //创建http request(GET)
        HttpGet request = new HttpGet(url);

        //设置http request header
        request.setHeader("authorization", "oauth c3cef7c66a1843f8b3a9e6a1e3160e20");
        //执行http请求
        CloseableHttpResponse response = httpClient.execute(request);
        //打印response
        String responseStr = EntityUtils.toString(response.getEntity());
        System.out.println(responseStr);

        String nextPageUrl = getNextPageUrl(responseStr);
        boolean isEnd = getIsEnd(responseStr);

        while (!isEnd && nextPageUrl != null){
            //创建http request(GET)
            request = new HttpGet(nextPageUrl);

            //设置http request header
            request.setHeader("authorization", "oauth c3cef7c66a1843f8b3a9e6a1e3160e20");
            response = httpClient.execute(request);
            //打印response
            responseStr = EntityUtils.toString(response.getEntity());
            System.out.println(responseStr);
            nextPageUrl = getNextPageUrl(responseStr);
            isEnd = getIsEnd(responseStr);
        }
    }

    /**
     * 获取next url
     * @param responseStr
     * @return
     */
    private static String getNextPageUrl(String responseStr){
        JSONObject jsonObject = (JSONObject) JSON.parse(responseStr);
        jsonObject = (JSONObject) jsonObject.get("paging");
        return jsonObject.get("next").toString();
    }

    /**
     * 获取is_end
     * @param responseStr
     * @return
     */
    private static boolean getIsEnd(String responseStr){
        JSONObject jsonObject = (JSONObject) JSON.parse(responseStr);
        jsonObject = (JSONObject) jsonObject.get("paging");
        return (boolean) jsonObject.get("is_end");
    }
}
  • maven依赖
<dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.31</version>
    </dependency>

版权声明
作者:wycm
出处:https://www.jianshu.com/p/5077878fed65
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 1 前言 作为一名合格的数据分析师,其完整的技术知识体系必须贯穿数据获取、数据存储、数据提取、数据分析、数据挖掘、...
    whenif阅读 17,988评论 45 523
  • Google + Wikipedia + English = Almost Everything 这是笑来老师给的...
    超级赋能王张胜萍阅读 324评论 2 10
  • 红玫瑰 与 白玫瑰 我遇见一支红玫瑰 她歌唱 枝头颓败的十七的月亮 熠熠生辉 这样的静夜里 突然想起她 我想起那支...
    墨倾城阅读 278评论 0 1