gecco 的学习和练习

2021-05-07  本文已影响0人  客观开发者

1,学习gecco
源码地址:https://gitee.com/xtuhcy/gecco
2,运行源码。
导入到idea 里面。
运行第一个demo 在text 里面可以找到

@Gecco(pipelines="consolePipeline")
public class CommonCrawler implements HtmlBean {

    private static final long serialVersionUID = -8870768223740844229L;

    @Request
    private HttpRequest request;
    
    @Text(own=false)
    @HtmlField(cssPath="body")
    private String body;

    public HttpRequest getRequest() {
        return request;
    }

    public void setRequest(HttpRequest request) {
        this.request = request;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
    
    public static void main(String[] args) {
        GeccoEngine.create()
        .classpath("com.geccocrawler.gecco.demo")
        .start("https://www.baidu.com/")
        .interval(2000)
        .start();
    }
}

代码说明
接口HtmlBean说明该爬虫是一个解析html页面的爬虫(gecco还支持json格式的解析)
注解@Gecco告知该爬虫匹配的url格式(matchUrl)和内容抽取后的bean处理类(pipelines处理类采用管道过滤器模式,可以定义多个处理类)。
注解@RequestParameter可以注入url中的请求参数,如@RequestParameter("user")表示匹配url中的{user}
注解@HtmlField表示抽取html中的元素,cssPath采用类似jquery的css selector选取元素
注解@Text表示获取@HtmlField抽取出来的元素的text内容
注解@Html表示获取@HtmlField抽取出来的元素的html内容(如果不指定默认为@Html)
GeccoEngine表示爬虫引擎,通过create()初始化,通过start()/run()运行。可以配置一些启动参数如:扫描@Gecco注解的包名classpath;开始抓取的url地址star;抓取线程数thread;抓取完一个页面后的间隔时间interval(ms)等

2021-05-08_17-20-17.png
根据文档,可以进行自己demo 测试了。
也可以先看文档
reamde.MD其实够好了。
http://www.geccocrawler.com/author/gecco/
看着困了。还是写吧。
代码在 源码里面添加的和测试的
Snipaste_2021-05-08_17-25-59.png

主要是爬明星的基本信息。。是循环进行的。
主方法

package com.geccocrawler.gecco.demo.mingxing;

import com.geccocrawler.gecco.GeccoEngine;
import com.geccocrawler.gecco.annotation.*;
import com.geccocrawler.gecco.request.HttpGetRequest;
import com.geccocrawler.gecco.request.HttpRequest;
import com.geccocrawler.gecco.spider.HtmlBean;

import java.util.List;

//@Gecco(matchUrl = "http://ku.ent.sina.com.cn/star/search&page_no={page}", pipelines = {"consolePipeline", "starIndexPagePipeline"})
@Gecco(matchUrl = "http://ent.sina.com.cn/ku/star_search_index.d.html?page={page}", pipelines = {"consolePipeline", "starIndexPagePipeline"})
//matchUrl是爬取相匹配的url路径,然后将获取到的HtmlBean输出到相应的管道(pipelines)进行处理。这里的管道是可以自定义的。
public class StarIndexPage implements HtmlBean {

    private static final long serialVersionUID = 1225018257932399804L;

    @Request
    private HttpRequest request;

    //url中的page参数
    @RequestParameter
    private String page;


    //首页中的明星板块的集合,li的集合
    @HtmlField(cssPath = "#dataListInner > ul >li")
    private List<StarDetail> lsStarDetail;
//@HtmlField(cssPath = "#dataListInner > ul >li")是用来抓取网页中的相应网页数据,csspath是jQuery的形式。
//cssPath获取小技巧:用Chrome浏览器打开需要抓取的网页,按F12进入发者模式。然后在浏览器右侧选中该元素,鼠标右键选择Copy–Copy selector,即可获得该元素的cssPath

    //当前的页码,如果当前的是有很多页码的话,可以通过获取当前页码还有总页码,为继续抓取下一页做准备
//@Text是指抓取网页中的文本部分。@Html是指抓取Html代码。@Href是用来抓取元素的连接 @Ajax是指获取Ajax得到的内容。
    @Text
    @HtmlField(cssPath = "#dataListInner > div > ul > li.curr a")
    private int currPageNum;

