webmagic初次学习 爬取知乎数据

爬虫知多少,小公司刚起步的公司做成的软件需要大量的数据来维持,这就需要专业的爬虫工程师来搞点数据了,个人是干大数据的,会java和scala,当然也了解过pychon,说是爬虫都是拿pychon来搞,当然这是对强大的java的一种蔑视,java可以说是程序界上的元老的,啥都可以搞。今天就来介绍一个简单又实用的爬虫架构,webmagic!!!,爬虫个人觉得只要研究透网站的规则,什么语言都是可以来搞这个网站的数据,今天以爬取知乎网页内容为例 来搞他
简单介绍
webmagic整体个人觉得可以分为两层,一层是process,一层是pipline,process层用来真正获取html并解析,解析完成后可以立即返回得到结果,也可以传到pipline,pipline是一个整理数据的通道,接收process的数据整理并返回。启动类用来创建process,将url地址传到process来进程爬虫。整体使用maven来做项目管理。
下面这个网址是用字符串拼接的知乎搜索文章的一个后台连接地址,我们可以通过网址抓包得到这个地址,发现是个json,整体就简单多了。
https://www.zhihu.com/api/v4/search_v3?t=general&q=%E9%BB%91%E5%A4%B4&correction=1&offset=0&limit=20&lc_idx=0&show_all_topics=0第一步 pom添加webmagic依赖

<!-- webmagic依赖 -->
        <dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-core</artifactId>
            <version>${webmagic.version}</version>
        </dependency>
        <dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-extension</artifactId>
            <version>${webmagic.version}</version>
        </dependency>

第二步 编写process类

@Component("processorZh")
public class ZHProcess implements PageProcessor {
    private Site site = Site.me().setRetryTimes(3).setSleepTime(1000);

    @Override
    public void process(Page page) {
        //先获取文本
        page.putField("result", page.getHtml().xpath("//body/text()").toString());
        //内容
        String data = page.getHtml().xpath("//body/text()").get();
        //拿出这个页面的下一个网址
        JSONObject jsonObject = JSON.parseObject(data);
        JSONObject obj = jsonObject.getJSONObject("paging");
        if (obj.get("is_end").toString().equals("false")) {
            //获取下一页
            page.addTargetRequest(obj.get("next").toString());


        }
    }

    @Override
    public Site getSite() {

        return site;
    }

爬虫process类先继承PageProcessor实现两个方法,getsite()方法是爬虫的配置,设置休眠时长,每页间隔时长和编码,编码是自适应的。page可以获取html页面,然后page有很多方法,获取url,html等,html就可以解析,使用xpath,css和$等,还可以使用正则解析,得到get和all之后将数据传入pipline。
第三部 编写pipline类

/**
 * 知乎pipleline  整理数据并入库
 */
public class ZhiHuPipleline implements Pipeline {
    private PcContentMapper mapper;
    private PcContentDTO pcd;

    public ZhiHuPipleline(PcContentMapper mapper, PcContentDTO pcd) {
        this.mapper = mapper;
        this.pcd = pcd;
    }


