py-02-爬虫比价器


目录:

day01:爬取新闻网站

day02:爬取京东商城

day03:爬取商品价格+报表eCharts

day04:用HttpClient+Jsoup的三种方式爬取网页内容

day05:抓取京东商品一系列信息(标题,卖点,价格,图片,描述)

day06:京东、淘宝、苏宁、比价系统框架的搭建

day07:比价系统具体完善


第一天:爬取新闻网站

比价器系统

比价系统功能

  • 利用Jsoup爬取每个页面的商品信息

  • ECharts柱状图、曲线图

  1. 工作原理

利用每个电商网站的搜索条查询同样的条件,例如:iphoneX,然后发现其规律

https://search.gome.com.cn/search?question=iphonex%2064g&searchType=goods

https://search.suning.com/iphonex%2064g/

https://list.tmall.com/search_product.htm?q=iphonex+64g

通过url传递了查询条件。

抓取步骤:

  1. 用户输入查询条件

  2. 分别到每个电商网站访问,利用其查询出它们网站的对应商品

  3. 获取其列表页面中第一个商品的链接

  4. 利用jsoup爬取每个页面的商品信息

  5. 把商品信息入库并设置爬取时间

  6. 使用ECharts进行进行价格的比较

  7. 拓展:ECharts实现抓取价格柱状图、饼形图、曲线图

2.涉及的技术点

1.JavaScript

JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。

在1995年时,由Netscape公司的Brendan Eich,在网景导航者浏览器上首次设计实现而成。因为Netscape与Sun合作,Netscape管理层希望它外观看起来像Java,因此取名为JavaScript。

2.json

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

3.HttpClient

HttpClient是Apache Jakarta Common下的子项目,可以用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。

注意:不要随意升级版本,每次大版本内容api变化比较大。

4.Jsoup

jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

爬虫新闻

5.Httpclient

package test;
import java.io.IOException;
import org.apache.http.HttpEntity;
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.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
public class TestPrice {
    @Test
    public void taobaoItemPrice() throws IOException{
       String itemId = "560563554738";
       String url = "http://mdskip.taobao.com/core/initItemDetail.htm?isRegionLevel=true&itemTags=385,775,843,1035,1163,1227,1478,1483,1539,1611,1863,1867,1923,2049,2059,2242,2251,2315,2507,2635,3595,3974,4166,4299,4555,4811,5259,5323,5515,6145,6785,7809,9153,11265,12353,12609,13697,13953,16321,16513,17473,17537,17665,17857,18945,19841,20289,21762,21826,25922,28802,53954&tgTag=false&addressLevel=4&isAreaSell=false&sellerPreview=false&offlineShop=false&showShopProm=false&isIFC=false&service3C=true&isSecKill=false&isForbidBuyItem=false&cartEnable=true&sellerUserTag=839979040&queryMemberRight=true&itemId="+itemId+"&sellerUserTag2=306250462070310924&household=false&isApparel=false¬AllowOriginPrice=false&tmallBuySupport=true&sellerUserTag3=144467169269284992&sellerUserTag4=1152930305168967075&progressiveSupport=true&isUseInventoryCenter=false&tryBeforeBuy=false&callback=setMdskip×tamp=1420351892310";
       HttpClientBuilder builder = HttpClients.custom(); 
        builder.setUserAgent("Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4)"); 
        CloseableHttpClient httpClient = builder.build(); 
        final HttpGet httpGet = new HttpGet(url); 
        httpGet.addHeader("Referer", "http://detail.tmall.com/item.htm?id="+itemId+"&skuId=68347779144&areaId=110000&cat_id=50024400&rn=763d147479ecdc17c2632a4219ce96b3&standard=1&user_id=263726286&is_b=1"); 
        CloseableHttpResponse response = null; 
        response = httpClient.execute(httpGet); 
        final HttpEntity entity = response.getEntity(); 
        String result = null; 
        if (entity != null) { 
            result = EntityUtils.toString(entity); 
            EntityUtils.consume(entity); 
        } 
         
        //商品价格的返回值,需要解析出来价格 
         
        result = result.substring(10, result.length()-1); 
    }
}

6.Jsoup

package test;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.Test;
public class TestNew {
    @Test
    public void site() throws IOException{
       String url = "http://ent.qq.com/a/20171117/007399.htm";
       String html = Jsoup.connect(url).execute().body();
       
       System.out.println(html);
    }
    
    @Test
    public void title() throws IOException{
       String url = "http://ent.qq.com/a/20171117/007399.htm";
       Document doc = Jsoup.connect(url).get();
       Elements els = doc.select(".hd h1");
       Element ele = els.get(0);
       String title = ele.text();
       
       System.out.println(title);
    }
    
    @Test
    public void img() throws IOException{
       String url = "http://ent.qq.com/a/20171117/007399.htm";
       String imageUrl = Jsoup.connect(url).get()
           .select(".Cnt-Main-Article-QQ p img")
           .get(0)
           .attr("src");
       
       System.out.println(imageUrl);
    }
}

7.抓取价格 json

2017年4月,京东开始对价格进行反爬虫控制,访问过多的IP地址会被禁止。      

    @Test    //价格
    public void getItemPrice() throws IOException{
       String url = "http://p.3.cn/prices/mgets?skuIds=J_3882469";
       String json = Jsoup.connect(url).ignoreContentType(true).execute().body();
       JsonNode jsonNode = MAPPER.readTree(json);
       
              //解析完数组,获取数组第一条数据,获取它的p元素值
       Double price = jsonNode.get(0).get("p").asDouble();
       log.debug(price);
    }

8.抓取描述 - jsonp

  

    @Test    //商品描述
    public void getItemDesc() throws IOException{
       String url = "http://d.3.cn/desc/3882469";
       String jsonp = Jsoup.connect(url).ignoreContentType(true).execute().body();
              String json = jsonp.substring(9, jsonp.length()-1);      //把函数名去掉
       JsonNode jsonNode = MAPPER.readTree(json);
       
       String desc = jsonNode.get("content").asText();
       log.debug(desc);
    }

第二天:爬取京东商城

抓取的五种方式

9.抓取页面

    @Test     //抓整个页面
    public void html() throws IOException{
       String url = "http://tech.qq.com/a/20170330/003855.htm";
       //doc代表一个页面
       String html = Jsoup.connect(url).execute().body();
       System.out.println(html);
    }

10.抓取整个网站

     

    @Test   //抓整站,找到所有a链接,然后进行广度优先/深度优先进行遍历
    public void getAllATag() throws IOException{
       String url = "http://tech.qq.com/a/20170330/003855.htm";
              //获取到页面
       Document doc = Jsoup.connect(url).get();
              //获取到页面中的所有a标签
       Elements eles = doc.getElementsByTag("a");
       for(Element ele : eles){
                     String title = ele.text();          //获取a标签的内容
                     String aurl = ele.attr("href");       //获取a标签的属性
           log.debug(title+" - "+aurl);
       }
    }

11.抓取标题 - 页面上的内容

可以多级父子样式嵌套

    @Test    //京东商城,商品标题
    public void getItemTile() throws IOException{
       String url = "https://item.jd.com/3882469.html";
       Document doc = Jsoup.connect(url).get();
       Element ele = doc.select(".itemInfo-wrap .sku-name").get(0);
       String title = ele.text();
       log.debug(title);
    }
       @Test    //当当商城,商品标题
    public void getDDItemTile() throws IOException{
       String url = "http://product.dangdang.com/1052875306.html";
       Document doc = Jsoup.connect(url).get();
       Element ele = doc.select("article").get(0);
       String title = ele.text();
       log.debug(title);
    }

12.抓取价格 json

2017年4月,京东开始对价格进行反爬虫控制,访问过多的IP地址会被禁止。

     

    @Test    //价格
    public void getItemPrice() throws IOException{
       String url = "http://p.3.cn/prices/mgets?skuIds=J_3882469";
       String json = Jsoup.connect(url).ignoreContentType(true).execute().body();
       JsonNode jsonNode = MAPPER.readTree(json);
       
              //解析完数组,获取数组第一条数据,获取它的p元素值
       Double price = jsonNode.get(0).get("p").asDouble();
       log.debug(price);
    }