    //相应的Getter和Setter方法...省略

    @Text
    @HtmlField(cssPath = "#dataListInner > div > ul > li")
    private String pageNum;

    public String getPageNum() {
        return pageNum;
    }

    public void setPageNum(String pageNum) {
        this.pageNum = pageNum;
    }

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public HttpRequest getRequest() {
        return request;
    }

    public void setRequest(HttpRequest request) {
        this.request = request;
    }

    public String getPage() {
        return page;
    }

    public void setPage(String page) {
        this.page = page;
    }

    public List<StarDetail> getLsStarDetail() {
        return lsStarDetail;
    }

    public void setLsStarDetail(List<StarDetail> lsStarDetail) {
        this.lsStarDetail = lsStarDetail;
    }

    public int getCurrPageNum() {
        return currPageNum;
    }

    public void setCurrPageNum(int currPageNum) {
        this.currPageNum = currPageNum;
    }
    /*
    * https://segmentfault.com/a/1190000010086659
    * 案例。。可以用
    * */
    public static void main(String[] args) {
//        String url = "http://ku.ent.sina.com.cn/star/search&page_no=1"; //想要爬取的网站的首页地址
        String url = "http://ent.sina.com.cn/ku/star_search_index.d.html?page=1"; //想要爬取的网站的首页地址
        HttpGetRequest start = new HttpGetRequest(url); //获取网站请求
        start.setCharset("UTF-8");
        GeccoEngine.create() //创建搜索引擎
                .classpath("com.geccocrawler.gecco.demo.mingxing") //要搜索的包名,会自动搜索该包下,含@Gecco注解的文件。
                .start(start)
                .thread(5)//开启多少个线程抓取
                .interval(2000) //隔多长时间抓取1次
                .run();
    }

}

@Gecco 来 找url 和 爬虫的参数
下面的放就是上面指定的方法
也是 数据存储的地方

package com.geccocrawler.gecco.demo.mingxing;

import com.geccocrawler.gecco.annotation.PipelineName;
import com.geccocrawler.gecco.pipeline.Pipeline;
import com.geccocrawler.gecco.request.HttpRequest;
import com.geccocrawler.gecco.scheduler.SchedulerContext;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;

@PipelineName("starIndexPagePipeline")
//@pipelineName 标签指定了pipline的名字。并且pipeline这个类需要实现Pipleline<T>。
public class StarIndexPagePipeline implements Pipeline<StarIndexPage> {

    @Override
    public void process(StarIndexPage starIndexPage) {

        List<StarDetail> lsStarDetail = starIndexPage.getLsStarDetail();

        StringBuilder inputText = new StringBuilder();

        for (StarDetail starDetail : lsStarDetail) {
            String professionHtml = starDetail.getProfessionHtml();
            String starNameHtml = starDetail.getStarNameHtml();
            Document docName = Jsoup.parse(starNameHtml);
            String starName = docName.getElementsByTag("a").attr("title").trim();

            String starSex = starDetail.getStarSex().trim();
            Document doc = Jsoup.parse(professionHtml);
            String profession = "未知"; //有不含a标签的,不含a标签的都是未知的
            if (professionHtml.indexOf("<a") != -1) {
                profession = doc.getElementsByTag("a").text();
            }
            String nationality = starDetail.getNationality().trim();
            String birthday = starDetail.getBirthday().trim();
            String constellation = starDetail.getConstellation().trim();
            String height = starDetail.getHeight().trim();
            inputText.append(starName + "\t" +
                    starSex + "\t" +
                    profession + "\t" +
                    nationality + "\t" +
                    birthday + "\t" +
                    constellation + "\t" +
                    height + "\t" +
                    System.getProperty("line.separator"));
        }
        //写入文件
        writeFile(inputText.toString());

        //爬取下一页
        HttpRequest currRequest = starIndexPage.getRequest();

//        int currPageNum = starIndexPage.getCurrPageNum();
//        String pageNum = starIndexPage.getPageNum();

        Map<String, String> parameters = currRequest.getParameters();
        String page = parameters.get("page");
        int i = Integer.parseInt(page);
        /*
        System.out.println("----------已爬取第" + currPageNum + "页----------");
        pageNum = pageNum.replace("第", "");
        pageNum = pageNum.replace("页", "");
        int i1 = Integer.parseInt(pageNum);
        System.out.println("----------已爬取第" + pageNum + "页----------");
        System.out.println("----------已爬取第" + i1 + "页----------");*/
        System.out.println("----------已爬取第" + i + "页----------");

        searchNext(i, currRequest);
    }

