网易新闻app自媒体号信息爬取

##目的:对网易新闻app进行自媒体号进行数据采集


工具: fiddler4,IDEA


前置技能点:

  1. Java基础,基本语法,文件操作,Date类应用,maven的配置等
  2. fiddler抓包
  3. 生产者 消费者模型的Java实现
  4. Java httpclient包的基本运用
  5. JSONObject类的运用

##流程:
###1. 用fiddler对网易新闻app进行抓包研究(此处最为复杂,又难以归纳出通用的法则)
下面给出几个关键的url

简介获取网址: http://c.m.163.com/nc/subscribe/abstract/自媒体id.html
以T1436178714849这个自媒体id为例
以get方式对该url进行请求,获取到下列返回数据,经过观察,我们可以发现该串json数据中的desc键描述了该自媒体的简介信息

{
    "abstractList": [
        {
            "topicid": "051187K4",
            "ename": "T1437980175117",
            "img": "http://dingyue.nosdn.127.net/CPyl06T14=6lx2V=Gy8BzwbDVnng40gvC2mkcTZmgYTvn1488900231940.jpg",
            "hasIcon": true,
            "tname": "智观察",
            "subnum": "9164",
            "topic_icons": "http://dingyue.nosdn.127.net/CPyl06T14=6lx2V=Gy8BzwbDVnng40gvC2mkcTZmgYTvn1488900231940.jpg",
            "tid": "T1437980175117"
        },
        {
            "topicid": "0511838M",
            "ename": "T1426650607511",
            "img": "http://img1.ph.126.net/0JLWi3-AFGkp_IJ39FzhTQ==/6619378857584634163.jpg",
            "hasIcon": true,
            "tname": "芯智讯",
            "subnum": "1万",
            "topic_icons": "http://img1.ph.126.net/0JLWi3-AFGkp_IJ39FzhTQ==/6619378857584634163.jpg",
            "tid": "T1426650607511"
        },
        {
            "topicid": "0511831Q",
            "ename": "T1425885645380",
            "img": "http://img2.ph.126.net/NiwTiVUjwMyfEbGGNuiAyA==/6630195852978107453.jpg",
            "hasIcon": true,
            "tname": "华强北在线",
            "subnum": "1.5万",
            "topic_icons": "http://img2.ph.126.net/NiwTiVUjwMyfEbGGNuiAyA==/6630195852978107453.jpg",
            "tid": "T1425885645380"
        }
    ],
    "abstractType": "similar",
    "desc": "3D打印在线(www.3d2013.com)是由世界3D打印技术产业联盟与中国3D打印技术产业联盟联袂主办的3D打印行业首家在线交易平台,是3D打印行业权威的信息资讯门户。"
}

同理对名称,别名等信息获取网址: http://c.m.163.com/nc/subscribe/v2/topic/自媒体id.html
利用get方式进行请求,获取下列json数据,可以通过该数据获取到自媒体的名称(tname),粉丝数(subnum),别名(alias)等信息

{
    "tab_list": [
        {
            "tab_type": "all",
            "tab_name": "文章"
        },
        {
            "tab_type": "abstract",
            "tab_name": "简介"
        }
    ],
    "subscribe_info": {
        "template": "normal1",
        "ename": "T1436178714849",
        "passport": "dDmPEbeEDNJpDRGAlWMCEmWlYJxG7i/ExMsC/IX1t2E=",
        "hasCover": false,
        "topic_background": "http://img2.cache.netease.com/m/newsapp/reading/cover1/C1374477668016.jpg",
        "certificationUrl": "http://mp.163.com/mobile/auth.html#/index",
        "hasIcon": true,
        "alias": "www.3d2013.com",
        "tname": "3D打印在线",
        "topic_icons": "http://dingyue.nosdn.127.net/Tg1kLhUOqzABq3i6HE7gyPJkHSeLxyRjDjqq43hrEFZWS.jpg",
        "subnum": "8746",
        "cid": "C1374477668016"
    }
}

通过app的搜索功能来获取自媒体的id
url:http://c.m.163.com/nc/topicset/v5/searchByPage/android/" + 从搜索结果的第几项开始+ "-20.html
利用post方式进行请求获取(注意构建header和RequestBody)
这里写图片描述

获取结果
这里写图片描述

2.利用maven导入相关包

主要是JSONObject依赖,httpclient依赖