13.抓取描述 - jsonp

    @Test    //商品描述
    public void getItemDesc() throws IOException{
       String url = "http://d.3.cn/desc/3882469";
       String jsonp = Jsoup.connect(url).ignoreContentType(true).execute().body();
              String json = jsonp.substring(9, jsonp.length()-1);      //把函数名去掉
       JsonNode jsonNode = MAPPER.readTree(json);
       
       String desc = jsonNode.get("content").asText();
       log.debug(desc);
    }

5.爬取京东

抓取商品先要找到商品ID,有两个方案:

方案一:商品ID是一串数字,猜测它是自增的,于是我们可以是做一个自增的循环。但如果商品的ID不是连续,会造成很多访问无法继续访问,报链接超时。

方案二:找到网站的所有商品的列表页面,解析html找到商品的ID,这个方式解析麻烦些,但商品ID直接可以获得。

所有一般来说都是采用第二种方案。

分类、商品列表、商品详情

那抓取京东网站就变成抓取所有分类,按分类找到商品列表页面,从商品列表页面抓取出商品ID,最终循环商品ID,抓取所有商品详情页面,解析商品详情页面,找到所有商品的详细信息。

断点抓取、离线分析

京东有近22个大类143个二级分类,1286三级分类,8615683种商品,近九百万种商品。如果持续在线抓取,会很快比屏蔽。也不方便测试。所以我们采取断点抓取,离线分析。先将分类抓取,将榨取后的信息保存到磁盘中,后期对磁盘中的文件进行分析入库。

14.抓取商品分类

    

    @Test //抓取商品分类(22,143,1286),http://www.jd.com/allSort.aspx
    public void itemCat() throws IOException{
       String url = "http://www.jd.com/allSort.aspx";
       Document doc = Jsoup.connect(url).get();
       
       Elements level1 = doc.select("h2 span");
              log.info("大类总数:"+level1.size());
       for(Element ele : level1){
           log.info(ele.text());
       }
       
       Elements level2 = doc.select("dl dt a");
              log.info("二级分类总数:"+level2.size());
       for(Element ele : level2){
           log.info(ele.text());
       }
       
       Elements level3 = doc.select("dl dd a");
              log.info("三级分类总数:"+level3.size());
       for(Element ele : level3){
           log.info(ele.text()+" "+ele.attr("href"));
       }
    }
    
       @Test    //抓取某个分类下的商品数
    public void itemCatCountOne() throws IOException{
       String url = "http://list.jd.com/list.html?cat=9987,653,655";
       Document doc = Jsoup.connect(url).get();
       Elements ele = doc.select(".st-ext span");
       log.info(ele.text());
    }
    
       @Test //抓取商品分类下商品的数量,去除特殊链接
    public void itemCatCount() throws IOException{
       Integer total = 0;
       String url = "http://www.jd.com/allSort.aspx";
       Document doc = Jsoup.connect(url).get();
       Elements level3 = doc.select("dl dd a");
              log.info("三级分类总数:"+level3.size());
       for(Element ele : level3){
           log.info(ele.text()+" "+ele.attr("href"));
           String urlList = "http:"+ele.attr("href");
           
                     if(urlList.indexOf("?cat=")>0){       //有多种链接,只有含有cat才是商品列表页面
              Document listDoc = Jsoup.connect(urlList).get();
              Elements eleCount = listDoc.select(".st-ext span");
              Integer catCount = 0;
              try{
                  catCount = Integer.valueOf(eleCount.text());
              }catch(Exception e){
                  catCount = 0;
              }
              total += catCount;
              
              Elements elePages = listDoc.select("#J_topPage span.fp-text i");
              Integer pages = 0;
              try{
                  pages = Integer.valueOf(elePages.text());
              }catch(Exception e){
                  pages = 0;
              }
              
              
                            log.info(ele.text()+" 商品数:"+catCount+" 页数:"+pages);
           }
       }
              log.info("总商品数量:"+total);
    }

15.商品列表页面抓取商品编号

     

    @Test    //获取商品详细信息
    public void getItemInfo() throws IOException{
       String itemId = "1411013";
       String url = "http://item.jd.com/"+itemId+".html";
       Document doc = Jsoup.connect(url).get();
       
       String title = doc.select(".sku-name").get(0).text();
              log.info("标题:"+title);
       
       Elements eleImages = doc.select("div#spec-list li img");
       String[] images = new String[eleImages.size()];
       for(int i=0;i<eleImages.size();i++){
           images[i] = eleImages.get(i).attr("src");
                     log.info("链接图片["+i+"]:" + images[i]);
       }
    }

16.抓取商品价格

京东的价格是单独发起ajax请求,返回json数组,一次可以查询多个价格

http://p.3.cn/prices/mgets?skuIds=J_1411013,J_1411014

返回结果为json数组:

[{"id":"J_1411013","p":"3888.00","m":"6699.00"},{"id":"J_1411014","p":"569.00","m":"1398.00"}]

17.抓取商品卖点

京东的卖点是单独发起ajax请求,返回json格式数据,回显到页面

http://ad.3.cn/ads/mgets?skuids=AD_1411013,AD_1411014

返回结果为json数组:

[
{"ad":"\u53CC\u66F2\u9762\u4FA7\u5C4F\uFF0C\u91D1\u5C5E\u4E0E\u73BB\u7483\u5DE7\u5999\u878D\u5408\uFF0C\u81EA\u52A8\u8FFD\u7126\uFF0C\u667A\u80FD\u9065\u63A7\u5668\u652F\u6301\u7EA2\u5916\u9065\u63A7\u529F\u80FD\uFF01\u003C\u0061\u0020\u0020\u0074\u0061\u0072\u0067\u0065\u0074\u003D\u0022\u005F\u0062\u006C\u0061\u006E\u006B\u0022\u0020\u0020\u0068\u0072\u0065\u0066\u003D\u0022\u0068\u0074\u0074\u0070\u003A\u002F\u002F\u0073\u0061\u006C\u0065\u002E\u006A\u0064\u002E\u0063\u006F\u006D\u002F\u0061\u0063\u0074\u002F\u006F\u004C\u005A\u0052\u006C\u0057\u007A\u004D\u0070\u0049\u002E\u0068\u0074\u006D\u006C\u0022\u003E\u9886\u5238\u51CF\u94B1\uFF0C\u4E0B\u5355\u8FD4\u73B0\u003C\u002F\u0061\u003E","id":"AD_1411013"},

{"ad":"\u62C9\u6746\u7BB1\u3001\u4E66\u5305\u3001\u53CC\u80A9\u5305\u7B49\u591A\u79CD\u5546\u54C1\u9886\u5238\u6EE1\u0039\u0039\u51CF\u0032\u0030\u3001\u6EE1\u0033\u0039\u0039\u51CF\u0038\u0030\u003C\u0061\u0020\u0068\u0072\u0065\u0066\u003D\u0027\u0068\u0074\u0074\u0070\u003A\u002F\u002F\u0073\u0061\u006C\u0065\u002E\u006A\u0064\u002E\u0063\u006F\u006D\u002F\u0061\u0063\u0074\u002F\u006F\u0057\u0073\u007A\u0055\u0033\u006E\u0032\u0059\u0070\u006D\u0074\u0044\u002E\u0068\u0074\u006D\u006C\u0027\u0020\u0074\u0061\u0072\u0067\u0065\u0074\u003D\u0027\u005F\u0062\u006C\u0061\u006E\u006B\u0027\u003E\u5F00\u5B66\u4E0D\u5C06\u201C\u65E7\u201D\uFF0C\u9886\u5238\u4EAB\u6EE1\u51CF\uFF01\uFF01\u901F\u901F\u62A2\u8D2D\u5427\u007E\u003C\u002F\u0061\u003E","id":"AD_1411014"}
]

18.抓取商品描述

京东的商品描述是单独发起ajax请求,返回jsonp格式数据,回显到页面

http://d.3.cn/desc/1411013

