文档章节

教您使用DynamicGecco抓取JD全部商品信息

xtuhcy
 xtuhcy
发布于 2016/07/18 11:22
字数 2093
阅读 525
收藏 1
点赞 2
评论 0

##关于gecco爬虫框架 如果对gecco还没有了解可以参看一下gecco的github首页。gecco爬虫十分的简单易用。之前有一篇文章《教您使用java爬虫gecco抓取JD商品信息》,使用的是传统的注解方式,建议看这篇文章前先了解之前的文章。这里介绍一下DynamicGecco方式,比之前更简单,抓取全部京东商品只要3个类就能搞定了。 ##什么是DynamicGecco DynamicGecco的目的是在不定义SpiderBean的情况下实现爬取规则的运行时配置。其实现原理是采用字节码编程,动态生成SpiderBean,而且通过自定义的GeccoClassLoader实现了抓取规则的热部署。通常我们可以利用DynamicGecco实现下面这些特性:

  • 已经定义了ORM(如:hiberante)的bean,将注解动态的加载到ORM的bean中,可以很方便的将页面格式化后入库
  • 很多类似的网站的抓取,SpiderBean都一样,只是提取元素的cssPath不一样,为了不构建很多重复的SpiderBean,可以考虑动态生成SpiderBean
  • 通过配置的方式抓取页面,通过后台管理系统、配置文件等配置抓取规则,动态的将配置规则转换成SpiderBean
  • 利用动态SpiderBean可以构建可视化爬虫,利用可视化工具构建抓取规则,将规则动态转换为SpiderBean

##规则定义 爬虫的抓取规则,如matchUrl、csspath、ajax等不需要再使用注解方式注入SpiderBean中,利用DynamicGecco直接定义。下面是抓取全部JD商品的规则定义:

public static void main(String[] args) {
	
	//对应原来的Category和HrefBean类
	Class<?> category = DynamicGecco.html()
	.stringField("parentName").csspath("dt a").text().build()
	.listField("categorys", 
			DynamicGecco.html()
			.stringField("url").csspath("a").href().build()
			.stringField("title").csspath("a").text().build()
			.register()).csspath("dd a").build()
	.register();
	
	//对应原来的AllSort类
	DynamicGecco.html()
	.gecco("http://www.jd.com/allSort.aspx", "consolePipeline", "allSortJsonPipeline")
	.requestField("request").request().build()
	.listField("mobile", category)
			.csspath(".category-items > div:nth-child(1) > div:nth-child(2) > div.mc > div.items > dl").build()
	.register();
	
	//对应ProductBrief类
	Class<?> productBrief = DynamicGecco.html()
	.stringField("code").csspath(".j-sku-item").attr("data-sku").build()
	.stringField("title").csspath(".p-name> a > em").text().build()
	.stringField("preview").csspath(".p-img > a > img").image("", "data-lazy-img", "src").build()
	.stringField("detailUrl").csspath(".p-name > a").href(true).build()
	.register();
	
	//对应ProductList类
	DynamicGecco.html()
	.gecco("http://list.jd.com/list.html?cat={cat}&delivery={delivery}&page={page}&JL={JL}&go=0", "consolePipeline", "productListJsonPipeline")
	.requestField("request").request().build()
	.intField("currPage").csspath("#J_topPage > span > b").text().build()
	.intField("totalPage").csspath("#J_topPage > span > i").text().build()
	.listField("details", productBrief).csspath("#plist .gl-item").build()
	.register();
	
	//对应ProductDetail类
	DynamicGecco.html()
	.gecco("http://item.jd.com/{code}.html", "consolePipeline")
	.stringField("code").requestParameter().build()
	.stringField("title").csspath("#name > h1").text().build()
	.stringField("detail").csspath("#product-detail-2").build()
	.stringField("image").csspath("#spec-n1 img").image("d:/gecco/jd/img").build()
	.field("price", FieldType.type(JDPrice.class)).ajax("http://p.3.cn/prices/get?type=1&pdtk=&pdbp=0&skuid=J_{code}").build()
	.field("jdAd", FieldType.type(JDad.class)).ajax("http://cd.jd.com/promotion/v2?skuId={code}&area=1_2805_2855_0&cat=737%2C794%2C798").build()
	.register();
	
	HttpGetRequest start = new HttpGetRequest("http://www.jd.com/allSort.aspx");
	start.setCharset("GBK");
	GeccoEngine.create()
	.classpath("com.geccocrawler.gecco.demo.jd")
	.start(start)
	.interval(2000)
	.run();

}

规则定义后,启动GeccoEngine即可,和之前没有两样,可以看出来,之前的例子定义了7个Bean,但是这里只需要一个类就都搞定了。