<dependencies>

        <!-- jsoup解析依赖包 -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.7.3</version>
        </dependency>

        <!-- JSONObject依赖-->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ezmorph</groupId>
            <artifactId>ezmorph</artifactId>
            <version>1.0.3</version>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>
            <classifier>jdk15</classifier>
        </dependency>

        <!-- httpclient依赖 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.3</version>
        </dependency>



    </dependencies>

3. 根据爬取流程确定架构

我们爬取一个自媒体信息的过程,首先是通过构建搜索url来获取一系列的自媒体的id,然后才通过其id与简介url和名称url爬取该自媒体的简介信息与名称信息
这很自然的构成了一个生产者-消费者模型
所以一只爬虫需要两类线程,一类生产者(负责爬取自媒体id),一类消费者(负责通过id补全自媒体其他信息)

实现代码

生产者

package thread;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import util.Filter;
import util.URLUtil;
import vo.PageVo;

import java.util.List;
import java.util.concurrent.BlockingQueue;


/**
 * 通过网易app搜索引擎获取自媒体名单
 */
public class ThirdThread implements Runnable
{
    BlockingQueue<PageVo> queue;
    Filter filter;
    //TODO 测试时设置上限
    static int MaxNum = 0;
    final String filePath = "D:/wangyiApp/";


    public ThirdThread(BlockingQueue<PageVo> queue)
    {
        this.queue = queue;
        filter = new Filter();
    }

    @Override
    public void run()
    {
        List<String> stringList = URLUtil.getGB2312List();
        //对搜索关键词进行遍历
        for (String s : stringList)
        {

            int num = 0;
            String data = null;
            //对单个关键词的结果进行遍历
            while (true)
            {
                String httpUrl = "http://c.m.163.com/nc/topicset/v5/searchByPage/android/" + num + "-20.html";
                num += 20;
                try
                {
                    data = URLUtil.doPost(httpUrl, "keyWord=" + URLUtil.getEncodedBase64(s));
                    JSONObject jsonObject = JSONObject.fromObject(data);
                    //获取其他公众号网址
                    JSONArray jsonArray = jsonObject.getJSONArray("result");

                    //当结果页结束时,换下一个搜索词
                    if (jsonArray.size() == 0)
                    {
                        break;
                    }
                    for (int i = 0; i < jsonArray.size(); i++)
                    {
                        System.out.println("线程" + Thread.currentThread().getName() + "容量" + queue.remainingCapacity());
                        //当队列剩余容量小于2时,丢弃该公众号网页
                        if (queue.remainingCapacity() < 2)
                        {

                        }
                        PageVo vo = new PageVo();

                        // 遍历 jsonarray 数组,把每一个对象转成 json 对象
                        JSONObject job = jsonArray.getJSONObject(i);
                        // 得到自媒体id
                        String ID = job.getString("tid");
                        vo.setId(ID);

                        /**
                         * 对网址去重
                         */
                        if (filter.Contain(ID))
                        {
                            System.out.println("爬取到一条重复记录");
                            continue;
                        }
                        vo.setUrl(ID);
                        queue.put(vo);

                        System.out.println(ID);
                    }
                } catch (Exception e)
                {
                    e.printStackTrace();
                }
            }

        }
    }


}


消费者

package thread;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import util.FileUtil;
import util.Filter;
import util.URLUtil;
import vo.PageVo;

import java.util.concurrent.BlockingQueue;




/**
 * 一号线程,爬取对应自媒体的简介,名称等信息
 *
 * 简介获取网址:  http://c.m.163.com/nc/subscribe/abstract/自媒体id.html
 * 名称,别名等信息获取网址:  http://c.m.163.com/nc/subscribe/v2/topic/自媒体id.html
 */
public class FirstThread implements Runnable
{
    BlockingQueue<PageVo> queue;
    Filter filter;
    //TODO 测试时设置上限
    static int MaxNum = 0;
    final String filePath="D:/wangyiApp/";



    public FirstThread(BlockingQueue<PageVo> queue)
    {
        this.queue = queue;
        filter = new Filter();
    }

