目录:
day01:爬取新闻网站
day02:爬取京东商城
day03:爬取商品价格+报表eCharts
day04:用HttpClient+Jsoup的三种方式爬取网页内容
day05:抓取京东商品一系列信息(标题,卖点,价格,图片,描述)
day06:京东、淘宝、苏宁、比价系统框架的搭建
day07:比价系统具体完善
第一天:爬取新闻网站
比价器系统
比价系统功能
-
利用Jsoup爬取每个页面的商品信息
-
ECharts柱状图、曲线图
-
工作原理
利用每个电商网站的搜索条查询同样的条件,例如: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传递了查询条件。
抓取步骤:
-
用户输入查询条件
-
分别到每个电商网站访问,利用其查询出它们网站的对应商品
-
获取其列表页面中第一个商品的链接
-
利用jsoup爬取每个页面的商品信息
-
把商品信息入库并设置爬取时间
-
使用ECharts进行进行价格的比较
-
拓展: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格式数据,回显到页面
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的三种方式爬取网页内容
最终实现爬虫比价系统
苏宁,实现如果发现其他家商品比苏宁商品便宜,立刻降价。
软件系统,去各大网站爬取它们对应商品的价格,如果发现价格便宜,立刻修改自己的商品的价格。
技术难题:
如何解决去爬取其他商城的商品的信息,其中最重要价格!
爬虫项目中会涉及一些技术点
-
JavaScript,js,脚本语言python html/jsp
js规范它的代码都写在<script标签>固定属性language=”javascript”
<script language="javascript">
调用js方法alert,作用在页面弹出一个提示框,框中显示字符串,以分号结尾
alert("Hello World.");
</script>
js只是在浏览器使用,功能受限,很多事情做不了(不能访问本地文件)
这种语言脚本语言。
-
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,多个属性以逗号隔开
-
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"}])
-
ObjectMapper转换工具
pojo转成json;json转成pojo
-
HttpClient 最简单爬虫,爬取整个网站,Hadoop底层
-
Jsoup 主角,专业爬虫;python beatifulsoup
-
Echarts 百度专业统计图形工具
网站获取有3种类型:
-
html
-
json,本质字符串
-
jsonp,fun(json) 本质还是字符串
追求:
-
对应目的,使用哪个工具更趁手
在工作中总会遇到新的知识,领导还不给你时间!
学会利用现有的程序,变成新的内容!
爬虫,学会爬京东,自己去学会爬淘宝。
创建一个javaWeb项目
-
导入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做实例,那实例修改!
分析它的一个结构
抓取整个网站
-
网站页面中都有很多链接去链接别的页面
-
把页面中所有的a标签都抓取到,把它的链接在去抓
-
重复
-
别的网站,只抓本域名下的链接
-
要避免死循环,出现过的不抓了
抓取所有的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"));
}
}
企业中实现一个爬虫秘诀:
-
要分析每个不同网站的不同的页面组成结构
-
反爬虫(电商)二次加载(有地方再次发出请求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); //获取数组中第一元素,哪怕只有一个元素
爬取的过程有异常吗?
有,网络出问题;对方网站忙;自己处理异常
-
抛出异常,谁调用我谁处理,
-
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();
难点:
-
页面变化 <div class=”yy”>,样式进行批量修改,重新分析编写爬虫代码
-
历史问题,历史写了多个不同的版本,根据不同的版本写不同的爬虫
-
它会检查是否上一个页面也是淘宝网址,如果不是直接不允许访问,如果是就可以继续
-
登录,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 |
专门用于pojo和json字符串直接转换,从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>
|
抓取一个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();
-
怎么在eclipse中调试代码(最多)
断点:breakpoint
在debug模式程序会自动进入到断点。
弹出的框是提示我们要进入新的debug的窗口环境,设置为yes。
以后不会弹出。
断点调试3个按钮,也支持快捷键
F5 进入到子程序中,调用一个函数,进入函数
F6 执行一行(用的最多)
F7 跳出当前的执行,返回上级调用
Fun a(){
Funb();
}
断点方便我们观察每个值,
时间开发中,大多数错误99%,变量的值不对;
通过断点观察这个值对不对!
-
抓取jd它的价格和描述怎么来的?
典型的二次提交
拷贝请求:
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商城的商品详情?
-
抓取核心信息:标题、卖点、价格、图片、描述
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,都是真实的,抓取效率高!
总结:
目标:抓取所有商品的商品详情信息
-
只关注3级分类
-
获取分页总数
-
获取每一页数据
-
直接获取每个商品的详情
-
放到数据库(京东,淘宝,苏宁)比价系统
额外:
1.获取京东网站所有三级分类
网址选择步骤:
第三步: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
-
获取每一页数据
-
获取详情的链接 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);
}
卖点:
查看数据:
得到:
/**
* 卖点 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");
}
}
选择器select,它里面多个样式,必须嵌套关系
https://list.jd.com/list.html?cat=9987,653,655
https://list.jd.com/list.html?cat=737,794,798
爬虫爬取比较规范的数据,不规范的数据舍弃,如果需要这部分数据,单独写爬虫!
我们抓到三级分类1259个!1183有效!
-
抓取商品详情,核心:
标题
卖点
价格
图片
描述
小结:
抓取京东商品详情过程
-
获取京东商城的所有的三级页面,商品列表页面都是由三级页面点击而来
-
进入列表第一个页面,获取它的总页数,拼接成所有的访问页的链接
-
访问列表页面就获取到页面上的商品链接
-
一个一个属性抓取
-
标题,直接抓网页定位
-
卖点,二次请求,json
-
价格,二次请求,json
-
图片,只小图片,集合,直接抓取页面定位
-
描述,二次请求,jsonp,去掉函数就是json
-
所有的Json引入ObjectMapper去搞定。
最难最耗时,二次请求和链接分析
反爬虫:
-
通过选择器来筛选,把class名称,id改
-
二次加载,json,换个属性名,jsonp换函数名
day06:京东、淘宝、苏宁、比价系统框架的搭
知识回顾:
系统体验爬虫
爬取京东商城网站商品详情
开发步骤:
-
通过3级分类
https://www.jd.com/allSort.aspx
只有在3级分类下才会链接列表页面
一定要验证抓取结果是否正确,不正确,抓取的代码有问题,修改!
https://list.jd.com/list.html?cat=9987,653,655
找共性,先把最多的统一链接形式的内容抓取下来,一般就足够了,
如果还不够,就像把电子书的抓取到?自己在写程序。
怎么找共性?
先把所有三级分类链接都打印出来,一扫一眼,就知道哪种更加标准或者说更多使用。
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)获取到每页一个链接,获取商品详情
开始自己在页面中抓取各个展现数据。
-
标题是可以直接抓取到
-
利用直接抓取price价格,抓到空,观察,加载情况,标题人家是先出来的,短暂停留,价格才显现出来。直接就判断价格,二次提交,写入到它的位置。(ajax)
找到二次提交的链接?
反爬虫,不会在链接中出现这种关键字
最差一个情况:一个一个链接找
它隐藏,有时隐藏js
、
没思路,百度,找前人的经验!
-
卖点
http://ad.3.cn/ads/mgets?skuids=AD_1411013,AD_1411014
百度获取的链接,从前人经验中获取这个信息
-
图片
直接在网页,直接能抓取小图片
-
描述
在f12调试时,过滤查询到,也可以获取前人的经验
抓取一个新网站时两种方式
-
自己分析
-
获取前人的经验(百度)
豆瓣(毕业设计)
电影,评分,评论
-
电影推荐
-
电影海报 quartz定时器,每天一张海报
-
评分可以写成排行,每月一个电影排行榜
-
评论
统计图表
-
饼形图
换成:个人消费的饼形图
要求:
-
3个,生活中的分类;住宿,旅游,学习
-
5个,住宿,旅游,学习,吃饭,交女盆友
-
柱状图
-
3个,3次月考成绩
-
5个指标,语文99,数学100,英语60,化学5,物理10
-
曲线图
-
一周早晨到校时间
-
工作日5天离校时间
工作中解决问题:
以工程思路去做!
最终项目目标:
需求:
抓取京东、淘宝、苏宁三家电商,某个商品的价格,用echarts百度图表软件展现出来,领导一看非常直观,领导来决定是否降价。
关键技术点:
-
抓取京东商城的某个商品的价格?
-
确定某个商品,(动态抓)
-
-
如何抓取淘宝、苏宁?
-
如何最终在echarts页面上展现出来
隐藏一个问题:怎么判断是同一个商品?
商品有不同规格和颜色,怎么能断定它是同一个呢?
技巧:
思路:(一个,动态)
利用搜索条,假设输入条件足够详细,理论上查询出来的商品信息匹配度越高。可以近似认为它们是等同。
来抓取列表的第一个商品的信息,价格信息,然后把3个商城的价格信息
传递给jsp页面,形成echarts所要的数据结构。
最终形成柱状图。
扩展:(批量,事先抓好)
-
都抓到数据库
-
列出多个字段,
-
做一个判断,京东-苏宁<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
开发步骤:
-
搜索url都有找到,动态拼接上搜索关键字
-
Servlet+创建jsp文件,输入框,用户可以填写关键字(表单)
SearchServlet,search.jsp
-
提交submit,转向到另外servlet,利用jsoup分别抓各个商场搜索数据,近似值,抓取第一款商品id
写成3个方法
getJDPrice 抓取京东商城某个商品的价格
getTBPrice 抓取淘宝商城某个商品的价格
getSNPrice 抓取苏宁商城某个商品的价格
-
通过商品详情链接,拼接上id,找到商品链接地址
-
只抓取价格
-
3个价格都抓取到拼接成满足echarts要的字符串结构,把字符串request传递给统计jsp页面
DoServlet调用每个方法,获取其价格,然后拼接jsp页面所要字符串
写入request对象中
-
统计的Jsp页面通过el表达式获取数据,最终以柱状图展现
result.jsp
实际开发中,
-
先搭建框架,其中遇到方法,假装!getPrice return 100;
-
每个小细节最终不同的人员完成,团队开发。
在页面中请求分成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="返回"/>知识回顾:
系统体验爬虫
爬取京东商城网站商品详情
开发步骤:
-
通过3级分类
https://www.jd.com/allSort.aspx
只有在3级分类下才会链接列表页面
一定要验证抓取结果是否正确,不正确,抓取的代码有问题,修改!
https://list.jd.com/list.html?cat=9987,653,655
找共性,先把最多的统一链接形式的内容抓取下来,一般就足够了,
如果还不够,就像把电子书的抓取到?自己在写程序。
怎么找共性?
先把所有三级分类链接都打印出来,一扫一眼,就知道哪种更加标准或者说更多使用。
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)获取到每页一个链接,获取商品详情
开始自己在页面中抓取各个展现数据。
-
标题是可以直接抓取到
-
利用直接抓取price价格,抓到空,观察,加载情况,标题人家是先出来的,短暂停留,价格才显现出来。直接就判断价格,二次提交,写入到它的位置。(ajax)
找到二次提交的链接?
反爬虫,不会在链接中出现这种关键字
最差一个情况:一个一个链接找
它隐藏,有时隐藏js
、
没思路,百度,找前人的经验!
-
卖点
http://ad.3.cn/ads/mgets?skuids=AD_1411013,AD_1411014
百度获取的链接,从前人经验中获取这个信息
-
图片
直接在网页,直接能抓取小图片
-
描述
在f12调试时,过滤查询到,也可以获取前人的经验
抓取一个新网站时两种方式
-
自己分析
-
获取前人的经验(百度)
豆瓣(毕业设计)
电影,评分,评论
-
电影推荐
-
电影海报 quartz定时器,每天一张海报
-
评分可以写成排行,每月一个电影排行榜
-
评论
统计图表
-
饼形图
换成:个人消费的饼形图
要求:
-
3个,生活中的分类;住宿,旅游,学习
-
5个,住宿,旅游,学习,吃饭,交女盆友
-
柱状图
-
3个,3次月考成绩
-
5个指标,语文99,数学100,英语60,化学5,物理10
-
曲线图
-
一周早晨到校时间
-
工作日5天离校时间
工作中解决问题:
以工程思路去做!
最终项目目标:
需求:
抓取京东、淘宝、苏宁三家电商,某个商品的价格,用echarts百度图表软件展现出来,领导一看非常直观,领导来决定是否降价。
关键技术点:
-
抓取京东商城的某个商品的价格?
-
确定某个商品,(动态抓)
-
-
如何抓取淘宝、苏宁?
-
如何最终在echarts页面上展现出来
隐藏一个问题:怎么判断是同一个商品?
商品有不同规格和颜色,怎么能断定它是同一个呢?
技巧:
思路:(一个,动态)
利用搜索条,假设输入条件足够详细,理论上查询出来的商品信息匹配度越高。可以近似认为它们是等同。
来抓取列表的第一个商品的信息,价格信息,然后把3个商城的价格信息
传递给jsp页面,形成echarts所要的数据结构。
最终形成柱状图。
扩展:(批量,事先抓好)
-
都抓到数据库
-
列出多个字段,
-
做一个判断,京东-苏宁<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
开发步骤:
-
搜索url都有找到,动态拼接上搜索关键字
-
Servlet+创建jsp文件,输入框,用户可以填写关键字(表单)
SearchServlet,search.jsp
-
提交submit,转向到另外servlet,利用jsoup分别抓各个商场搜索数据,近似值,抓取第一款商品id
写成3个方法
getJDPrice 抓取京东商城某个商品的价格
getTBPrice 抓取淘宝商城某个商品的价格
getSNPrice 抓取苏宁商城某个商品的价格
-
通过商品详情链接,拼接上id,找到商品链接地址
-
只抓取价格
-
3个价格都抓取到拼接成满足echarts要的字符串结构,把字符串request传递给统计jsp页面
DoServlet调用每个方法,获取其价格,然后拼接jsp页面所要字符串
写入request对象中
-
统计的Jsp页面通过el表达式获取数据,最终以柱状图展现
result.jsp
实际开发中,
-
先搭建框架,其中遇到方法,假装!getPrice return 100;
-
每个小细节最终不同的人员完成,团队开发。
在页面中请求分成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:比价系统具体完善
知识回顾:
-
画图
拿到项目,任务,无从下手。
-
分解任务,把一个大任务分成很多的小的任务,然后去实现每个任务,链接起来就实现最终大任务。开发步骤。
-
画图,一图百文。流程图。整个完成爬虫过程。先把框架性画出来,然后一个一个点去实现,最终项目也就完成。
-
系统框架图,系统的架构图
Servlet,jsp,jsoup
任务:爬取京东、淘宝、苏宁;动态实现输入关键字,我们的系统后台分别去每个网站抓取价格,最终返回后拼接成字符串,最终通过echarts柱状图展现。
今天内容:
-
从头把整个项目实现
开发步骤:
-
创建javaWeb,配置tomcat,把项目部署上去
-
搭建项目框架,然后在细化每个内容
创建类:SearchServlet,DoServlet
创建安全的访问的jsp路径/WEB-INF/views
创建两个空的jsp:search.jsp,result.jsp
-
细化
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进行编译。如果报错说明没有编译过去。基本是运行不了的,这种错误必须解决。
-
把项目进行优化(重构)
爬虫遇到新的内容
-
反爬虫技术,如果是当前网站转过来的链接就可以继续的访问,如果不是就直接拒绝
403 Forbidden
淘宝为了链接的安全,防止盗链,或者从其他的网站来提交这个链接
检查域名taobao.com,
解决方案:
自己设置一个请求头,Referer代表当前页是从哪个页面过来的(html规范)
-
反爬虫,每个请求都会有一个请求头来标识自己是谁,规范。请求头中就会标识我是火狐,就会标识我是一个谷歌浏览器,如果httpclient它也有自己的标识,jsoup它也有自己的标识。<head>
服务器后台可以获取这个信息,判断如果是浏览器就可以执行,如果不是也就说明是爬虫类型,服务器就会拒绝
解决方案:
伪装请求头
-
反爬虫,服务器会获取到所有用户的请求,例如tomcat就可以,它会把所有的信息写入到日志文件,监控这个日志文件,就能发现在很短时间内某一个ip地址频繁的访问,1s几百次以上。Nginx监控,封杀。害怕封杀错误,把这个ip暂时加入到一个黑名单中,1分钟后释放。
解决方案:
-
动态ip,花生壳(不是很好用)
-
简单易行,延时。延时1分钟,黑名单1小时,抓取效率极低。
小结:
-
爬虫,几种方式httpclient,jsoup。
-
httpClient模拟浏览器发起http请求,请求和响应response,api(application interface 应用程序接口)把httpClient工具包。Api学习成本高
-
jsoup java soup,java爬虫,引入选择器select,选择器利用tag(div span h1 a img),id(页面唯一性,#id),class属性(style样式修饰用,定位,重复几率很大, .class)
-
获取json中某个节点的数据
ObjectMapper MAPPER = new ObjectMapper();
JsonNode node = MAPPER.readTree(json字符串);
数组
for(JsonNode n :node){
n.get(“name”);
}
node.get(0);
文本 node.get(“name”).asText();
获取它的href属性:node.get(0).get(“name”).attr(“href”);
-
Servlet3.0 ,(2.3 需要一个配置文件去声明servlet)通过@WebServlet注解方式
-
Request请求对象,封装jsp页面的请求参数,request.getParamter
-
转向request.getRequestDispatcher(“result.jsp”).forword(request,response);
-
Jsp引入样式表 <link rel=”stylesheet” href=”css/base.css”>
-
Jsp引入taglib <%@ taglib prefix=”c” uri=” http://java.sun.com/jsp/jstl/core”>
<%@ taglib prefix=”fmt” uri=”http://java.sun.com/jsp/jstl/fmt”>格式化日期
-
Jstl进行for循环
<c:forEach var=”o” items=”${oList}”>
${o.name}
</c:forEach>
-
EL表达式${name}
-
Json [{“name”:”tony”},{}] 中括号数组,大括号一条记录,字符串使用双引号括起来,之间用逗号分库
-
Jsonp fun(json),去掉函数名,剩下就是json,通过ObjectMapper
-
<script> javascript脚本语言,增强页面展示能力和做些简单交互js
-
Echarts.js利用js生成特效效果,利用json串结构来声明数据等,然后通过js对齐进行渲染render,把页面形成图形化的内容。统计图形:饼形图,柱状图,曲线图。
一开始树立项目观念,和学习技能有什么不同?
项目,要在最短的时间内把东西做出来!不需要最完美的,先搞定0到1的突破,然后在1到n。
永和项目和爬虫项目就可以在后面学习些技能后,就来改造我们的项目!
拓展:
永和项目,任何一家小票打印都可以融入到这个项目。做10家!
爬虫项目,豆瓣网数据爬取,百合网(3000w数据!)3000w查询10分钟+,创建索引,1s秒内返回。爬虫爬取电影院坐标,百度地图,直接展示爬取数据。
作者:Darren
电话:15110448224
QQ:603026148
以上内容归Darren所有,如果有什么错误或者不足的地方请联系我,希望我们共同进步。