您的位置:首页 > 业界新闻

大数据互联网架构阶段 Java爬虫

2018-02-02 23:12 525 查看

Java爬虫

一 、 爬虫简介

http://www.lete.com , 乐贷网其实就是爬虫的简单应用 ,发送一个商品连接 , 获取商品信息

目标

爬取京东所有商品的信息

封装在自己的Item实体类中

分析:

京东允许爬虫爬取数据么?

京东是允许爬虫的 , 没有反爬虫技术

爬虫产品:

httpClient :但是httpClient抓取的是整个页面 , 整夜字符串的处理、解析比较繁琐 , 数据的定位非常不准确 。

htmlUnit : 也获取整个页面 , 抓取页面也可以包含二次提交 , 数据定位也比较准确 , 但是爬取过程不稳定 , 在爬取过程中 需要断点续爬代码的编写 。

jsoup: 是一款比较稳定 , 定位准确 , 包含二次提交的java爬虫技术 。

python也可以做爬虫 , 使用beautifulSoup技术 ,底层原理与jsoup是一样的 。 只是语言不同。

jsoup

抓取整个页面

抓取整个网站(以京东为列 , 抓取从首页能获取所有的连接地址)

抓取页面中某一个定位的数据

抓取二次提交ajax(如 : price)

抓取其他的jsonp数据 (如: 商品描述)

以上五种问题 , 如果都能解决 ,那么使用jsoup爬取任何网站都是可行的 。

案例

整个页面

与httpclient无异

/**
* 爬取网页
* @throws IOException
* */
@Test
public void testt_01() throws IOException {
String url = "http://www.jd.com";
Connection connect = Jsoup.connect(url);
Response execute = connect.execute();
System.out.println(execute.body());
}


整个网站

抓取绝大部分的连接地址

观察网站的连接大部分都是使用的a标签 , 连接在href中

使用jsoup定位a标签 , 获取所有a标签 , 然后获取href的值

/**
* 爬取整个网站
* @throws IOException
* */
@Test
public void test_02() throws IOException {
String url = "http://www.jd.com";
Document document = Jsoup.connect(url).get();
//寻找a标签
Elements elementsByTag = document.getElementsByTag("a");
for(Element element :elementsByTag) {
String href = element.attr("href");
String val = element.val();
System.out.println("连接地址:"+href + "---"+val);
}
}


定位信息

/**
* 爬取一个网页中的信息
* 定位具体标签中的数据
* @throws IOException
* */
@Test
public void test_03() throws IOException {
String url= "http://item.jd.com/4329035.html";

//get请求 获取的是返回结构的document树
//excute获取的是返回的所有数据
Document doc = Jsoup.connect(url).get();
//选择器与jQ中的选择器使用一致
//为了 定位准确 , 使用父子选择器 , 确定唯一的定位
Element select = doc.select("ul li .p-img a").get(0);
System.out.println(select.attr("href"));
}


json二次提交获取信息

需要自己 寻找页面中发起 ajax的请求地址

/**
* 抓取二次提交
* 商品价格是页面加载之后又通过ajax获取的
* @throws IOException
* */
@Test
public void test_04() throws IOException {
String url = "http://p.3.cn/prices/mgets?skuIds=J_5089253";
Response response = Jsoup.connect(url).ignoreContentType(true).execute();
String  json = response.body();
System.out.println(json);
ObjectMapper mp = new ObjectMapper();
JsonNode jn = mp.readTree(json);
//[{"op":"8388.00","m":"9999.00","id":"J_5089253","p":"8388.00"}]
//直接获取到的是数组  ,需要获取到第一个元素
String price = jn.get(0).get("p").asText();
System.out.println(price);
}


jsonp数据

/**
* 获取jsonp请求数据
* @throws IOException
* */
@Test
public void test_05() throws IOException {
String url = "http://d.3.cn/desc/4329035";
String jsonDesc = Jsoup.connect(url).ignoreContentType(true).execute().body();
System.out.println(jsonDesc);
String data = jsonDesc.substring(jsonDesc.indexOf("(")+1, jsonDesc.lastIndexOf(")"));
System.out.println(data);
ObjectMapper mp = new ObjectMapper();
JsonNode jn = mp.readTree(data);
String  desc = jn.get("date").asText();
System.out.println(desc);

}


爬取京东商品信息