    //写入文档的方法
    public void writeFile(String inputText) {
        try {

            File f1 = new File("D:\\明星数据.txt");
            if (!f1.exists()) {
                f1.createNewFile();
            }
            FileWriter fw1 = new FileWriter("D:\\明星数据.txt", true);
            PrintWriter pw = new PrintWriter(fw1, true);
            pw.println("姓名" + "\t" + "性别" + "\t" + "职业" + "\t" + "国籍" + "\t" + "生日" + "\t" + "星座" + "\t" + "身高");
            pw.print(inputText);
            pw.flush();
            pw.close();
            /*if (new File("D:\\明星数据.txt").exists()) {
                FileWriter fw1 = new FileWriter("D:\\明星数据.txt", true);
                PrintWriter pw = new PrintWriter(fw1);
                pw.print(inputText);
                pw.flush();
                pw.close();

            } else {

            }*/
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void searchNext(int currPageNum, HttpRequest currRequest) {
        //总页数只有2178
        if (currPageNum < 2179) {
//            System.out.println("----------第" + currPageNum + "页----------");
            int nextPageNum = currPageNum + 1;
//            System.out.println("----------第" + nextPageNum + "页----------");
            String currUrl = currRequest.getUrl();
            String nextUrl = StringUtils.replaceOnce(currUrl, "page=" + currPageNum, "page=" + nextPageNum);
            SchedulerContext.into(currRequest.subRequest(nextUrl));
        } else {
            System.out.println("---------------爬取完毕------------------");
        }

    }
}

主要是第几页没有能对。以及页面获取属性。@Text @HtmlField的理解使用
这就就搞定了。总页数是根据网站里面二判断的。
还有明星的实体

package com.geccocrawler.gecco.demo.mingxing;

import com.geccocrawler.gecco.annotation.Html;
import com.geccocrawler.gecco.annotation.HtmlField;
import com.geccocrawler.gecco.annotation.Text;
import com.geccocrawler.gecco.spider.HtmlBean;

public class StarDetail implements HtmlBean {

    /*//明星的照片
    @Image("src")
    @HtmlField(cssPath = "a > img")
    prie String PhotoString;*/

    //明星的名字
    @Html
    @HtmlField(cssPath ="div > div > h4")
    private String  starNameHtml;

    //明星的性别
    @Text
    @HtmlField(cssPath = "div > p:nth-child(2)")
    private  String starSex;

    //明星的职业
    @Html
    @HtmlField(cssPath = "div > p:nth-child(3)")
    private String professionHtml;

    //明星的国籍
    @Text
    @HtmlField(cssPath = " div > p:nth-child(4)")
    private String  nationality;

    //明星的出生日期
    @Text
    @HtmlField(cssPath = "div > p.special")
    private String birthday;

    //明星的星座
    @Text
    @HtmlField(cssPath = "div > p:nth-child(6)>a")
    private String constellation;

    //明星的身高
    @Text
    @HtmlField(cssPath = "div > p:nth-child(7)")
    private String height;

    public String getStarNameHtml() {
        return starNameHtml;
    }

    public void setStarNameHtml(String starNameHtml) {
        this.starNameHtml = starNameHtml;
    }

    public String getStarSex() {
        return starSex;
    }

    public void setStarSex(String starSex) {
        this.starSex = starSex;
    }

    public String getProfessionHtml() {
        return professionHtml;
    }

    public void setProfessionHtml(String professionHtml) {
        this.professionHtml = professionHtml;
    }

    public String getNationality() {
        return nationality;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getConstellation() {
        return constellation;
    }

    public void setConstellation(String constellation) {
        this.constellation = constellation;
    }

    public String getHeight() {
        return height;
    }

    public void setHeight(String height) {
        this.height = height;
    }
}

最后就保存在d盘明星数据.txt中,就循环进行的。

上一篇下一篇

猜你喜欢

热点阅读