showdesc({"date":1469167322294,"content":"<div style=\"text-align: center;\"><img data-lazyload=\"//img30.360buyimg.com/jgsq-productsoa/jfs/t2767/259/3526014654/1592287/78020f87/5791889cN81b6b7f9.jpg\" alt=\"\" /><br /></div><div style=\"text-align: center;\"><img data-lazyload=\"//img30.360buyimg.com/jgsq-productsoa/jfs/t2386/186/1517820110/681125/d98ab4ac/56b1b1a3N229b52dc.jpg\" style=\"widows: auto;\" alt=\"\" /><br /></div><br/>"})

第三天:爬取商品价格+报表eCharts

eCharts

19.饼形图

pie.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <!-- 引入 echarts.js -->
    <script type="text/javascript" src="js/echarts-all.js"></script>
</head>
<body>
    <!-- 为ECharts准备一个具备大小(宽高)的dom -->
    <div id="main" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
        var json = [ 
                            {value:30,name:'高圆圆'}, 
                            {value:26,name:'赵丽颖'}, 
                            {value:24,name:'关晓彤'}
           ];
       
        // 指定图表的配置项和数据
        var option = {
            title: {
                text: 'ECharts 入门示例'
            },
            tooltip: {},
            legend: {
                data:['高圆圆','赵丽颖','关晓彤']
            },
            series: [{
                name: '女神',
                type: 'pie',
                radius: '55%', //饼形图半径大小
                data:json
            }]
        };
        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    </script>
</body>
</html>

20.柱状图

bar.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <!-- 引入 echarts.js -->
    <script type="text/javascript" src="js/echarts-all.js"></script>
</head>
<body>
    <!-- 为ECharts准备一个具备大小(宽高)的dom -->
    <div id="main" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
        // 指定图表的配置项和数据
        var option = {
            title: {
                text: 'ECharts 入门示例'
            },
            tooltip: {},
            legend: {
                data:['销量']
            },
            xAxis: {
                data: ["衬衫","羊毛衫","雪纺衫"]
            },
            yAxis: {},
            series : [
                {
                                   name:'数量',
                  type:'bar',
                  barWidth: 50,
                  data:[200, 152, 100],
                  itemStyle: {
                     normal: {
                         color: new echarts.graphic.LinearGradient(
                            0, 0, 0, 1,[{offset: 0, color: '#2FDE80'},{offset: 1, color: '#2FDECA'}                           ]
                         )
                  },
                  emphasis: {
                     color: new echarts.graphic.LinearGradient(
                         0, 0, 0, 1,[{offset: 0, color: '#2FDECA'},{offset: 1, color: '#2FDE80'}]
                     )
                  }
              }
           }]
        };
        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    </script>
</body>
</html>

21.曲线图

line.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <!-- 引入 echarts.js -->
    <script type="text/javascript" src="js/echarts-all.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的dom -->
<div id="main" style="height:400px;width: 600px"></div>
<!-- ECharts单文件引入 -->
<script src="http://echarts.baidu.com/build/dist/echarts-all.js"></script>
<script type="text/javascript">
    // 基于准备好的dom,初始化echarts图表
    var myChart = echarts.init(document.getElementById("main"));
    var option = {
        title : {
            text: '未来一周气温变化',
            subtext: '纯属虚构'
        },
        tooltip : {
            trigger: 'axis'
        },
        legend: {
            data:['最高气温','最低气温']
        },
        //右上角工具条
        toolbox: {
            show : true,
            feature : {
                saveAsImage : {show: true}
            }
        },
        calculable : true,
        xAxis : [
            {
                type : 'category',
                boundaryGap : false,
                data : ['周一','周二','周三','周四','周五','周六','周日']
            }
        ],
        yAxis : [
            {
                type : 'value',
                axisLabel : {
                    formatter: '{value} °C'
                }
            }
        ],
        series : [
            {
                name:'最高气温',
                type:'line',
                data:[11, 11, 15, 13, 12, 13, 10],
                markPoint : {
                    data : [
                        {type : 'max', name: '最大值'},
                        {type : 'min', name: '最小值'}
                    ]
                },
                markLine : {
                    data : [
                        {type : 'average', name: '平均值'}
                    ]
                }
            },
            {
                name:'最低气温',
                type:'line',
                data:[1, -2, 2, 5, 3, 2, 0],
                markPoint : {
                    data : [
                        {type : 'min', name: '周最低'}
                    ]
                },
                markLine : {
                    data : [
                        {type : 'average', name : '平均值'}
                    ]
                }
            }
        ]
    };
    // 为echarts对象加载数据
    myChart.setOption(option);
</script>
</body>
</html>

7.比价器系统

23.京东

京东的价格是单独发起ajax请求,返回json数组,一次可以查询多个价格

http://p.3.cn/prices/mgets?skuIds=J_1411013,J_1411014

返回结果为json数组:

[{"id":"J_1411013","p":"3888.00","m":"6699.00"},{"id":"J_1411014","p":"569.00","m":"1398.00"}]

23.淘宝

   

    @Test
    public void taobaoItemPrice() throws IOException{
       String itemId = "560563554738";
       String url = "http://mdskip.taobao.com/core/initItemDetail.htm?isRegionLevel=true&itemTags=385,775,843,1035,1163,1227,1478,1483,1539,1611,1863,1867,1923,2049,2059,2242,2251,2315,2507,2635,3595,3974,4166,4299,4555,4811,5259,5323,5515,6145,6785,7809,9153,11265,12353,12609,13697,13953,16321,16513,17473,17537,17665,17857,18945,19841,20289,21762,21826,25922,28802,53954&tgTag=false&addressLevel=4&isAreaSell=false&sellerPreview=false&offlineShop=false&showShopProm=false&isIFC=false&service3C=true&isSecKill=false&isForbidBuyItem=false&cartEnable=true&sellerUserTag=839979040&queryMemberRight=true&itemId="+itemId+"&sellerUserTag2=306250462070310924&household=false&isApparel=false¬AllowOriginPrice=false&tmallBuySupport=true&sellerUserTag3=144467169269284992&sellerUserTag4=1152930305168967075&progressiveSupport=true&isUseInventoryCenter=false&tryBeforeBuy=false&callback=setMdskip×tamp=1420351892310";
       HttpClientBuilder builder = HttpClients.custom(); 
        builder.setUserAgent("Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4)"); 
        CloseableHttpClient httpClient = builder.build(); 
        final HttpGet httpGet = new HttpGet(url); 
        httpGet.addHeader("Referer", "http://detail.tmall.com/item.htm?id="+itemId+"&skuId=68347779144&areaId=110000&cat_id=50024400&rn=763d147479ecdc17c2632a4219ce96b3&standard=1&user_id=263726286&is_b=1"); 
        CloseableHttpResponse response = null; 
        response = httpClient.execute(httpGet); 
        final HttpEntity entity = response.getEntity(); 
        String result = null; 
        if (entity != null) { 
            result = EntityUtils.toString(entity); 
            EntityUtils.consume(entity); 
        } 
         
        //商品价格的返回值,需要解析出来价格 
         
        result = result.substring(10, result.length()-1); 
    }

24.苏宁

http://ds.suning.cn/ds/generalForTile/000000000690128134-9173-2-0000000000-1--ds000000000.jsonp

25.价格比较柱状图

price.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <!-- 引入 echarts.js -->
    <script type="text/javascript" src="js/echarts-all.js"></script>
</head>
<body>
    <!-- 为ECharts准备一个具备大小(宽高)的dom -->
    <div id="main" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
        // 指定图表的配置项和数据
        var option = {
            title: {
                text: '各大电商商品价格比价'
            },
            tooltip: {},
            legend: {
                data:['iphoneX 64g']
            },
            xAxis: {
                data: ["京东","淘宝","苏宁"]
            },
            yAxis: {},
            series : [
                {
                                   name:'数量',
                  type:'bar',
                  barWidth: 50,
                  data:[8316, 6783, 7788],
                  itemStyle: {
                     normal: {
                         color: new echarts.graphic.LinearGradient(
                            0, 0, 0, 1,[{offset: 0, color: '#2FDE80'},{offset: 1, color: '#2FDECA'}                           ]
                         )
                  },
                  emphasis: {
                     color: new echarts.graphic.LinearGradient(
                         0, 0, 0, 1,[{offset: 0, color: '#2FDECA'},{offset: 1, color: '#2FDE80'}]
                     )
                  }
              }
           }]
        };
        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    </script>