##语法讲解

  • html():定义一个html页面爬虫,对应原来HtmlBean接口。对已经存在的类可以指定参数html(className),如果不定义系统会自动生成类名。
  • json():定义一个json数据的爬虫,对应原来JsonBean接口。对已经存在的类可以指定参数json(className),如果不定义系统会自动生成类名。
  • gecco(matchUrl, pipelines...):定义url匹配模式和pipeline处理器。对应原来@Gecco注解
  • existField(fileName):定义一个存在的属性,用来承载抽取出来的内容,如果属性不存在,不会创建新的属性
  • stringField(fileName):定义一个属性,用来承载抽取出来的内容,如果属性已经存在则复用,如果不存在会生成一个新的属性。目前支持的原始类型属性包括:intField, floatField, longField, doubleField,shortField
  • requestField(fileName):对应HttpRequest类型的属性
  • listField(fileName,memberClass):对应一个list类型属性,memberClass表示List里元素的数据类型
  • field(fileName,memberClass):定义memberClass类型的属性,这是一个比较灵活的定义属性的方式,可以任意指定属性类型
  • csspath(csspath):jsoup的抽取元素规则,对应原来@HtmlField注解
  • text():对应原来@Text注解
  • href():对应原来@Href注解
  • image():对应原来@Image注解
  • attr():对应原来@Attr注解
  • ajax():对应原来@Ajax注解
  • jsonpath():对应原来@JSONPath注解
  • build():构建属性抽取规则
  • loadClass():构建整个Bean,使用GeccoClassLoader加载进JVM
  • register():完成loadClass()并且注册到GeccoEngine(GeccoEngine必须后启动才有效)

##JsonPipeline Pipeline的写法也和之前有所区别,由于是运行时生成的Bean,不能像以前那样直接使用定义的Bean,Gecco会将所有Bean都转换为JSONObject,通过json操作来获取抓取来的信息。下面是DynamicJD定义的两个Pipeline:

类别处理Pipeline,对应原来的AllSortPipeline

@PipelineName("allSortJsonPipeline")
public class AllSortJsonPipeline extends JsonPipeline {
	
	public static List<HttpRequest> sortRequests = new ArrayList<HttpRequest>();

	@Override
	public void process(JSONObject allSort) {
		HttpRequest currRequest = HttpGetRequest.fromJson(allSort.getJSONObject("request"));
		JSONArray categorys = allSort.getJSONArray("mobile");
		process(currRequest, categorys);
	}
	
	private void process(HttpRequest currRequest, JSONArray categorys) {
		if(categorys == null) {
			return;
		}
		for(int i = 0; i < categorys.size(); i++) {
			JSONObject category = categorys.getJSONObject(i);
			JSONArray hrefs = category.getJSONArray("categorys");
			for(int j = 0; j < hrefs.size(); j++) {
				String url = hrefs.getJSONObject(j).getString("url")+"&delivery=1&page=1&JL=4_10_0&go=0";
				SchedulerContext.into(currRequest.subRequest(url));
			}
		}
	}

}

产品列表处理Pipeline,对应原来的ProductListPipeline

@PipelineName("productListPipeline")
public class ProductListPipeline implements Pipeline<ProductList> {

	@Override
	public void process(ProductList productList) {
		HttpRequest currRequest = productList.getRequest();
		//下一页继续抓取
		int currPage = productList.getCurrPage();
		int nextPage = currPage + 1;
		int totalPage = productList.getTotalPage();
		if(nextPage <= totalPage) {
			String nextUrl = "";
			String currUrl = currRequest.getUrl();
			if(currUrl.indexOf("page=") != -1) {
				nextUrl = StringUtils.replaceOnce(currUrl, "page=" + currPage, "page=" + nextPage);
			} else {
				nextUrl = currUrl + "&" + "page=" + nextPage;
			}
			SchedulerContext.into(currRequest.subRequest(nextUrl));
		}
	}

}

以上三个类就完成了JD全部商品的抓取,是不是足够简单了。那注解方式还有必要用吗?当然还是有必要的,你会发现,DynamicGecco虽然足够简单,但是他的可理解性、可读性还是没有注解方式好,对于Gecco框架的新手我还是建议先从注解方式开始。

##动态增加修改规则 DynamicGecco通过自定义的GeccoClassLoader实现了规则的热部署,这个是个很有用的功能,你可以想象,假如你有一个管理后台,通过配置就能实现爬虫规则的定义,写爬虫不需要再开发程序,直接配置一下就可以了,如果管理系统做的足够强大,你甚至可以做成可视化的方式,csspath都不需要自己写了。这里还是以最简单的MyGithub为例讲解动态增加修改规则。