    @Override
    public void process(ResultItems resultItems, Task task) {
        List<PcContent> pclist=new ArrayList<PcContent>();
        String result = resultItems.get("result");
        String title = "";
        String answer = "";
        String auther = "";
        Long comment = 0L;
        Long voteupCount = 0L;
        String type = "";
        Long createtime = 0L;
        Long updatetime = 0L;
        JSONObject jsonObject = JSON.parseObject(result);
        JSONArray arrays = jsonObject.getJSONArray("data");

        for (int i = 0; i < arrays.size(); i++) {
            JSONObject obj = (JSONObject) arrays.get(i);
            JSONObject objHigh = obj.getJSONObject("highlight");
            title = objHigh.getString("title");
            JSONObject objObject = obj.getJSONObject("object");
            //获取作者
            if (objObject.getJSONObject("author") != null) {
                auther = MyStringUtil.filterEmoji(objObject.getJSONObject("author").getString("name"));
            }
            //评分
            if (objObject.getString("voteup_count") != null) {
                voteupCount = Long.parseLong(objObject.getString("voteup_count"));
            }
            //内容
            //评论个数
            if (objObject.getString("comment_count") != null) {
                comment = Long.parseLong(objObject.getString("comment_count"));
            }
            if (objObject.getString("type") != null) {
                //类型(article:文章  answer:问答式)
                type = objObject.getString("type");
            }
            if (objObject.getLong("created_time") != null) {
                createtime = objObject.getLong("created_time");
            }
            if (objObject.getLong("updated_time") != null) {
                updatetime = objObject.getLong("updated_time");
            }
            PcContent pc = new PcContent();
            pc.setDetailUrl(returnUrl(objObject));
            pc.setCommentNum(comment);
            pc.setKeyword(pcd.getKeyword());
            pc.setTitle(MyStringUtil.filterEmoji(title));
            pc.setArticleType(type);
            pc.setServiceType(AllCommon.ZHIHU);
            pc.setPageview(0L);
            pc.setLikeCount(voteupCount);
            pc.setMatter(answer);
            pc.setInsertDatetime(new Date());
            pc.setUpdateDatetime(MyStringUtil.timeToDate(updatetime * 1000));
            pc.setCrawlType(AllCommon.TUWEN);

            //收藏数没有
            pc.setCollectNum(0);
            pc.setAuthor(auther);

            try {
                if (pc.getLikeCount() >= pcd.getLikeCount() &&//判断前段传来的条件
                        pc.getCommentNum() >= pcd.getCommentCount()
                        && MyStringUtil.isEffectiveDate(createtime, pcd.getStartTime(), pcd.getEndTime())

                ) {
                    //mapper.insert(Page2OOS.downloadpage(pc));
                    pc.setReleaseTime(MyStringUtil.timeToDate(createtime * 1000));
                    //mapper.insert(pc);
                    pclist.add(pc);
                }
            } catch (Exception e) {
                new PaChongException(ResponseEnum.SYSTEM_ERROR);
            }

        }

        try {//判断集合中数据是否异常
            if(pclist.size()!=0) {
                mapper.insertList(pclist);
            }
        }catch (Exception e){
            System.err.println("数据异常");
        }

    }
    /**
     * 知乎的拼接网址  规则   获取
     *
     * @param jsobj
     * @return
     */
    public static String returnUrl(JSONObject jsobj) {
        StringBuffer str = new StringBuffer("https://www.zhihu.com/question/");
        StringBuffer str1 = new StringBuffer("https://zhuanlan.zhihu.com/p/");
        JSONObject jsb = jsobj.getJSONObject("question");
        if (jsb != null) {
            String url = jsb.getString("url");
            return str.append(url.substring(32, url.length())).toString();
        } else {
            return str1.append(jsobj.getString("id")).toString();
        }


    }

}

pipline主要整理数据,我们也可以在process类整理,完全可以不需要pipline类。
第三部 编写启动方法

/**
     * 知乎爬虫运行方法
     */
    public static void zhTextRun(PcContentDTO pcd, PcContentMapper mapper) {

        String url = "https://www.zhihu.com/api/v4/search_v3?t=general&q=" + pcd.getKeyword() + "&correction=3&offset=20&limit=20&lc_idx=25&show_all_topics=0&search_hash_id=32f69fe3edb72c74ab16573708957c46&vertical_info=0%2C1%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C1";
        Spider spi = new Spider(new ZHProcess());
        Map<String,Object> map=new HashMap<>();
        map.put("service_type",pcd.getServiceType());
        map.put("keyword",pcd.getKeyword());
        map.put("crawl_type",pcd.getCrawlType());
        //先删除数据库中已存在的
        mapper.rmContext(map);

        spi.addPipeline(new ZhiHuPipleline(mapper, pcd)).addUrl(url).thread(10).run();
    }

spider new出process,将连接传入,然后增加pipline,设置线程并启动,成功