</body>
</html>

day04:用HttpClient+Jsoup的三种方式爬取网页内容

最终实现爬虫比价系统

苏宁,实现如果发现其他家商品比苏宁商品便宜,立刻降价。

软件系统,去各大网站爬取它们对应商品的价格,如果发现价格便宜,立刻修改自己的商品的价格。

技术难题:

如何解决去爬取其他商城的商品的信息,其中最重要价格!

爬虫项目中会涉及一些技术点

  1. JavaScriptjs,脚本语言python html/jsp

js规范它的代码都写在<script标签>固定属性language=”javascript”

         <script language="javascript">

                   调用js方法alert,作用在页面弹出一个提示框,框中显示字符串,以分号结尾

                   alert("Hello World.");

         </script>

         js只是在浏览器使用,功能受限,很多事情做不了(不能访问本地文件)

         这种语言脚本语言。

  1. Json

就是一个字符串”{‘name’:’hello World’}”

Json本质是字符串,但是这个字符串有规定

字符串{};有key=name,value=hello World;冒号分割key:value

http://p.3.cn/prices/mgets?skuIds=J_7348367

[

{

"op":"899.00",

"m":"9999.00",

"id":"J_22769568633",

"p":"899.00"

}

,

{

"op":"1699.00","m":"1999.00","id":"J_5842519","p":"1399.00"

}

]

[]开始,支持多条记录,数组,

每条记录以{开始,以}结束,每条记录以逗号分隔

4个属性,key,value,多个属性以逗号隔开

  1. Jsonp

Fun(json)

Showname(“{‘name’:’hello World’}”)

Jsonp本质就是一个函数名把一个json串括在里面

show([{"op":"899.00","m":"9999.00","id":"J_22769568633","p":"899.00"},{"op":"1699.00","m":"1999.00","id":"J_5842519","p":"1399.00"}])

http://d.3.cn/desc/7348367

  1. ObjectMapper转换工具

pojo转成jsonjson转成pojo

  1. HttpClient 最简单爬虫,爬取整个网站,Hadoop底层

  2. Jsoup 主角,专业爬虫;python beatifulsoup

  3. Echarts 百度专业统计图形工具

网站获取有3种类型:

  1. html

  2. json,本质字符串

  3. jsonpfun(json) 本质还是字符串

追求:

  1. 对应目的,使用哪个工具更趁手

在工作中总会遇到新的知识,领导还不给你时间!

学会利用现有的程序,变成新的内容!

爬虫,学会爬京东,自己去学会爬淘宝。

创建一个javaWeb项目

  1. 导入jar

commons-logging-1.1.1.jar                                  公用日志包,工具内部需要

httpclient-4.3.3.jar                                                httpClient模拟http请求的客户端工具

httpcore-4.3.2.jar                                                  依赖包

jsoup-1.7.2.jar                                                         jsoup爬虫包,爬虫工程师

jackson-annotations-2.4.0.jar                             jackson json      ObjectMapper对象 pojo<>json

jackson-core-2.4.2.jar

jackson-databind-2.4.2.jar

fastjson-1.1.37.jar

抓取一个页面后

1.获取整个页面html

2.从中可以获取标题,内容,图片

可以做自己的新闻网站,今日头条都是自己的新闻吗?不是!

怎么从Html里分离出标题,内容,图片。

1.创建项目和导入jar包

1.1 用HttpClient的方式抓取

package test;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
public class TestHttpClient {
    
    @Test
    public void page() throws ClientProtocolException, IOException {
        //目标:抓取整个网站
        
        /*
         * 用httpclient工具包来实现抓取网站
         * 1)创建builder对象
         * 2)创建一个httpclient对象
         * 3)模拟发起一个http请求,url,可以去访问这个网站
         * 4)接收一个响应对象,对象中有很多的内容,我们只要页面的内容
         * 5)把这个页面对象转成string(html)
         */
        //                              客户端             习惯
        HttpClientBuilder builder = HttpClients.custom();
        HttpClient hc = builder.build();
        //构建一个请求的对象
        String url = "http://ent.qq.com/a/20180412/017735.htm";
        HttpGet get = new HttpGet(url);
        //执行完成把结果封装到响应的对象中,执行get请求
        HttpResponse response = hc.execute(get);
        //获取实体 返回对象
        HttpEntity entity = response.getEntity();
        //解析出整个页面的html代码;EntityUtils:实体工具对象
        String html = EntityUtils.toString(entity);
        System.out.println(html);
    }
}

1.2 用Jsoup的方式抓取

<h1> 标题</h1>

<h2><h3>

利用谷歌浏览器,快速定位标签,点击标签所在位置右键,检查

展现这个标签结构,如果只是部分内容,选择它的父标签,选中所有内容

工作中开发方式,无法把每个的内容都熟透!

应用!计算机是一门应用科学。

在别人的基础上实现新的功能。

HttpClient 模拟http请求,可以进行代码的提交。

问度娘,demo做实例,那实例修改!

分析它的一个结构

抓取整个网站

  1. 网站页面中都有很多链接去链接别的页面

  2. 把页面中所有的a标签都抓取到,把它的链接在去抓

  3. 重复

  4. 别的网站,只抓本域名下的链接

  5. 要避免死循环,出现过的不抓了

抓取所有的a标签:

 @Test      //抓取页面所有的a链接标签
 public void a() throws IOException{
    String url = "http://ent.qq.com/a/20171117/007399.htm";
    Elements els = Jsoup.connect(url).get().select("a");
    for(Element o:els){
        System.out.println(o.attr("href"));
    }
 }