###启动爬虫引擎 动态增加修改规则,意味着你可以在没有规则的情况下先启动爬虫引擎。规则可以在你定义好后再加入爬虫引擎。

//初始化爬虫引擎,此时由于没有初始请求,爬虫引擎会阻塞初始队列,直到获取到初始请求
GeccoEngine ge = GeccoEngine.create("com.geccocrawler.gecco.demo.dynamic")	
	.interval(5000)
	.loop(true)
	.engineStart();

###定义爬取规则 爬虫规则的定义和之前讲的基本一致,唯一不同的是register()改成loadClass()。loadClass()用于先启动爬虫引擎后定义规则的场景

//定义爬取规则
Class<?> rule1 = DynamicGecco
	.html()
	.gecco("https://github.com/xtuhcy/gecco", "consolePipeline")
	.stringField("title").csspath(".repository-meta-content").text(false).build()
	.intField("star").csspath(".pagehead-actions li:nth-child(2) .social-count").text(false).build()
	.intField("fork").csspath(".pagehead-actions li:nth-child(3) .social-count").text().build()
	.loadClass();

###注册新的规则 犹豫规则定好后并没有注册,通过下面的方法注册规则:

//注册规则
ge.register(rule1);

###初始请求 加入初始请求队列后,爬虫就开始工作了

//加入初始请求,爬虫引擎开始工作
ge.getScheduler().into(new HttpGetRequest("https://github.com/xtuhcy/gecco"));

###更新规则 如果这时我们希望更新一下抓取规则,比如不想抓star了,我们可以这样更新:

try {
	//开始更新规则
	ge.beginUpdateRule();
	//修改规则
	Class<?> newRule = DynamicGecco
	.html(rule1.getName())
	.gecco("https://github.com/xtuhcy/gecco", "consolePipeline")
	.intField("fork").csspath(".pagehead-actions li:nth-child(3) .social-count").text().build()
	.removeField("star")
	.loadClass();
	//注册新规则
	ge.register(newRule);
} catch(Exception ex) {
	ex.printStackTrace();
} finally {
	//规则更新完毕
	ge.endUpdateRule();
}

###下线已有规则 已经定义好的规则,我们可以将其下线,方法如下:

try {
	//开始更新规则
	ge.beginUpdateRule();
	//下线之前的规则
	ge.unregister(rule);
} catch(Exception ex) {
	ex.printStackTrace();
} finally {
	//规则更新完毕
	ge.endUpdateRule();
}

到此,爬虫规则的增加/修改/删除都已经实现。可以愉快的配置爬虫规则了!

完整的的Demo代码可以参考github上的源代码,位于com.geccocrawler.gecco.demo.dynamic包下。

© 著作权归作者所有

共有 人打赏支持
xtuhcy

xtuhcy

粉丝 103
博文 16
码字总数 18277
作品 1
北京
后端工程师
Gecco 1.2.2 发布,首个支持动态加载的java爬虫框架

Gecco 1.2.2 发布了,DynamicGecco实现了爬虫规则的热部署,是首个支持动态加载的java爬虫框架。 DynamicGecco的目的是在不定义SpiderBean的情况下实现爬取规则的运行时配置。其实现原理是采...

xtuhcy
2016/07/18
1K
4
Gecco 1.2.3 发布,易用的轻量化爬虫

Gecco 1.2.3 发布了,DynamicGecco 实现了爬虫规则的热部署,是首个支持动态加载的java爬虫框架。 DynamicGecco 的目的是在不定义 SpiderBean 的情况下实现爬取规则的运行时配置。其实现原理...

xtuhcy
2016/08/19
1K
1
Gecco 1.2.4 发布,易用的轻量化爬虫

Gecco 1.2.4 发布了,DynamicGecco 实现了爬虫规则的热部署,是首个支持动态加载的java爬虫框架。 DynamicGecco 的目的是在不定义 SpiderBean 的情况下实现爬取规则的运行时配置。其实现原理...

xtuhcy
2016/09/12
1K
5
Gecco 1.2.5 发布,易用的轻量化爬虫

Gecco 1.2.5 发布了,DynamicGecco 实现了爬虫规则的热部署,是首个支持动态加载的java爬虫框架。 更新内容: @Gecco注解支持多个matchUrl,一个spiderbean可以同时匹配多种形式的url jd地址...

xtuhcy
2016/11/03
2.1K
5
gecco 1.0.5 发布,轻量化网络爬虫

gecco 1.0.5 发布了,gecco 是易用的轻量化网络爬虫。 主要改进: 1、修复redirect的相对url的bug 2、增加了支持htmlunit的插件 3、增加抓取JD全部商品的demo 下载地址: Source code (zip) ...