    @Override
    public void run()
    {
        while (true)
        {
            //TODO 测试用
            synchronized (this)
            {
                MaxNum++;
            }


            try
            {
                PageVo pageVo = queue.take();
                //构建简介获取网址
                String  descUrl="http://c.m.163.com/nc/subscribe/abstract/"+pageVo.getId()+".html";
                JSONObject jsonObject = JSONObject.fromObject(URLUtil.doGet(descUrl));
                String desc = jsonObject.getString("desc");
                pageVo.setDescription(desc);

                //构建名称等信息获取网址
                String msgUrl="http://c.m.163.com/nc/subscribe/v2/topic/"+pageVo.getId()+".html";
                JSONObject jsonObject2 = JSONObject.fromObject(URLUtil.doGet(msgUrl));
                JSONObject msg=jsonObject2.getJSONObject("subscribe_info");
                pageVo.setAlias(msg.getString("alias"));
                pageVo.setAvatarsUrl(msg.getString("topic_icons"));
                pageVo.setName(msg.getString("tname"));
                //将subnum中的xx万转为数字
                String tmp=msg.getString("subnum");
                if (tmp.length()>1&&tmp.substring(tmp.length()-1).equals("万"))
                {
                    float tmp2= Float.parseFloat(tmp.substring(0,tmp.length()-1));
                    pageVo.setFanNum(String.valueOf((int) (tmp2*10000)));
                }
                pageVo.setUrl(msgUrl);
                //标注为网易
                pageVo.setSite("netease");



                //储存
                FileUtil.save(pageVo,filePath);

//                //获取其他公众号网址
//                JSONArray jsonArray = jsonObject.getJSONArray("abstractList");
//
//                for (int i = 0; i < jsonArray.size(); i++)
//                {
//                    System.out.println("线程"+Thread.currentThread().getName()+"容量"+queue.remainingCapacity());
//                    //当队列剩余容量小于2时,丢弃该公众号网页
//                    if (queue.remainingCapacity() < 2)
//                    {
//
//                    }
//                    PageVo vo = new PageVo();
//
//                    // 遍历 jsonarray 数组,把每一个对象转成 json 对象
//                    JSONObject job = jsonArray.getJSONObject(i);
//                    // 得到 每个对象中的属性值
//                    String ID = job.getString("ename");
//                    vo.setId(ID);
//
//
//                    /**
//                     * 对网址去重
//                     */
//                    if (filter.Contain(ID))
//                    {
//                        System.out.println("爬取到一条重复");
//                        continue;
//                    }
//                    vo.setUrl(ID);
//                    queue.put(vo);
//
//                    System.out.println();
//                }

            }catch (Exception e)
            {

            }
        }
    }
}

可能遇到的问题:

####NO1.fiddler抓不到包,真心玄学问题,试过各种解决方法包括利用wireshark从底层抓包,都没有解决,在这一步卡了快一天,最后发现重装了一下网易新闻app。。。。就好了,好了,了
####No2.用被爬取app的搜索引擎,然后就想到了利用常用的汉字作为搜索关键词,然而,去那去找这些常用的汉字表呢?此时很自然的就想到了gb2312字符集。所以此时需要遍历gb2312字符集

//对gb2312字符集遍历
        try
        {

            //遍历gb2312汉字编码分区
            for (int i=0xB0;i<0xF7;i++)
            {
                //遍历每个分区中的汉字
                for (int j=0xA1;j<0xFF;j++)
                {
                    byte[] bytes=new byte[2];
                    bytes[0]= (byte) i;
                    bytes[1]= (byte) j;
                    short tmp= (short) (i+j);
                    String s = new String(bytes, "gb2312");
                    System.out.println(s);
                }

            }
        } catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }

No3.网易app的搜索汉字使用base64编码的(常识),但一时想不到可能就会很迷

/**
     *  通过jdk8的util类中的Base64类实现 base64编码
     * @param plainText
     * @return
     */
    public static String getEncodedBase64(String plainText)
    {
        String encoded = null;
        try
        {
            byte[] bytes = plainText.getBytes("UTF-8");
            encoded = Base64.getEncoder().encodeToString(bytes);
        } catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
        return encoded;
    }

####No4. set的线程安全问题,因为爬虫肯定涉及到去重问题,在数据量不大时,常直接采用set去重,然而set并不是线程安全滴,在爬虫这种典型的多线程应用中,肯定是不行的,此时可以通过利用synchronizedSet使set线程安全(private static Set set= Collections.synchronizedSet(new HashSet());),当然,也可以自己通过synchronized实现


###思考:app爬虫和一般的网络爬虫的区别
app的各个网址(节点)分布就类似于计网中的星型拓扑结构,各个页面间一般没用能直接跳转到其他页面的url(或者是只有同一类的页面间能相互跳转),所以普通页面就相当于星型结构中的周围节点。而中心节点则是app的搜索url,只有通过app的搜索功能才能找到这些周围节点
这里写图片描述
而一般的网络爬虫,则是网状模型,一般来说,重任意节点都能很容易的遍历整个网络
这里写图片描述

发布了51 篇原创文章 · 获赞 26 · 访问量 10万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览