企业中实现一个爬虫秘诀:

  1. 要分析每个不同网站的不同的页面组成结构

  2. 反爬虫(电商)二次加载(有地方再次发出请求url

org.jsoup.UnsupportedMimeTypeException:
Unhandled content type. Must be text/*, application/xml, or application/xhtml+xml. Mimetype=application/json;charset=utf-8, URL=http://p.3.cn/prices/mgets?skuIds=J_3296833
text/html

程序出错大多原因是获取的值不正确,eclipse给我们提供断点功能。

获取jsonp形式来解析

showdesc({"date":1525854280288,"content":""})

jd商品详情,二次提交,获取是showdesc(json)

Jackson ObjectMapper JsonNode

小结:

全版的

Connection cn = Jsoup.connect(url);         //引入报错
Document doc = cn.get();                           //页面js document
Elements els= doc.select(“.qq”).select(“.pp”);                  <div class=”qq pp”>
for(Element o:els){
         o.text();                      //div里面是文字
         o.attr(“href”);           //<a href=”http://www.jd.com/123123”>
         o.attr(“src”);             //<img src=”http://www.jd.com/123.jpg”/>
}
els.get(0);                  //获取数组中第一元素,哪怕只有一个元素

爬取的过程有异常吗?

有,网络出问题;对方网站忙;自己处理异常

  1. 抛出异常,谁调用我谁处理,

  2. try{}catch(Exception e){ 打印异常到控制台 }

1.第一种html

获取Html

获取Html
Jsoup.connect(url).get().body().text();
title 选择器是在页面中找寻指定位置,可以一个,也可以多个
Jsoup.connect(url).get().select(“h1”)

2.第二种json

数组[{“name”:”tony”,”age”:68},{“name”:”hellen”,”age”:18}]

Jsoup.connect(url) .ignoreContentType(true).get().body()      Element  .text()
获取Json时不是一个标准的响应text/html(html,jsp),这时必须 ignoreContentType(true)
String json就是一个字符串 [{“n”:”pr”,”p”:”3399.00”}]

怎么从这里获取p价格?

ObjectMapper MAPPER = new ObjectMapper();
JsonNode node = MAPPER.readTree(json);               
//readTree会把Json字符串,转成JsonNode对象
JsonNode node1 = node.get(0)         
//从数组中获取第一个元素
String price = node1.get(“p”).asText()

3.第三种jsonp

show ({“n”:”pr”,”p”:”3399.00”})
fun(json)
String jsonp = Jsoup.connect(url).ignoreContentType(true).get().body().text();
String json = jsonp.substring(5,jsonp.length()-1)               截取字符串
ObjectMapper MAPPER = new ObjectMapper();
JsonNode node = MAPPER.readTree(json);
node.get(“p”).asText();

难点:

  1. 页面变化 <div class=”yy”>,样式进行批量修改,重新分析编写爬虫代码

  2. 历史问题,历史写了多个不同的版本,根据不同的版本写不同的爬虫

  3. 它会检查是否上一个页面也是淘宝网址,如果不是直接不允许访问,如果是就可以继续

  4. 登录,cookie,session

------------------------------------------------------------------------------

上午:

1.第一种抓取html

创建TestJsoup

    /**
     * 抓取某个页面
     * @throws IOException
     */
    @Test
    public void html() throws IOException {
        String url = "http://ent.qq.com/a/20180412/017735.htm";
        //创建链接
        Connection conn = Jsoup.connect(url);
        //获取Document页面对象
        Document docu = conn.get();
        //获取页面的内容,jsoup中把网页中的所有body中的内容
        Element ele = docu.body();
        //获取到页面Html内容
        String html = ele.html();
        System.out.println(html);
    }
    /**
     * 抓取标题
     * @throws IOException
     */
    @Test
    public void titlel() throws IOException {
        String url = "http://ent.qq.com/a/20180412/017735.htm";
        //创建链接
        Connection conn = Jsoup.connect(url);
        //获取Document页面对象
        Document docu = conn.get();
        ////找到页面中所有的h1标签,放在一个元素集合
        Elements ele = docu.select("h1");
        //循环中每个元素类型 Elment,e给它起个别名,循环是每个对象,els就是上面的集合
        for(Element e : ele) {
            //获取其中的文本
            String title = e.text();
            System.out.println(title);
        }
        
    }

    /**
     * 获取新闻内容,唯一定位
     * @throws IOException
     */
    @Test
    public void content() throws IOException {
        String url = "http://ent.qq.com/a/20180412/017735.htm";
        //创建链接
        Connection conn = Jsoup.connect(url);
        //获取Document页面对象
        Document docu = conn.get();
        ////找到页面中所有的h1标签,放在一个元素集合
        Elements ele = docu.select(".bd");
        for(Element e:ele) {
            String content = e.text();
            System.out.println(content);
            
        }
    }

    /**
     * 获取新闻图片,获取新闻中多张图片
     * @throws IOException
     */
    @Test
    public void img() throws IOException {
        String url = "http://ent.qq.com/a/20180412/017735.htm";
        Elements ele = Jsoup.connect(url).get().select("p img");
        for(Element e:ele) {
            String img = e.attr("src");
            System.out.println(img);
        }
    }

    /**
     * 抓取页面所有的a链接标签
     * @throws IOException
     */
    @Test   
    public void a() throws IOException{
        String url = "http://ent.qq.com/a/20171117/007399.htm";
        Elements els = Jsoup.connect(url).get().select("a");
        for(Element o:els){
            System.out.println(o.attr("href"));
        }
    }

------------------------------------------------------------------------------

下午:

2.第二种抓取json串数据

1、创建TestJD类抓取京东商品的价格

    /**
     * 在爬取网页内容时,请求返回的是json字符串
     * 如何来获取json串中的数据
     *
     * 例:获取京东商品的价格;
     * @throws IOException
     */
    @Test
    public void price() throws IOException {
        String url = "http://p.3.cn/prices/mgets?skuIds=J_3296833";
        //ignoreContentType:忽略内容类型
        String json = Jsoup.connect(url).ignoreContentType(true).get().body().text();
        System.out.println(json);
        
        //2.获取json字符串中把p获取到
        
        ObjectMapper om = new ObjectMapper();
        //把字符串转成成JsonNode对象结构
        JsonNode node = om.readTree(json);
        //从JsonNode对象结构获取p
        String price = node.get(0).get("p").asText();
        System.out.println(price);//2999.00
    
    }

3.第三种抓取jsonp数据

    /**
     * 3.第三种抓取jsonp中的数据
     *
     * 获取商品的详情
     * @throws IOException
     */
    @Test
    public void deatil() throws IOException {
        String url = "http://d.3.cn/desc/7348367";
        String jsoup              =Jsoup.connect(url).ignoreContentType(true).get().body().text();
        //截取字符串 showdesc({"date":
        String json =jsoup.substring(9, jsoup.length()-1);
        //获取Json串中的content属性的内容
        ObjectMapper om = new ObjectMapper();
        JsonNode node = om.readTree(json);
        String desc =node.get("content").asText();
        System.out.println(desc);
        
    }

day05:抓取京东商品一系列信息(标题,卖点,价格,图片,描述)

知识回顾:

1.概念

Javascript

脚本语言(弱语言,只在网页使用,为了安全原因)java(面向对象,强语言)

Css两种方式

style属性;link标签 base.css

Js两种方式

<script language=”javascript”> js语言</script>   xxx.js

Json

本质字符串,String a = “[{“name”:”[{},{}]”,”age”,18},{}]”; 

 成为日常系统交换数据的通用方式,安卓手机,httpclient+json

Jsonp

本质字符串,show([{},{}]) javascript函数,解决跨域问题

ObjectMapper

Jackson json

专门用于pojojson字符串直接转换,从json字符串中挑出我们关心的属性的值

           ObjectMapper MAPPER = new ObjectMapper();
           //从json字符串中获取某个key的value?
           JsonNode node = MAPPER.readTree(json);
           //注意node中的结构,
           数组:node.get(0).get(“p”).asText();
           直接是单个元素:node.get(“p”).asText();

2.爬虫

Httpclient

模拟发起一个http请求,携带参数,获取返回值

Jsoup

真正爬虫,对页面数据有一套解析方法,利用css,样式表+jQuery提出选择器

选择器3种情况

<div id=”orderId” class=”ordercss  detail”></div>

  1. 直接使用html规范的标签:div

  2. 使用id#orderId

  3. 使用class.ordercss

  4. .select(.ordercss).select(.detail) 特殊情况

抓取一个title整个过程?

String url = “sina/1289392.htm”;
Connection cn = Jsoup.connect(url);         //找到要爬取的网站
Docment doc = cn.get();                             //获取到爬取的页面
Elements els = doc.select(“h1”);               //获取了h1标签的集合,集合只有一个元素
Element ele = els.get(0);                             //只获取第一个
for(Element e:els){                                        //获取多张图片
         e.attr(“src”);
}
ele.text();       

简洁方式:

Jsoup.connect(url).get().select(“h1”).get(0).text();
  1. 怎么在eclipse中调试代码(最多)

断点:breakpoint

debug模式程序会自动进入到断点。

弹出的框是提示我们要进入新的debug的窗口环境,设置为yes

以后不会弹出。

断点调试3个按钮,也支持快捷键

F5     进入到子程序中,调用一个函数,进入函数

F6     执行一行(用的最多)

F7     跳出当前的执行,返回上级调用

         Fun a(){

                   Funb();

}

断点方便我们观察每个值,

时间开发中,大多数错误99%,变量的值不对;

通过断点观察这个值对不对!

  1. 抓取jd它的价格和描述怎么来的?

典型的二次提交

拷贝请求:

https://p.3.cn/prices/mgets?callback=jQuery634178&type=1&area=1_2901_4135_0.137859419&pdtk=&pduid=15196129033001458838393&pdpin=52399178_m&pin=52399178_m&pdbp=0&skuIds=J_4483112&ext=11000000&source=item-pc

http://p.3.cn/prices/mgets?skuIds=J_7348367

https://

协议

http://

安全,防止黑客攻击

/prices

映射路径

p.3.cn

网址

,3.cn域名p.3.cn

二级域名

mgets

类似servlet

?

参数的开始,很多的参数

&

参数之间分隔符

key,value

callback

参数名称

jQery334234

参数的值

怎么去抓取jd商城的商品详情?

  1. 抓取核心信息:标题、卖点、价格、图片、描述

  2. https://item.jd.com/7348367.html

https://item.jd.com/7348368.html

https:// 协议

item.jd.com二级域名

7348367  代表唯一标识id

自增

解决方案:for 0(起始值小于网站商品的id值)到 99999999(大于最大的商品的id号)

Jsoup一个一个url

https://item.jd.com/+7348368+“.html”

缺点:很多链接无效的!

判断是否无效,判断时间很短,先必须出错,时间耗时!

从一级分类链接页面没规律,二级分类链接页面没规律,都直接找不到很多 商品的链接。

三级分类,点开列表页面,5*12=60,一页中显示60个商品

分页

找到列表页面,利用选择器找到当前页的所有的商品图片链接

Id就可以找到,找到链接jsoup,抓取商品详情!

https://list.jd.com/list.html?cat=9987,653,655#

https://list.jd.com/list.html?cat=9987,653,655&page=162

找到所有分页,再次请求,抓取到所有的页面的链接

这个方案中没有多余的id,都是真实的,抓取效率高!

总结:

目标:抓取所有商品的商品详情信息

  1. 只关注3级分类

  2. 获取分页总数

  3. 获取每一页数据

  4. 直接获取每个商品的详情

  5. 放到数据库(京东,淘宝,苏宁)比价系统

额外:

1.获取京东网站所有三级分类

网址选择步骤:

第一步:https://www.jd.com/

第二步:https://shouji.jd.com/

第三步:https://www.jd.com/allSort.aspx

https://www.jd.com/allSort.aspx

aspx.net,后台服务都使用java

创建项目-包-类

代码实现:

    /**
      * 获取京东网站所有三级分类
      */
     @Test
     public void level3() throws IOException{
           String url = "https://www.jd.com/allSort.aspx";
           Elements els = Jsoup.connect(url).get()
                .select(".items .clearfix dd a");
           for(Element e:els){
                //获取到商品的链接
                String itemUrl = "http:"+e.attr("href");
                //判断依据,有共同的前缀,以prefix前缀,true,false
                if(itemUrl.startsWith("http://list.jd.com/list.html?cat=")){
                     System.out.println(itemUrl);
                }
           }
     }

2.抓取某个分类分页总数,所有分页链接

            手机3级分类,161页,161*60

  1. 获取每一页数据

  1. 获取详情的链接   https://list.jd.com/list.html?cat=9987,653,655&page=1

   

     /**
     * 抓取某个分类分页总数,所有分页链接
     * @throws IOException
     */
    @Test
    public void itemListUrl() throws IOException {
        
        String url ="https://list.jd.com/list.html?cat=9987,653,655";
        //获得总页数
        Elements  ele = Jsoup.connect(url).get().select(".p-skip").select("b");
        String pageAll=null;
        for(Element e :ele) {
            pageAll = e.text();
        }
        int page = Integer.parseInt(pageAll);
        System.out.println(page);
        
        //获得每一页的链接
        String href = url+"&page=";
        for(int i=1;i<=page;i++) {
            String hrefs = href+i;
            System.out.println(hrefs);
        }
    }

3.获取商品的链接

/**
 * 获取商品的链接
 * @throws IOException
 */
 @Test
 public void itemUrl() throws IOException{
        String url = "https://list.jd.com/list.html?cat=9987,653,655&page=154";
        Elements els = Jsoup.connect(url).get().select(".p-img a");
           for(Element o:els){
                String itemUrl = "http:"+o.attr("href");
                System.out.println(itemUrl);
           }
 }

4.抓取所有商品的商品详情信息(标题,卖点,价格,图片,描述

标题:

/**
 * 标题
 * @throws IOException
 */
 @Test
 public void getTitle() throws IOException {
        String url ="https://item.jd.com/7348367.html";
        String title =Jsoup.connect(url).get().select(".sku-name").get(0).text();
        System.out.println("商品的标题是:"+title);
 }

卖点:

查看数据:

得到:

https://c.3.cn/recommend?callback=handleComboCallback&methods=accessories&sku=7348367&cat=9987%2C653%2C655


 

/**
 * 卖点 json
 * 拿到:荣耀10 全面屏AI摄影手机 6GB+64GB 游戏手机 幻夜黑 全网通 移动联通电信4G 双卡双待
 * @throws IOException
 */
 @Test
 public void getSellPoint() throws IOException {
        String url ="https://c.3.cn/recommend?callback=handle"+
                    "ComboCallback&methods=accessories&sku=73"+
                    "48367&cat=9987%2C653%2C655";
        String jsoup = Jsoup.connect(url).ignoreContentType(true).execute().body();
        
        String json =jsoup.substring(20, jsoup.length()-1);
        
        System.out.println(json);
        ObjectMapper om = new ObjectMapper();
        JsonNode node = om.readTree(json);
        String SellPoint =node.get("accessories").get("data").get("wName").asText();
        System.out.println("卖点:"+SellPoint);
 }

价格:

1.找到获取价格的请求可以看出是个jsoup的数据:

2.复制这条请求用谷歌浏览器筛选自己需要的数据得到

https://p.3.cn/prices/mgets?callback=jQuery1348823&type=1&skuIds=J_7348367

4.用火狐浏览器打开复制截取的链接确认数据类型:

    /**
     * 价格
     * @throws IOException
     */
    @Test
    public void getPrice() throws IOException {
        String url = "https://p.3.cn/prices/mgets?callback=jQuery1348823&type=1&skuIds=J_7348367";
        //得到jsonp:
        //jQuery1348823([{"op":"2599.00","m":"9999.00","id":"J_7348367","p":"2599.00"}]);
        String jsonp =Jsoup.connect(url).ignoreContentType(true).execute().body();
        System.out.println(jsonp);
        //截取成json串
        String json = jsonp.substring(14, jsonp.length()-3);
        System.out.println(json);
        ObjectMapper om = new ObjectMapper();
        JsonNode node = om.readTree(json);
        String price = node.get(0).get("op").asText();
        System.out.println("价格:"+price);
      
    }

图片

    /**
     * 图片
     * @throws IOException
     */
    @Test
    public void getImage() throws IOException {
        String url="https://item.jd.com/7348367.html";
        Elements ele  = Jsoup.connect(url).get().select(".lh li img");
       
        for(Element e:ele) {
            String img = e.attr("src");
            System.out.println(img);
        }
    }

描述

    /**
     * 商品介绍
     * @throws IOException
     */
    @Test
    public void getDesc() throws IOException {
        String url = "https://item.jd.com/7348367.html";
        Elements ele = Jsoup.connect(url).get().select(".p-parameter").select("ul").select("li");
        //System.out.println(ele);
        for(Element e : ele) {
            
            System.out.println(e.text()+"\n");
        }
    }
  1. 只关注3级分类https://www.jd.com/allSort.aspx进入https://list.jd.com/list.html?cat=9987,653,655

选择器select,它里面多个样式,必须嵌套关系

https://list.jd.com/list.html?cat=9987,653,655

http://e.jd.com/

https://mvd.jd.com/music.html

https://list.jd.com/list.html?cat=737,794,798

爬虫爬取比较规范的数据,不规范的数据舍弃,如果需要这部分数据,单独写爬虫!

我们抓到三级分类1259个!1183有效!

  1. 抓取商品详情,核心:

                标题

                卖点

                价格

                图片

                描述

小结:

抓取京东商品详情过程

  1. 获取京东商城的所有的三级页面,商品列表页面都是由三级页面点击而来

  2. 进入列表第一个页面,获取它的总页数,拼接成所有的访问页的链接

  3. 访问列表页面就获取到页面上的商品链接

  4. 一个一个属性抓取

    1. 标题,直接抓网页定位

    2. 卖点,二次请求,json

    3. 价格,二次请求,json

    4. 图片,只小图片,集合,直接抓取页面定位

    5. 描述,二次请求,jsonp,去掉函数就是json

所有的Json引入ObjectMapper去搞定。

最难最耗时,二次请求和链接分析

反爬虫:

  1. 通过选择器来筛选,把class名称,id

  2. 二次加载,json,换个属性名,jsonp换函数名


day06:京东、淘宝、苏宁、比价系统框架的搭

知识回顾:

系统体验爬虫

爬取京东商城网站商品详情

开发步骤:

  1. 通过3级分类

https://www.jd.com/allSort.aspx

只有在3级分类下才会链接列表页面

一定要验证抓取结果是否正确,不正确,抓取的代码有问题,修改!

https://list.jd.com/list.html?cat=9987,653,655

http://e.jd.com/

找共性,先把最多的统一链接形式的内容抓取下来,一般就足够了,

如果还不够,就像把电子书的抓取到?自己在写程序。

怎么找共性?

先把所有三级分类链接都打印出来,一扫一眼,就知道哪种更加标准或者说更多使用。

2)所有的商品列表中有商品id,链接

https://list.jd.com/list.html?cat=9987,653,655

里面是含有分页,一页只能抓取60个数据,

https://list.jd.com/list.html?cat=9987,653,655&page=3

分析出来,其他的参数可以不要,只要page参数,page就是当前页

拿到总页数,把它拼接成所有的分页链接,然后一个一个链接去抓取就可以

3)获取到每页一个链接,获取商品详情

