京东等电商爬虫问题与总结(二)
- 京东
这一段时间比较忙,更新也不及时。。。下面将总结一下在写京东爬虫的时候,遇到的一些问题。
京东的页面,列表页的数据基本包含了所有的信息了,详情页里面也没有什么有价值的信息了。所以我的数据都是取自于列表页。并且京东的数据经过我多次测试(主要是公司很多运营同学在使用。。。),他是不反爬的。
这样听起来是不是觉得很简单?但是这里面还是有一个坑的,京东列表页数据,如果你什么操作都不做,单纯http请求数据,那么只能拿到一半的数据(京东列表页有60条商品数据,只能拿到30条商品数据),因为他是动态加载的,需要模拟人的滑动操作,滚动到页面底端。这里我使用的是phantomjs来模拟人的滚动操作。还有一点需要注意的是,使用phantomjs滚到到底端获取到数据之后,他获取到的是为滑动前30条商品数据+滑动后60条数据,共90条数据,我们需要的是后60条数据,所以这个在页面解析的时候需要注意一下。
因为我使用的是webmagic这个框架,所以页面下载、解析这些继承的他的类,进一步做的处理。
private void processListPage(Page page, String spiderUrl) {
List<GoodsInfo> goodsInfoList = new ArrayList<>();
// 下一页
Integer totalPage = Integer.valueOf(page.getHtml().$(JD_TOTAL_PAGE, "text").toString());
if ((pageNum++) < totalPage) {
Integer currentPage = Integer.valueOf(page.getHtml().$(JD_CURRENT_PAGE, "text").toString());
String[] urls = spiderUrl.split("&page=");
String nextPageUrl = urls[0] + "&page=" + (currentPage * 2 + 1);
page.addTargetRequest(nextPageUrl);
logger.info("nextpage is ==>{}", nextPageUrl);
}
// 列表详情
Document document = Jsoup.parse(page.getHtml().get());
Elements elements = document.select("#J_goodsList > ul");
if (elements.size() > 0) {
Elements element = elements.get(elements.size() - 1).select("li");
logger.info("element size is ==>{}", element.size());
element.forEach(element1 -> getParsedPageInfo(goodsInfoList, element1));
logger.info("the goodsCount is ==>{}", goodsCount);
page.putField("goodsInfo", goodsInfoList);
}
}
下面是具体的页面解析。
private void getParsedPageInfo(List<GoodsInfo> goodsInfoList, Element element1) {
try {
String goodsId = element1.select("li").attr("data-sku");
String goodsTitle = null;
try{
goodsTitle = element1.select("div").get(4).select("em").get(0).text();
}catch (Exception e){
goodsTitle = element1.select("div.gl-i-wrap > div").select("em").text().replaceAll("¥","");
}
String goodsLink = "http://item.jd.com/" + goodsId + ".html";
String goodsPrice = element1.select("strong").text();
String imgLink = element1.select("a > img").get(0).attr("src");
String storeName = element1.select("span.J_im_icon").text();
String storeLink = element1.select("span.J_im_icon > a").get(0).attr("href");
GoodsInfo goodsInfo = new GoodsInfo();
goodsInfo.setGoodsId(goodsId);
if (StringUtils.isBlank(imgLink)) {
imgLink = element1.select("a > img").get(0).attr("data-lazy-img");
}
if (StringUtils.isNotBlank(goodsLink)&&!goodsLink.startsWith("http")) {
goodsLink = "http:" + goodsLink;
}
if (StringUtils.isNotBlank(imgLink)&&!imgLink.startsWith("http")) {
imgLink = "http:" + imgLink;
}
if (StringUtils.isNotBlank(storeLink)&&!storeLink.startsWith("http")) {
storeLink = "http:" + storeLink;
}
goodsInfo.setGoodsLink(goodsLink);
goodsPrice = goodsPrice.split(" ")[0].replace("¥", "");
goodsInfo.setGoodsCurrentPrice(goodsPrice);
goodsInfo.setGoodsCalcPrice(Double.parseDouble(goodsPrice));
goodsInfo.setGoodsTitle(goodsTitle);
goodsInfo.setShopName(storeName);
goodsInfo.setShopKeeper(storeName);
goodsInfo.setGoodsImages(imgLink);
goodsInfo.setShopLink(storeLink);
String shopId = storeLink.replace("http://mall.jd.com/index-", "").replace(".html", "").trim();
//店铺名称 商品id 店铺链接 商品标题 商品链接 商品图片链接 商品列表价格 平台id 平台名称 总页数 搜索关键词
logger.info("\n商品id==>{}\n商品链接==>{}\n商品列表价格==>{}\n商品名称==>{}\n图片链接==>{}\n店铺ID==>{}\n店铺名称==>{}\n店铺链接==>{}\n任务ID==>{}\n",
goodsId, goodsLink, goodsPrice, goodsTitle, imgLink, shopId, storeName, storeLink, crawlerRule.getTaskNo());
goodsInfo.setShopId(shopId);
goodsInfo.setPlatformId(crawlerRule.getSpiderPlatformId());
goodsInfo.setTaskNo(crawlerRule.getTaskNo());
goodsInfo.setRuleNo(crawlerRule.getRuleNo());
goodsInfo.setCrawlerTaskId(crawlerRule.getCrawlerTaskId());
goodsInfo.setCrawlerRuleId(crawlerRule.getId());
goodsInfo.setCreateBy(crawlerRule.getCreateBy());
goodsInfo.setCreateDate(crawlerRule.getCreateDate());
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyyMMdd");
String crawlerVersion = format.format(date);
goodsInfo.setCrawlerVersion(Integer.parseInt(crawlerVersion));
goodsInfoList.add(goodsInfo);
} catch (Exception e) {
logger.info("parse failed ==>{}", e);
}
}
下载器呢,使用的是webmagic自带的PhantomjsDownloader,源代码里面,写了可以调用js来达到滑动的效果,并且源代码也给出了一个例子,我基于这个例子,修改了那段js,达到了我需要的效果(如果要使用,要记得安装phantomjs呀),下面是这段js:
var system = require('system');
var url = system.args[1];
var page = require('webpage').create();
page.settings.loadImages = false;
page.settings.resourceTimeout = 5000;
var vWidth = 1080;
var vHeight = 1920;
page.viewportSize = {
width: vWidth ,
height: vHeight
};
page.open(url, function (status) {
if (status != 'success') {
console.log("HTTP request failed!");
phantom.exit();
} else {
console.log(page.content);
setTimeout((function() {
page.evaluate(function() {
var pos, scroll;
pos = 0;
scroll = function() {
pos += 250;
window.document.body.scrollTop = pos;
return setTimeout(scroll, 100);
};
return scroll();
});
return setTimeout((function() {
console.log(page.content);
return phantom.exit();
}), 5000);
}), 1000);
}
});
PhantomjsDownloader调用的源代码我也列一下,其实完全可以自己编写
/**
* 新增构造函数,支持crawl.js路径自定义,因为当其他项目依赖此jar包时,runtime.exec()执行phantomjs命令时无使用法jar包中的crawl.js
* <pre>
* crawl.js start --
*
* var system = require('system');
* var url = system.args[1];
*
* var page = require('webpage').create();
* page.settings.loadImages = false;
* page.settings.resourceTimeout = 5000;
*
* page.open(url, function (status) {
* if (status != 'success') {
* console.log("HTTP request failed!");
* } else {
* console.log(page.content);
* }
*
* page.close();
* phantom.exit();
* });
*
* -- crawl.js end
* </pre>
* 具体项目时可以将以上js代码复制下来使用
*
* example:
* new PhantomJSDownloader("/your/path/phantomjs", "/your/path/crawl.js");
*
* @param phantomJsCommand phantomJsCommand
* @param crawlJsPath crawlJsPath
*/
public PhantomJSDownloader(String phantomJsCommand, String crawlJsPath) {
PhantomJSDownloader.phantomJsCommand = phantomJsCommand;
PhantomJSDownloader.crawlJsPath = crawlJsPath;
}
/** 中间还有一些框架自带的方法我省略了。。。。大家看一下最主要的方法,这段没有注释,我 解释一下
* 这个Request类是对URL地址的一层封装,可从中取出下载的url
* phantomJsCommand 这个phantomjs安装的路径
* crawlJsPath js文件的路径
*/
protected String getPage(Request request) {
try {
String url = request.getUrl();
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(phantomJsCommand + " " + crawlJsPath + " " + url);
InputStream is = process.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuffer stringBuffer = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
stringBuffer.append(line).append("\n");
}
return stringBuffer.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}