py-02-爬虫比价器
目录:
day01:用HttpClient+Jsoup的三种方式爬取网页内容
day02:抓取京东商品一系列信息(标题,卖点,价格,图片,描述)
day03:京东、淘宝、苏宁、比价系统框架的搭建
day04:比价系统具体完善
day01
最终实现爬虫比价系统
苏宁,实现如果发现其他家商品比苏宁商品便宜,立刻降价。
软件系统,去各大网站爬取它们对应商品的价格,如果发现价格便宜,立刻修改自己的商品的价格。
技术难题:
如何解决去爬取其他商城的商品的信息,其中最重要价格!
爬虫项目中会涉及一些技术点
-
Javascript
,
js
,脚本语言
python html/jsp
js规范它的代码都写在
js 只是在浏览器使用,功能受限,很多事情做不了(不能访问本地文件) 这种语言脚本语言。- Json
- html
- json ,本质字符串
- jsonp , fun(json) 本质还是字符串
- 对应目的,使用哪个工具更趁手
- 导入 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 {
//目标:抓取整个网站
// 客户端 习惯
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的方式抓取
标题
利用谷歌浏览器,快速定位标签,点击标签所在位置右键,检查
展现这个标签结构,如果只是部分内容,选择它的父标签,选中所有内容
工作中开发方式,无法把每个的内容都熟透!
应用!计算机是一门应用科学。
在别人的基础上实现新的功能。
HttpClient
模拟
http
请求,可以进行
代码
的提交。
问度娘,
demo
做实例,那实例修改!
分析它的一个结构
抓取整个网站
- 网站页面中都有很多链接去链接别的页面
- 把页面中所有的 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
@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);
}
@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);
}
}
@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);
}
}
@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);
}
}
@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类抓取京东商品的价格
@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
}
2.第三种抓取jsonp数据
@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);
}
day02 知识回顾: 1.概念
| Javascript | 脚本语言(弱语言,只在网页使用,为了安全原因) java (面向对象,强语言) |
| Css 两种方式 | style 属性; link 标签 base.css |
| Js 两种方式 | 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 种情况 |
|
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(); //title文字
简洁方式:
Jsoup.connect(url).get().select(“h1”).get(0).text();
3.怎么在eclipse中调试代码(最多) 断点:breakpoint 在 debug 模式程序会自动进入到断点。 弹出的框是提示我们要进入新的 debug 的窗口环境,设置为 yes 。 以后不会弹出。 断点调试 3 个按钮,也支持快捷键 F5 进入到子程序中,调用一个函数,进入函数 F6 执行一行(用的最多) F7 跳出当前的执行,返回上级调用 Fun a(){ Funb(); } 断点方便我们观察每个值, 时间开发中,大多数错误 99% ,变量的值不对; 通过断点观察这个值对不对! 4.抓取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 | 参数的值 |
- 抓取核心信息:标题、卖点、价格、图片、描述
- 【华为荣耀10】荣耀10 GT游戏加速 AIS手持夜景 6GB+64GB 幻夜黑 全网通 移动联通电信4G 双卡双待 游戏手机【行情 报价 价格 评测】-京东
https:// 协议
item.jd.com二级域名
7348367 代表唯一标识id,
自增
解决方案:for 0(起始值小于网站商品的id值)到 99999999(大于最大的商品的id号)
Jsoup一个一个url抓
“京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!”+7348368+“.html”
缺点:很多链接无效的!
判断是否无效,判断时间很短,先必须出错,时间耗时!
从一级分类链接页面没规律,二级分类链接页面没规律, 都直接找不到很多 商品的链接。 三级分类,点开列表页面, 5*12=60 ,一页中显示 60 个商品 分页 找到列表页面,利用选择器找到当前页的所有的商品图片链接 Id 就可以找到,找到链接 jsoup ,抓取商品详情! https://list.jd.com/list.html?cat=9987,653,655# 【行情 价格 评价 正品行货】-京东,653,655&page=162 找到所有分页,再次请求,抓取到所有的页面的链接 这个方案中没有多余的 id ,都是真实的,抓取效率高! 总结: 目标:抓取所有商品的商品详情信息- 只关注 3 级分类
- 获取分页总数
- 获取每一页数据
- 直接获取每个商品的详情
- 放到数据库(京东,淘宝,苏宁)比价系统
第一步:京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!
第二步:手机通讯频道
第三步:京东 - 全部分类
京东 - 全部分类 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
3.获取每一页数据
4.获取详情的链接
【行情 价格 评价 正品行货】-京东
,653,655&page=1
@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.获取商品的链接
@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.抓取所有商品的商品详情信息(标题,卖点,价格,图片,描述)
标题:
@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
@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.用火狐浏览器打开复制截取的链接确认数据类型:
@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);
}
图片
@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);
}
}
描述
@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");
}
}
- 只关注 3 级分类 京东 - 全部分类 进入 【行情 价格 评价 正品行货】-京东 ,653,655
- 获取京东商城的所有的三级页面,商品列表页面都是由三级页面点击而来
- 进入列表第一个页面,获取它的总页数,拼接成所有的访问页的链接
- 访问列表页面就获取到页面上的商品链接
- 一个一个属性抓取 a. 标题,直接抓网页定位
b. 卖点,二次请求,json
c. 价格,二次请求,json
d. 图片,只小图片,集合,直接抓取页面定位 e. 描述,二次请求, jsonp ,去掉函数就是 json 所有的 Json 引入 ObjectMapper 去搞定。 最难最耗时,二次请求和链接分析 反爬虫: A. 通过选择器来筛选,把 class 名称, id 改 B. 二次加载, json ,换个属性名, jsonp 换函数名day03 知识回顾: 系统体验爬虫 爬取京东商城网站商品详情 开发步骤:
- 通过3级分类
- 自己分析
- 获取前人的经验(百度)
- 电影推荐
- 电影海报 quartz 定时器,每天一张海报
- 评分可以写成排行,每月一个电影排行榜
- 评论
- 饼形图
- 3 个,生活中的分类;住宿,旅游,学习
- 5 个,住宿,旅游,学习,吃饭,交女盆友
2. 柱状图
-
3
个,
3
次月考成绩
-
5
个指标,语文
99
,数学
100
,英语
60
,化学
5
,物理
10
3. 曲线图
-
一周早晨到校时间
-
工作日
5
天离校时间
工作中解决问题:
以工程思路去做!
最终项目目标:
需求:
抓取京东、淘宝、苏宁三家电商,某个商品的价格,用
echarts
百度图表软件展现出来,领导一看非常直观,领导来决定是否降价。
关键技术点:
-
抓取京东商城的某个商品的价格?
a. 确定某个商品,(动态抓)
-
如何抓取淘宝、苏宁?
-
如何最终在
echarts
页面上展现出来
隐藏一个问题:怎么判断是同一个商品?
商品有不同规格和颜色,怎么能断定它是同一个呢?
技巧:
思路:(一个,动态)
利用搜索条,假设输入条件足够详细,理论上查询出来的商品信息匹配度越高。可以近似认为它们是等同。
来抓取列表的第一个商品的信息,价格信息,然后把
3
个商城的价格信息
传递给
jsp
页面,形成
echarts
所要的数据结构。
最终形成柱状图。
扩展:(批量,事先抓好)
-
都抓到数据库
-
列出多个字段,
-
做一个判断,京东
-
苏宁
<0
,修改价格,淘宝
-
苏宁
<0
,
京东
- 商品搜索 - 京东
iphonex%2064g
iphonex 64g_iphonex 64g推荐 - 苏宁易购
天猫tmall.com--理想生活上天猫
开发步骤:
-
搜索
url
都有找到,动态拼接上搜索关键字
-
Servlet+
创建
jsp
文件,输入框,用户可以填写关键字(表单)
SearchServlet
,
search.jsp
- 一周早晨到校时间
- 工作日 5 天离校时间
- 抓取京东商城的某个商品的价格? a. 确定某个商品,(动态抓)
- 如何抓取淘宝、苏宁?
- 如何最终在 echarts 页面上展现出来
- 都抓到数据库
- 列出多个字段,
- 做一个判断,京东 - 苏宁 <0 ,修改价格,淘宝 - 苏宁 <0 ,
- 搜索 url 都有找到,动态拼接上搜索关键字
- Servlet+ 创建 jsp 文件,输入框,用户可以填写关键字(表单)
3. 提交submit,转向到另外servlet,利用jsoup分别抓各个商场搜索数据,近似值,抓取第一款商品id
写成 3 个方法 getJDPrice 抓取京东商城某个商品的价格 getTBPrice 抓取淘宝商城某个商品的价格 getSNPrice 抓取苏宁商城某个商品的价格4. 通过商品详情链接,拼接上id,找到商品链接地址
5. 只抓取价格
6. 3个价格都抓取到拼接成满足echarts要的字符串结构,把字符串request传递给统计jsp页面
DoServlet 调用每个方法,获取其价格,然后拼接 jsp 页面所要字符串 写入 request 对象中7. 统计的Jsp页面通过el表达式获取数据,最终以柱状图展现
result.jsp 实际开发中,- 先搭建框架,其中遇到方法,假装! getPrice return 100;
- 每个小细节最终不同的人员完成,团队开发。