开始自己在页面中抓取各个展现数据。

  1. 标题是可以直接抓取到

  2. 利用直接抓取price价格,抓到空,观察,加载情况,标题人家是先出来的,短暂停留,价格才显现出来。直接就判断价格,二次提交,写入到它的位置。(ajax

找到二次提交的链接?

反爬虫,不会在链接中出现这种关键字

最差一个情况:一个一个链接找

它隐藏,有时隐藏js

没思路,百度,找前人的经验!

  1. 卖点

http://ad.3.cn/ads/mgets?skuids=AD_1411013,AD_1411014

百度获取的链接,从前人经验中获取这个信息

  1. 图片

直接在网页,直接能抓取小图片

  1. 描述

http://d.3.cn/desc/1411013

f12调试时,过滤查询到,也可以获取前人的经验

抓取一个新网站时两种方式

  1. 自己分析

  2. 获取前人的经验(百度)

豆瓣(毕业设计)

电影,评分,评论

  1. 电影推荐

  2. 电影海报 quartz定时器,每天一张海报

  3. 评分可以写成排行,每月一个电影排行榜

  4. 评论

统计图表

  1. 饼形图

换成:个人消费的饼形图

要求:

  1. 3个,生活中的分类;住宿,旅游,学习

  2. 5个,住宿,旅游,学习,吃饭,交女盆友

  1. 柱状图

  1. 3个,3次月考成绩

  2. 5个指标,语文99,数学100,英语60,化学5,物理10

  1. 曲线图

  1. 一周早晨到校时间

  2. 工作日5天离校时间

工作中解决问题:

以工程思路去做!

最终项目目标:

需求:

抓取京东、淘宝、苏宁三家电商,某个商品的价格,用echarts百度图表软件展现出来,领导一看非常直观,领导来决定是否降价。

关键技术点:

  1. 抓取京东商城的某个商品的价格?

    1. 确定某个商品,(动态抓)

  2. 如何抓取淘宝、苏宁?

  3. 如何最终在echarts页面上展现出来

隐藏一个问题:怎么判断是同一个商品?

商品有不同规格和颜色,怎么能断定它是同一个呢?

技巧:

思路:(一个,动态)

利用搜索条,假设输入条件足够详细,理论上查询出来的商品信息匹配度越高。可以近似认为它们是等同。

来抓取列表的第一个商品的信息,价格信息,然后把3个商城的价格信息

传递给jsp页面,形成echarts所要的数据结构。

最终形成柱状图。

扩展:(批量,事先抓好)

  1. 都抓到数据库

  2. 列出多个字段,

  3. 做一个判断,京东-苏宁<0,修改价格,淘宝-苏宁<0

京东

https://search.jd.com/Search?keyword=iphonex%2064g

https://search.suning.com/iphonex%2064g/

https://list.tmall.com/search_product.htm?q=iphonex+64g

开发步骤:

  1. 搜索url都有找到,动态拼接上搜索关键字

  2. Servlet+创建jsp文件,输入框,用户可以填写关键字(表单)

SearchServletsearch.jsp

  1. 提交submit,转向到另外servlet,利用jsoup分别抓各个商场搜索数据,近似值,抓取第一款商品id

写成3个方法

getJDPrice         抓取京东商城某个商品的价格

getTBPrice         抓取淘宝商城某个商品的价格

getSNPrice         抓取苏宁商城某个商品的价格

  1. 通过商品详情链接,拼接上id,找到商品链接地址

  2. 只抓取价格

  3. 3个价格都抓取到拼接成满足echarts要的字符串结构,把字符串request传递给统计jsp页面

DoServlet调用每个方法,获取其价格,然后拼接jsp页面所要字符串

写入request对象中

  1. 统计的Jsp页面通过el表达式获取数据,最终以柱状图展现

result.jsp

实际开发中,

  1. 先搭建框架,其中遇到方法,假装!getPrice return 100;

  2. 每个小细节最终不同的人员完成,团队开发。

在页面中请求分成2类,

一类get请求,

https://search.jd.com/Search?keyword=iphonex%2064g%20黑色

一类post请求(表单请求就属于)

<form method="post" action="/jt-crawler/DoServlet">

<input type="button"      按钮

onclick="history.back();return false;"               onclick单击事件

history.back();           history是系统提供,back()是回退到上一个页面

return false;              放在它去干别的事情,死记!

 name="ok" value="返回"/>知识回顾:

系统体验爬虫

爬取京东商城网站商品详情

开发步骤:

  1. 通过3级分类

https://www.jd.com/allSort.aspx

只有在3级分类下才会链接列表页面

一定要验证抓取结果是否正确,不正确,抓取的代码有问题,修改!

https://list.jd.com/list.html?cat=9987,653,655

http://e.jd.com/

找共性,先把最多的统一链接形式的内容抓取下来,一般就足够了,

如果还不够,就像把电子书的抓取到?自己在写程序。

怎么找共性?

先把所有三级分类链接都打印出来,一扫一眼,就知道哪种更加标准或者说更多使用。

2)所有的商品列表中有商品id,链接

https://list.jd.com/list.html?cat=9987,653,655

里面是含有分页,一页只能抓取60个数据,

https://list.jd.com/list.html?cat=9987,653,655&page=3

分析出来,其他的参数可以不要,只要page参数,page就是当前页

拿到总页数,把它拼接成所有的分页链接,然后一个一个链接去抓取就可以

3)获取到每页一个链接,获取商品详情

开始自己在页面中抓取各个展现数据。

  1. 标题是可以直接抓取到

  2. 利用直接抓取price价格,抓到空,观察,加载情况,标题人家是先出来的,短暂停留,价格才显现出来。直接就判断价格,二次提交,写入到它的位置。(ajax

找到二次提交的链接?

反爬虫,不会在链接中出现这种关键字

最差一个情况:一个一个链接找

它隐藏,有时隐藏js

没思路,百度,找前人的经验!

  1. 卖点

http://ad.3.cn/ads/mgets?skuids=AD_1411013,AD_1411014

百度获取的链接,从前人经验中获取这个信息

  1. 图片

直接在网页,直接能抓取小图片

  1. 描述

http://d.3.cn/desc/1411013

f12调试时,过滤查询到,也可以获取前人的经验

抓取一个新网站时两种方式

  1. 自己分析

  2. 获取前人的经验(百度)

豆瓣(毕业设计)

电影,评分,评论

  1. 电影推荐

  2. 电影海报 quartz定时器,每天一张海报

  3. 评分可以写成排行,每月一个电影排行榜

  4. 评论

统计图表

  1. 饼形图

换成:个人消费的饼形图

要求:

  1. 3个,生活中的分类;住宿,旅游,学习

  2. 5个,住宿,旅游,学习,吃饭,交女盆友

  1. 柱状图

  1. 3个,3次月考成绩

  2. 5个指标,语文99,数学100,英语60,化学5,物理10

  1. 曲线图

  1. 一周早晨到校时间

  2. 工作日5天离校时间

工作中解决问题:

以工程思路去做!

最终项目目标:

需求:

抓取京东、淘宝、苏宁三家电商,某个商品的价格,用echarts百度图表软件展现出来,领导一看非常直观,领导来决定是否降价。

关键技术点:

  1. 抓取京东商城的某个商品的价格?

    1. 确定某个商品,(动态抓)

  2. 如何抓取淘宝、苏宁?

  3. 如何最终在echarts页面上展现出来

隐藏一个问题:怎么判断是同一个商品?

商品有不同规格和颜色,怎么能断定它是同一个呢?

技巧:

思路:(一个,动态)

利用搜索条,假设输入条件足够详细,理论上查询出来的商品信息匹配度越高。可以近似认为它们是等同。

来抓取列表的第一个商品的信息,价格信息,然后把3个商城的价格信息

传递给jsp页面,形成echarts所要的数据结构。

最终形成柱状图。

扩展:(批量,事先抓好)

  1. 都抓到数据库

  2. 列出多个字段,

  3. 做一个判断,京东-苏宁<0,修改价格,淘宝-苏宁<0

京东

https://search.jd.com/Search?keyword=iphonex%2064g

https://search.suning.com/iphonex%2064g/

https://list.tmall.com/search_product.htm?q=iphonex+64g

开发步骤:

  1. 搜索url都有找到,动态拼接上搜索关键字

  2. Servlet+创建jsp文件,输入框,用户可以填写关键字(表单)

SearchServletsearch.jsp

  1. 提交submit,转向到另外servlet,利用jsoup分别抓各个商场搜索数据,近似值,抓取第一款商品id

写成3个方法

getJDPrice         抓取京东商城某个商品的价格

getTBPrice         抓取淘宝商城某个商品的价格

getSNPrice         抓取苏宁商城某个商品的价格

  1. 通过商品详情链接,拼接上id,找到商品链接地址

  2. 只抓取价格

  3. 3个价格都抓取到拼接成满足echarts要的字符串结构,把字符串request传递给统计jsp页面

DoServlet调用每个方法,获取其价格,然后拼接jsp页面所要字符串

写入request对象中

  1. 统计的Jsp页面通过el表达式获取数据,最终以柱状图展现

result.jsp

实际开发中,

  1. 先搭建框架,其中遇到方法,假装!getPrice return 100;

  2. 每个小细节最终不同的人员完成,团队开发。

在页面中请求分成2类,

一类get请求,

https://search.jd.com/Search?keyword=iphonex%2064g%20黑色

一类post请求(表单请求就属于)

<form method="post" action="/jt-crawler/DoServlet">

<input type="button"      按钮

onclick="history.back();return false;"               onclick单击事件

history.back();           history是系统提供,back()是回退到上一个页面

return false;              放在它去干别的事情,死记!

 name="ok" value="返回"/>


day07:比价系统具体完善

知识回顾:

  1. 画图

拿到项目,任务,无从下手。

  1. 分解任务,把一个大任务分成很多的小的任务,然后去实现每个任务,链接起来就实现最终大任务。开发步骤。

  2. 画图,一图百文。流程图。整个完成爬虫过程。先把框架性画出来,然后一个一个点去实现,最终项目也就完成。

  1. 系统框架图,系统的架构图

Servlet,jsp,jsoup

         任务:爬取京东、淘宝、苏宁;动态实现输入关键字,我们的系统后台分别去每个网站抓取价格,最终返回后拼接成字符串,最终通过echarts柱状图展现。

今天内容:

  1. 从头把整个项目实现

开发步骤:

  1. 创建javaWeb,配置tomcat,把项目部署上去

  2. 搭建项目框架,然后在细化每个内容

创建类:SearchServletDoServlet

创建安全的访问的jsp路径/WEB-INF/views

创建两个空的jspsearch.jspresult.jsp

  1. 细化

SearchServlet直接转向search.jsp

search.jsp创建<form method=”post” action=”/jt-crawler/DoServlet”>

创建一个输入框<input type=”text” name=”key”>

创建一个提交按钮<input type=”submit” name=”ok” value=”提交”>(提交按钮的价值在于用户点击后自动去执行form.action链接)

DoServlet获取参数request.getParameter(“key”),转换中文

调用3个网站爬虫方法,京东jsoup,苏宁jsoup,淘宝jsoup+httpclient

resuslt.jsp

接收DoServlet参数,标识一个价格字符串,直接用el表达式嵌入到json

Jsp因为语法校验,如果出错,出错提示,看看是否是自己写错。

不管它了,在页面运行起来是进行检查!

Class报错,预编译错误,当文件保存,eclipse进行编译。如果报错说明没有编译过去。基本是运行不了的,这种错误必须解决。

  1. 把项目进行优化(重构)

爬虫遇到新的内容

  1. 反爬虫技术,如果是当前网站转过来的链接就可以继续的访问,如果不是就直接拒绝

403 Forbidden

淘宝为了链接的安全,防止盗链,或者从其他的网站来提交这个链接

检查域名taobao.com,

解决方案:

自己设置一个请求头,Referer代表当前页是从哪个页面过来的(html规范)

  1. 反爬虫,每个请求都会有一个请求头来标识自己是谁,规范。请求头中就会标识我是火狐,就会标识我是一个谷歌浏览器,如果httpclient它也有自己的标识,jsoup它也有自己的标识。<head>

服务器后台可以获取这个信息,判断如果是浏览器就可以执行,如果不是也就说明是爬虫类型,服务器就会拒绝

解决方案:

伪装请求头

  1. 反爬虫,服务器会获取到所有用户的请求,例如tomcat就可以,它会把所有的信息写入到日志文件,监控这个日志文件,就能发现在很短时间内某一个ip地址频繁的访问,1s几百次以上。Nginx监控,封杀。害怕封杀错误,把这个ip暂时加入到一个黑名单中,1分钟后释放。

解决方案:

  1. 动态ip,花生壳(不是很好用)

  2. 简单易行,延时。延时1分钟,黑名单1小时,抓取效率极低。

小结:

  1. 爬虫,几种方式httpclient,jsoup

  2. httpClient模拟浏览器发起http请求,请求和响应responseapiapplication interface 应用程序接口)把httpClient工具包。Api学习成本高

  3. jsoup java soupjava爬虫,引入选择器select,选择器利用tagdiv span h1 a img,id(页面唯一性,#id,class属性(style样式修饰用,定位,重复几率很大, .class

  4. 获取json中某个节点的数据

ObjectMapper MAPPER = new ObjectMapper();

JsonNode node = MAPPER.readTree(json字符串)

数组

forJsonNode n :node{

 n.get(“name”);

}

node.get(0);

文本 node.get(“name”).asText();

获取它的href属性:node.get(0).get(“name”).attr(“href”);

  1. Servlet3.0 ,(2.3 需要一个配置文件去声明servlet)通过@WebServlet注解方式

  2. Request请求对象,封装jsp页面的请求参数,request.getParamter

  3. 转向request.getRequestDispatcher(“result.jsp”).forword(request,response);

  4. Jsp引入样式表 <link rel=”stylesheet” href=”css/base.css”>

  5. Jsp引入taglib <%@ taglib prefix=”c” uri=” http://java.sun.com/jsp/jstl/core”>

<%@ taglib prefix=”fmt” uri=”http://java.sun.com/jsp/jstl/fmt”>格式化日期

  1. Jstl进行for循环

<c:forEach var=”o” items=”${oList}”>

 ${o.name}

</c:forEach>

  1. EL表达式${name}

  2. Json [{“name”:”tony”},{}] 中括号数组,大括号一条记录,字符串使用双引号括起来,之间用逗号分库

  3. Jsonp fun(json),去掉函数名,剩下就是json,通过ObjectMapper

  4. <script> javascript脚本语言,增强页面展示能力和做些简单交互js

  5. Echarts.js利用js生成特效效果,利用json串结构来声明数据等,然后通过js对齐进行渲染render,把页面形成图形化的内容。统计图形:饼形图,柱状图,曲线图。

一开始树立项目观念,和学习技能有什么不同?

项目,要在最短的时间内把东西做出来!不需要最完美的,先搞定01的突破,然后在1n

永和项目和爬虫项目就可以在后面学习些技能后,就来改造我们的项目!

拓展:

永和项目,任何一家小票打印都可以融入到这个项目。做10家!

爬虫项目,豆瓣网数据爬取,百合网(3000w数据!)3000w查询10分钟+,创建索引,1s秒内返回。爬虫爬取电影院坐标,百度地图,直接展示爬取数据。


作者:Darren

电话:15110448224

QQ:603026148

以上内容归Darren所有,如果有什么错误或者不足的地方请联系我,希望我们共同进步。


猜你喜欢

转载自blog.csdn.net/weixin_39519454/article/details/84039221