/**
* 爬取京东商品的所有商品信息
* @author outman
* 2018 - 1 - 31 - 17:48
* 步骤:
* 1. 先获取所有的商品三级分类链接
* 2. 访问商品分类链接后获取一个分类下所有商品的链接(可能存在分页的情况)
* 3. 访问商品链接后获取商品信息
*
* 过程中要十分注意异常的处理
* 在爬取过程中一旦出现异常 , 后续的过程也将受到影响 , 导致整个数据错乱
* */
public class JDCrawler {
private static SqlSession session ;
static {
//获取一个数据流
InputStream in;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
//创建一个工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//创建一个会话
session = factory.openSession(true);//true表示自动提交 , 默认为false , 需要手动提交
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 入口函数
* @throws Exception
* */
public static void main (String[] args) throws Exception {
//测试
//http://www.jd.com/allSort.aspx  商品 分类页面
//      getItemCatUrls("http://www.jd.com/allSort.aspx");
//list.jd.com/list.html?cat=12379,13302,13313 某一分类下的商品展示页面
//      getItemsPageUrls("http://list.jd.com/list.html?cat=12379,13302,13313");
//http://list.jd.com/list.html?cat=12379,13302,13313&page=2  商品展示页面
//      getItemUrls("http://list.jd.com/list.html?cat=12379,13302,13313&page=2");
//item.jd.com/12017077901.html  商品信息页面
//      getItem("http://item.jd.com/12017077901.html");
// 12017077901某一个商品的ID
//      getPrice(new Long("12017077901"));

//完整测试
List<String> itemCatUrls = getItemCatUrls("http://www.jd.com/allSort.aspx");
for(String itemCaturl :itemCatUrls) {
System.out.println("商品分类链接:"+itemCaturl);
List<String> itemsPageUrls = getItemsPageUrls(itemCaturl);
for(String itemsPageUrl : itemsPageUrls) {
System.out.println("商品展示页面链接:"+itemsPageUrl);
List<String> itemUrls = getItemUrls(itemsPageUrl);
for(String itemUrl : itemUrls) {
System.out.println("商品链接:"+itemUrls);
Item item = getItem(itemUrl);
saveItem(item);
System.out.println(item);
}
}
}
}
/**
* 获取京东商品的所有分类链接
* @throws Exception
* */
public static List<String> getItemCatUrls(String url) throws Exception{
//记录数据数量
Integer hrefPreNum  = 0  ;
List<String> itemCatUrls =  new ArrayList<String>();
//这里选择抛出异常 , 这里如果抛出异常 , 说明url有问题  , 或者网络有问题 , 后续的操作没有任何意义
Document doc = Jsoup
4000
.connect(url).get();
Elements eles = doc.select("dl dd a");
for(Element ele : eles) {
String href = ele.attr("href");
hrefPreNum += 1;
if(href.startsWith("//list.jd.com/")) {
itemCatUrls.add("http:"+href);
//              System.out.println(href);
}
}
System.out.println("获取到的总三级分类链接量:"+hrefPreNum);
System.out.println("数据清洗后的数量:"+itemCatUrls.size());

return itemCatUrls;
}
/**
* 获取三级分类下所有商品页面的链接
* 商品展示可能存在分页的情况
* 所以在获取所有的商品链接之前需要先获取 所有的商品分类页
* */
public static List<String> getItemsPageUrls(String url){
List<String> itemsPages = new ArrayList<String>();
//从商品展示页面获取分页信息
String num;
try {//抛出异常 , 如果 出现异常则继续执行 , 丢失一点信息是正常的
num = Jsoup.connect(url).get().select("#J_topPage span i").get(0).text();
Long numL = new  Long(num);
for(int i = 1 ; i<=numL ; i++) {
String pageUrl = url+"&page="+i;
//              System.out.println(pageUrl);
itemsPages.add(pageUrl);
}
} catch (Exception e) {
e.printStackTrace();
}
return itemsPages;
}
/**
* 获取每个商品分类页面的商品链接
* */
public static List<String> getItemUrls(String url){
List<String> itemUrls = new ArrayList<String>();
try {
Elements eles = Jsoup.connect(url).get().select(" li div .p-img a");
for(Element ele : eles) {
String itemUrl = ele.attr("href");
itemUrls.add("http:"+itemUrl);
}
} catch (Exception e) {
System.out.println("获取商品展示页面的商品链接出错:"+url);
}
return itemUrls;
}
/**
* 访问商品链接 , 获取商品数据
* */
public static Item getItem (String url) {
Item item = new Item();
Long id = null;
try {
Document doc = Jsoup.connect(url).get();
//获取id   //item.jd.com/12016709876.html
id = new Long(url.substring(url.lastIndexOf("/")+1, url.indexOf(".html")));
//获取title
String title = doc.select("#name h1").get(0).text();
//获取卖点  获取到的 值为"" 说明页面时是通过ajax方式请求  需要json格式的数据
//          String sellPoint = doc.select("#p-ad").get(0).text();
String sellPoint = getSellPoint(id);
//获取价格  价格是通过ajax二次请求的
//          Long price = new Long(doc.select(".dd .p-price .price").get(0).text());
Long price = getPrice(id);
//获取图片
//          String img = doc.select("#spec-n1 img").attr("src");
String img = getImg(url);
//          System.out.println(img);
//获取商品详情
//          String desc = doc.select("J-detail-content").get(0).text();
String desc = getDesc(id);
//封装属性
item.setId(id);
item.setTitle(title);
item.setSellPoint(sellPoint);
item.setPrice(price);
item.setImg(img);
item.setDesc(desc);
System.out.println(item);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("获取商品信息失败");
}
return item;
}
/**
* 爬取卖点
* 由于商品价格是页面加载完成之后 , 有通过ajax获取的 , 所以单独爬取json格式的数据
* 通过页面分析 得到卖点的url
* http://ad.3.cn/ads/mgets?skuids=AD_ +12017077901
* */
public static String getSellPoint(Long id) {

String sellPoint = null;
try {
Response resp = Jsoup.connect("http://ad.3.cn/ads/mgets?skuids=AD_"+id).ignoreContentType(true).execute();
ObjectMapper mapper = new ObjectMapper();
sellPoint = mapper.readTree(resp.body()).get(0).get("ad").asText();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("获取卖点失败");
}
return sellPoint;
}
/**
* 爬取商品价格
* 由于商品价格是页面加载完成之后 , 有通过ajax获取的 , 所以单独爬取
* 通过页面分析 得到商品价格的链接 //p.3.cn/prices/get?skuid=id
* */
public static Long getPrice(Long id) {
Long price  = null;
try {
Response resp = Jsoup.connect("http://p.3.cn/prices/get?skuid="+id).ignoreContentType(true).execute();
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(resp.body()).get(0);
price = jsonNode.get("m").asLong();
//          System.out.println(price);
} catch (Exception e) {
System.out.println("获取价格失败");
}
return price;
}
/**
* 获取商品图片
* 通过分析页面 , 得到图片的请求地址
* */
public static String getImg(String url) {
String img = "";
Document doc;
try {
doc = Jsoup.connect(url).get();
//获取页面大图的地址
String bigsrc = doc.select("#spec-n1 img").attr("src");
//          System.out.println("大图地址:"+bigsrc);
//获取小图地址
Elements smallsrcs = doc.select("#spec-list div ul li img");
for(Element ele : smallsrcs) {
String src = ele.attr("src");
//              System.out.println("小图地址:"+src);
//将小图地址替换成大图
String newSrc = src.replace("n5", "n1");
img+=newSrc+";";
//              System.out.println(newSrc);
}
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("获取图片失败");
}
img = img.substring(0 , img.length()-1);
return img;
}
/**
* 爬取商品详情
* 商品详情是页面加载完成之后 , 通过jsonp获取的 , 需要单独获取
* http://dx.3.cn/desc/10316672107 * */
public static String getDesc(Long id) {
String desc  = null;
try {
Response resp = Jsoup.connect("http://dx.3.cn/desc/"+id).ignoreContentType(true).execute();
ObjectMapper mapper = new ObjectMapper();
String body = resp.body();
body = body.substring(body.indexOf("(")+1, body.lastIndexOf(")"));
desc = mapper.readTree(body).get("content").asText();

} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("获取不到"+id+"的商品描述");
}
return desc;

}

/**
* 数据入库
* */
public static void saveItem(Item item) {
session.insert("ItemMapper.saveItem" , item);
}
}


爬虫的注意事项

网络不稳定 , 最好使用完整的严谨 的逻辑(断点续爬)

爬虫代码量不大(逻辑种类不多) , 最重要 的是页面结构的分析

网站改版导致爬虫的代码更新 。

反爬虫技术

频繁修改样式关键字(最简单的反爬虫机制)

nginx就可以反爬虫 (使用nginx黑名单)

jsoup的连接 请求头和浏览器请求头不一样

jsoup可以用代码模拟请求头—伪装请求头 参考: http://jilongliang.iteye.com/blog/2048459

查看访问频率 , 如果频率过高 , 则封ip一段时间

问题

数据是会每天更新或添加的 ,怎样在原有的基础上爬取最新的数据
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: