近来,云计算结课要求是要做一个基于Hadoop组件的电商网站数据分析项目;数据分析,数据分析,要先有数据才能分析~数据哪来呢,当然是爬虫。
其中爬虫最热门的语言肯定都是想到Python。但是Java作为我的本命语言,我就尝试了一下Java的爬虫实现,其实,现在Java也有很多框架包对爬虫的支持非常方便。下面,作为爬虫的入门,我用Jsoup来实现一下京东商品数据的采集。
首先,和任何项目前提一样,新建一个项目,配置好依赖。
org.jsoup
jsoup
1.11.3
junit
junit
4.12
compile
上面就先引入jsoup包和测试用的junit包。
在进行爬虫前,要先对目标网站有一定的了解:
我这次是需求是对京东商城某个关键词下的商品数据采集
1、打开京东商城
在首页输入一个关键词搜索,观察url的变化
比如:输入 iphone 搜索
可以尝试一下去掉关键词后面的信息再刷新网页
依然没问题,这时候就可以确定在京东上搜索什么都是由keyword后面的参数决定的。
2、代码实现
确定之后就可以写代码尝试先吧搜索后的一页数据获取到
Jsoup提供的API非常简单,只需要几步就能获取到网页的数据。
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.net.URL;
public class JsoupTest {
public static void main(String[] args) throws IOException {
//要爬取的网址
String url = "https://search.jd.com/Search?keyword=iphone";
//传进一个URL,和响应超时时间,返回一个类似前端Js的DOM对象
Document doc = Jsoup.parse(new URL(url),3000);
//打印DOM对象的html
System.out.println(doc.html());
//打印DOM对象的所有text
System.out.println(doc.text());
}
}
运行代码
第一个doc.html()打印出了网页源代码的所有标签+文本内容
第二个doc.text()只打印出网页的文本内容。
爬虫的第一小步实现了!可以通过代码访问到了网页
3、提取有效数据
上面得到数据肯定是不符合我们的需求的,爬虫最重要的是洗数据
就是只提取有效的数据
比如说:我只需要商品的标题和价格
在网页上右击你想要的数据,检查。
可以确定价格是在p-price的div中,标题是在p-name这个div中。
而每个商品都在J_goodsList这个div里面:
这时候,就可以用到Jsoup强大的解析功能,和js的用法十分相似。
Element goodsList = doc.getElementById("J_goodsList");
//遍历本页所有商品
Elements items = goodsList.getElementsByClass("gl-item");
for (Element item : items) {
//获取商品id
String id = item.attr("data-sku");
//获取商品标题
String pname = item.getElementsByClass("p-name").get(0).getElementsByTag("em").get(0).text();
System.out.println(pname);
//获取商品价格
String price = item.getElementsByClass("p-price").get(0).getElementsByTag("i").get(0).text();
System.out.println(price);
}
前面拿到的都是本页的数据,那么如何获取多页的呢?
在页面中点击下一页,观察url的变化
发现jd的页面变化参数是奇数变化的,也就是第二页page参数是3,第三页是5…知道规律了还不简单,写个循环搞定。
这时候可以优化一下代码,在url中可以吧keyword和page提出来,这样就
传参数查询所需要的数据。
private static void findProductByKey(String keyword, Integer page) throws IOException {
for (Integer integer = 1; integer <= page * 2 - 1; integer += 2) {
String url = "https://search.jd.com/Search?keyword=" + keyword + "&page=" + integer;
Document doc = Jsoup.parse(new URL(url), 5000);
Element goodsList = doc.getElementById("J_goodsList");
//遍历本页所有商品
Elements items = goodsList.getElementsByClass("gl-item");
for (Element item : items) {
//获取商品标题
String pname = item.getElementsByClass("p-name").get(0).getElementsByTag("em").get(0).text();
System.out.println(pname);
//获取商品价格
String price = item.getElementsByClass("p-price").get(0).getElementsByTag("i").get(0).text();
System.out.println(price);
}
}
}
5、获取动态加载的数据
现在很多网站为了加载速度,很多页面上的东西都是通过js或者ajax动态加载出来的,页面的源代码并没有这些数据,而Jsoup获得的DOM对象只能是静态的源码。
比如说我想获取一个商品的评价。
虽然在页面的检查中可以看到有数据,但是这些都是动态加载出来的,
如果你在网页上查看整个页面源代码就发现找不到这些数据。这种情况就不能单纯靠Jsoup获取了。
打开控制台的network,刷新页面,观察数据包
这时候发现productPageComments这个包中有商品评价的数据
观察这个数据包的请求头信息
复制请求头的url,这个才是我们需要解析的地址,而且观察多个商品,发现改地址的变化规律只和商品的id和page参数有关,所以我们也可以利用这个拼接字符串循环拿到多条数据。
/**
* @param id 商品ID
* @param page 要拿多少页评价
* @throws IOException
*/
private static void getAppraise(String id, int page) throws IOException {
for (int i = 0; i < page; i++) {
String url = "https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98&productId="
+ id + "&score=0&sortType=5&page=" + i + "&pageSize=10&isShadowSku=0&fold=1";
Document parse = Jsoup.parse(new URL(url), 3000);
System.out.println(parse.html());
}
}
}
到这里离成功还差一点,因为上面请求得到不是我们的html格式的,而是一串Json格式的字符串
这个问题简单,将Json解析就好了。
我这里采用fastjson进行解析。
首先先添加fastjson的依赖。
com.alibaba
fastjson
1.2.66
要去掉前面的“fetchJSON_comment98(” 和后面的“);”才是符合格式的json数据,可以用字符串工具进行字符串截取。
//截取正确格式的json字符串,进行json解析,转为json对象
JSONObject jsonObject = JSON.parseObject(parse.text().substring(parse.text().indexOf("(") + 1, parse.text().lastIndexOf(");")));
//获取评论元素,循环输出
JSONArray comments = jsonObject.getJSONArray("comments");
for (Object comment : comments) {
JSONObject parse1 = (JSONObject) comment;
System.out.println(parse1.get("content").toString());
System.out.println("-------------------");
}