oschina
2016/03/07
3.4K
3
关于怎样获取JD等购物网站的价格信息

我在做毕业设计的时候,遇到个问题,抓取网页的信息,但是JD的价格信息做过防爬处理所以我抓取不下来,有没有大神帮个忙,教教我怎么写代码,或者直接给我发代码,我使用java写的,急!先谢过...

只是漫延
2014/06/02
892
4
手把手教你写电商爬虫-第五课 京东商品评论爬虫 一起来对付反爬虫

系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取 手把手教...

游牧民族
2016/05/16
204
0
不用写代码,30秒抓取李笑来所有微博

不知道大家发现没,《得到》专栏的导师,李笑来、万维钢、古典等,在专栏的分享中,都曾讲过这么一个道理——“最有效的学习方法就是教别人”。 如果我们学到某些知识,能清楚的讲给别人听;...

100个俯卧撑
2017/12/22
0
0
网易惠惠购物助手:大数据实时更新框架概述

一、需求是什么? 互联网中的许多应用都有数据实时更新的需求,比如网页搜索如何展示几分钟之前的新闻结果,购物搜索中价格、库存信息的实时更新。在大数据量的情况下,数据如何做到稳定及时...

xrzs
2015/12/05
636
0
android反编译工具 ApkDec-Release-0.1

(1)使用帮助 1.选择 all 全部编译内容包括jar、xml、及其他资源文件 2.选择 jar 只反编译并打成jar包 3.反编译后你可以使用jd-gui.exe查看源码 (2)jd-gui免费的java反编译查看工具,下载地...

eoeandroid
2013/03/31
0
2

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Android 复制和粘贴功能

做了一回搬运工,原文地址:https://blog.csdn.net/kennethyo/article/details/76602765 Android 复制和粘贴功能,需要调用系统服务ClipboardManager来实现。 ClipboardManager mClipboardM...

她叫我小渝
30分钟前
0
0
拦截SQLSERVER的SSL加密通道替换传输过程中的用户名密码实现运维审计(一)

工作准备 •一台SQLSERVER 2005/SQLSERVER 2008服务 •SQLSERVER jdbc驱动程序 •Java开发环境eclipse + jdk1.8 •java反编译工具JD-Core 反编译JDBC分析SQLSERVER客户端与服务器通信原理 SQ...

紅顏為君笑
46分钟前
4
0
jQuery零基础入门——(六)修改DOM结构

《jQuery零基础入门》系列博文是在廖雪峰老师的博文基础上,可能补充了个人的理解和日常遇到的点,用我的理解表述出来,主干出处来自廖雪峰老师的技术分享。 在《零基础入门JavaScript》的时...

JandenMa
今天
0
0
linux mint 1.9 qq 安装

转: https://www.jianshu.com/p/cdc3d03c144d 1. 下载 qq 轻聊版,可在百度搜索后下载 QQ7.9Light.exe 2. 去wine的官网(https://wiki.winehq.org/Ubuntu) 安装 wine . 提醒网页可以切换成中...

Canaan_
今天
0
0
PHP后台运行命令并管理运行程序

php后台运行命令并管理后台运行程序 class ProcessModel{ private $pid; private $command; private $resultToFile = ''; public function __construct($cl=false){......

colin_86
今天
1
0
数据结构与算法4

在此程序中,HighArray类中的find()方法用数据项的值作为参数传递,它的返回值决定是否找到此数据项。 insert()方法向数组下一个空位置放置一个新的数据项。一个名为nElems的字段跟踪记录着...

沉迷于编程的小菜菜
今天
1
1
fiddler安装和基本使用以及代理设置

项目需求 由于开发过程中客户端和服务器数据交互非常频繁,有时候服务端需要知道客户端调用接口传了哪些参数过来,这个时候就需要一个工具可以监听这些接口请求参数,已经接口的响应的数据,这种...

银装素裹
今天
0
0
Python分析《我不是药神》豆瓣评论

读取 Mongo 中的短评数据,进行中文分词 对分词结果取 Top50 生成词云 生成词云效果 看来网上关于 我不是药神 vs 达拉斯 的争论很热啊。关于词频统计就这些,代码中也会完成一些其它的分析任...

猫咪编程
今天
0
0
虚拟机怎么安装vmware tools

https://blog.csdn.net/tjcwt2011/article/details/72638977

AndyZhouX
昨天
1
0
There is no session with id[xxx]

参考网页 https://blog.csdn.net/caimengyuan/article/details/52526765 报错 2018-07-19 23:04:35,330 [http-nio-1008-exec-8] DEBUG [org.apache.shiro.web.servlet.SimpleCookie] - Found......

karma123
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部