前提:


  1. 了解平台的app、scenario、rule的创建。
  2. 了解RxTask、RxCrawler、RxDatabase、RxResult 4个平台接口

 

目录:


示例0 – 获得页面节点的XPath

示例1 – 抓取网页文本

示例2 – 参数的使用

示例3 – 将抓取结果入数据库

示例4 – 分Rule抓取,通过多个Rule完成最终抓取作业

示例5 – 批量创建任务

 

示例0 页面节点的XPath

一. 会使用google chrome浏览器提供的调试工具,在Chrome上获取指定节点的XPath。

Step1: 打开一个网页,按F12快捷键,在屏幕右侧会出现调试工具框。

Step2: 使用调试工具框获取页面元素的XPath。 首先点击调试工具框左上角的按钮,然后选择搜索关键字的输入框,这时后又阴影覆盖,如下图所示,鼠标左键点击该

输入框,这时调试工具框里与该输入框相对应的html代码会变成有背景色显示。鼠标右键该区域代码,在弹出的menu里选择

Copy -> Copy XPath,这时该输入框的XPath就会在系统剪切版中,然后在文本中Ctrl+V,就可以显示出该输入框的XPath了,

既//*[@id=”kw”]。

 

二.XPath基础

1.什么是XPath XPath 是XML Path的简称, 由于HTML文档本身就是一个标准的XML页面,所以我们可以使用XPath 的用法来定位页面元素。

2.绝对路径和相对路径 绝对路径 以 “/” 开头, 让xpath 从文档的根节点开始解析

以百度首页搜索关键字的输入框为例,XPath:/html/body/div[2]/div/div/div/div/form/span/input

相对路径 以”//” 开头, 让xpath 从文档的任何元素节点开始解析

同样以百度首页搜索关键字的输入框为例,XPath://*[@id=”kw”]

很明显相对路径比绝对路径更加简洁,而且一旦页面结构发生改变,绝对路径也随之失效,必须重新定位XPath。 所以不推荐使用绝对路径的写法。

XPath 更加详细的教程,请参考下面的网址。

http://www.w3school.com.cn/xpath

 

示例1 – 抓取网页文本

Step1: 打开平台的【Rule Script】页面,选择之前创建的app、scenario、rule,这时在【code】编辑框中,会出现默认的代码。

Step2: 点击【下载Eclipse本地编译Jar包】链接,下载平台接口jar包

Step3: 利用本地安装的eclipse等开发工具,创建一个java工程,引入刚才下载的jar包,并创建名为Rule的java类,将平台【code】编辑框中的代码,拷贝到Rule类中。

Step4: 修改默认的代码,以京东一个商品为例,抓取商品名信息,并输出,代码如下。

Step5: 代码没有编译错误,将代码拷贝到平台的【code】编辑框中,点击【Test】按钮,然后查看执行结果。商品名被抓取到,并输出。

示例代码:

import com.ruixuesoft.crawler.open.RxCrawler;
import com.ruixuesoft.crawler.open.RxCrawlerException;
import com.ruixuesoft.crawler.open.RxDatabase;
import com.ruixuesoft.crawler.open.RxNode;
import com.ruixuesoft.crawler.open.RxResult;
import com.ruixuesoft.crawler.open.RxRule;
import com.ruixuesoft.crawler.open.RxTask;

public class Rule implements RxRule {

	@Override
	public RxResult execute(RxTask task, RxCrawler crawler, RxDatabase database) throws RxCrawlerException {

		// 打开目标网站
		crawler.open("https://item.jd.com/12186460736.html");

		// 用xpath定位到抓取元素
		RxNode node = crawler.getNodeByXpath("/html/body/div[5]/div/div[2]/div[1]");

		// 输出元素文本内容
		task.log(node.getText());

		RxResult result = new RxResult();
		result.setFinishCode(200);

		return result;
	}

示例2 – 参数的使用

平台为Rule提供输入参数的功能,最多支持9个参数。

Step1: 将【示例1】中的代码改造一下,将目标网站的URL作为参数,传入程序,改造后的代码如下。

Test结果跟示例1相同

Step2: 利用参数这个功能,可以实现代码的复用。比如将v1参数的值更改,再Test程序,就会爬取其他商品名信息,并输出,如下所示。

示例代码:

import com.ruixuesoft.crawler.open.RxCrawler;
import com.ruixuesoft.crawler.open.RxCrawlerException;
import com.ruixuesoft.crawler.open.RxDatabase;
import com.ruixuesoft.crawler.open.RxNode;
import com.ruixuesoft.crawler.open.RxResult;
import com.ruixuesoft.crawler.open.RxRule;
import com.ruixuesoft.crawler.open.RxTask;

public class Rule2 implements RxRule {

	@Override
	public RxResult execute(RxTask task, RxCrawler crawler, RxDatabase database) throws RxCrawlerException {

		// 打开目标网站
		crawler.open(task.getV1());

		// 用xpath定位到抓取元素
		RxNode node = crawler.getNodeByXpath("/html/body/div[5]/div/div[2]/div[1]");

		// 输出元素文本内容
		task.log(node.getText());

		RxResult result = new RxResult();
		result.setFinishCode(200);

		return result;
	}
}

示例3 – 将抓取结果入数据库

Step1: 根据创建App时,填写的数据库信息,连接到该数据库,并建立数据表。

Step2: 根据创建的表结构,创建一个内部静态类。

Step3: 在execute()方法里,写入抓取和入库的代码。

Step4: 将程序拷贝到平台的【code】编辑框中,点击【Test】,等执行完毕后,到数据库中查看抓取结果。

示例代码:

import com.ruixuesoft.crawler.open.RxCrawler;
import com.ruixuesoft.crawler.open.RxCrawlerException;
import com.ruixuesoft.crawler.open.RxDatabase;
import com.ruixuesoft.crawler.open.RxNode;
import com.ruixuesoft.crawler.open.RxResult;
import com.ruixuesoft.crawler.open.RxRule;
import com.ruixuesoft.crawler.open.RxTask;

public class Rule implements RxRule {

	@Override
	public RxResult execute(RxTask task, RxCrawler crawler, RxDatabase database) throws RxCrawlerException {

		// 打开目标网站
		crawler.open(task.getV1());

		// 抓取商品名
		RxNode goodsNameNode = crawler.getNodeByXpath("/html/body/div[5]/div/div[2]/div[1]");
		GoodsEntity goodsEntity = new GoodsEntity();
		goodsEntity.setName(goodsNameNode.getText());
		task.log(goodsEntity.getName());

		// 抓取商品名
		RxNode goodsPriceNode = crawler
				.getNodeByXpath("/html/body/div[5]/div/div[2]/div[3]/div/div[1]/div[2]/span/span[2]");
		goodsEntity.setPrice(goodsPriceNode.getText());
		task.log(goodsEntity.getPrice());

		String sql = "insert into goods(name,price) values (?,?)";

		Object[] params = new Object[] { goodsEntity.getName(), goodsEntity.getPrice() };
		database.insert(sql, params);

		RxResult result = new RxResult();
		result.setFinishCode(200);

		return result;
	}

	public static class GoodsEntity {
		public GoodsEntity() {
		}

		private String name;
		private String price;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getPrice() {
			return price;
		}

		public void setPrice(String price) {
			this.price = price;
		}

	}
}

示例4 – 分Rule抓取,通过多个Rule完成最终抓取作业

Step1: 抓取业务说明,及Rule设计。

在京东网搜索【短袖t恤男】商品,在搜索结果中,爬取前5个商品名称、价格、商品介绍信息。

Rule1:完成搜索功能,爬取搜索结果中,爬取前5个商品的链接。

Rule2:根据Rule1爬取的商品链接,打开商品详细页面,爬取商品名称、价格、商品介绍信息。

 

注:当爬取全部搜索结果的时候,建议分成3个Rule,Rule1:抓取每一页的URL,Rule2:抓取每一页所有商品的连接,

Rule3:抓取每个商品的信息。分Rule的策略是将一个大的任务分成若干个子任务,避免一个环节的失败,导致整个任务

的失败。

Step2: 创建Rule2的爬取结果表。

Step3: 编写Rule1程序,输入v1和v2参数值,点击【Test】,查看结果

Step4: 查看Rule2创建情况。

打开【Task Progress】页面,选择【app】、【scenario】、【rule】,点击【查询】按钮,可以看到由Rule1创建的5个Rule2任务,并且V1参数都存放了商品的URL。

注:通过点击【Test】创建的Rule2任务,并不会执行。只有通过【Task Progress】页面通过Rule1创建的Rule2才会执行。

Step5: 编写Rule2程序,输入v1参数值,点击【Test】,查看运行结果,并查看数据库数据。

示例代码Rule1:

import java.util.List;

import com.ruixuesoft.crawler.open.RxCrawler;
import com.ruixuesoft.crawler.open.RxCrawlerException;
import com.ruixuesoft.crawler.open.RxDatabase;
import com.ruixuesoft.crawler.open.RxNode;
import com.ruixuesoft.crawler.open.RxResult;
import com.ruixuesoft.crawler.open.RxRule;
import com.ruixuesoft.crawler.open.RxTask;

public class Rule implements RxRule {

	@Override
	public RxResult execute(RxTask task, RxCrawler crawler, RxDatabase database) throws RxCrawlerException {

		// 打开目标网站
		crawler.open(task.getV1());// https://www.jd.com/

		// 输入商品关键字
		crawler.input("//*[@id='key']", task.getV2());// 短袖t恤男

		// 点击搜索
		RxNode searchNode = crawler.getNodeByXpath("//*[@id='search']/div/div[2]/button");
		searchNode.click();

		crawler.sleepSeconds(2);

		// 爬取搜索结果
		List<RxNode> goodsNodeList = crawler.getNodeListByXpath("//*[@id='J_goodsList']/ul/li");

		RxTask newNextRuleTask = new RxTask();

		for (int i = 0; i < 5; i++) {
			// 爬取商品链接
			RxNode goodsNode = goodsNodeList.get(i);
			RxNode goodsUrlNode = goodsNode.getNodeByXpath("./div/div[4]/a");
			String goodsUrl = goodsUrlNode.getAttribute("href");

			// 商品URL作为Rule2的参数
			newNextRuleTask.setV1(goodsUrl);

			// 创建Rule2任务
			task.createNextRuleTask(newNextRuleTask);

		}

		RxResult result = new RxResult();
		result.setFinishCode(200);

		return result;

	}
}

示例代码Rule2:

import com.ruixuesoft.crawler.open.RxCrawler;
import com.ruixuesoft.crawler.open.RxCrawlerException;
import com.ruixuesoft.crawler.open.RxDatabase;
import com.ruixuesoft.crawler.open.RxNode;
import com.ruixuesoft.crawler.open.RxResult;
import com.ruixuesoft.crawler.open.RxRule;
import com.ruixuesoft.crawler.open.RxTask;

public class Rule implements RxRule {
	@Override
	public RxResult execute(RxTask task, RxCrawler crawler, RxDatabase database) throws RxCrawlerException {
		// 得到任务参数
		String goodsUrl = task.getV1();

		// 打开商品URL
		crawler.open(goodsUrl);

		GoodsInfo goodsInfo = new GoodsInfo();

		// 商品名称
		RxNode nameNode = crawler.getNodeByXpath("/html/body/div[7]/div/div[2]/div[1]");
		goodsInfo.setName(nameNode.getText());

		// 价格
		RxNode priceNode = crawler.getNodeByXpath("/html/body/div[7]/div/div[2]/div[3]/div/div[1]/div[2]/span/span[2]");
		if (priceNode != null) {
			goodsInfo.setPrice(priceNode.getText());
		}

		// 品牌
		RxNode brandNode = crawler.getNodeByXpath("//*[@id='parameter-brand']/li");
		goodsInfo.setPin_pai(brandNode.getAttribute("title"));

		// 编号
		RxNode bianhaoNode = crawler.getNodeByXpath("//*[@id='detail']/div[2]/div[1]/div[1]/ul[2]/li[2]");
		goodsInfo.setBianhao(bianhaoNode.getAttribute("title"));

		// 店铺
		RxNode dianpuNode = crawler.getNodeByXpath(" //*[@id='detail']/div[2]/div[1]/div[1]/ul[2]/li[3]");
		goodsInfo.setDianpu(dianpuNode.getAttribute("title"));

		// 毛重
		RxNode maozhongNode = crawler.getNodeByXpath(" //*[@id='detail']/div[2]/div[1]/div[1]/ul[2]/li[4]");
		goodsInfo.setMaozhong(maozhongNode.getAttribute("title"));

		// 货号
		RxNode huohaoNode = crawler.getNodeByXpath(" //*[@id='detail']/div[2]/div[1]/div[1]/ul[2]/li[5]");
		goodsInfo.setHuohao(huohaoNode.getAttribute("title"));

		// 尺码
		RxNode chimaNode = crawler.getNodeByXpath(" //*[@id='detail']/div[2]/div[1]/div[1]/ul[2]/li[6]");
		goodsInfo.setChima(chimaNode.getAttribute("title"));

		// 版型
		RxNode banxingNode = crawler.getNodeByXpath(" //*[@id='detail']/div[2]/div[1]/div[1]/ul[2]/li[8]");
		goodsInfo.setBanxing(banxingNode.getAttribute("title"));

		// 主要材质
		RxNode zhuyaocaizhiNode = crawler.getNodeByXpath(" //*[@id='detail']/div[2]/div[1]/div[1]/ul[2]/li[9]");
		goodsInfo.setZhuyaocaizhi(zhuyaocaizhiNode.getAttribute("title"));

		// 入库SQL
		String sql = "INSERT INTO `goods_info`(`name`,`price`,`pin_pai`,`bianhao`,`dianpu`,`maozhong`,`huohao`,`chima`,`banxing`,`zhuyaocaizhi`)VALUES(?,?,?,?,?,?,?,?,?,?);";

		// 执行入库
		Object[] params = new Object[] { goodsInfo.getName(), goodsInfo.getPrice(), goodsInfo.getPin_pai(),
				goodsInfo.getBianhao(), goodsInfo.getDianpu(), goodsInfo.getMaozhong(), goodsInfo.getHuohao(),
				goodsInfo.getChima(), goodsInfo.getBanxing(), goodsInfo.getZhuyaocaizhi() };
		database.insert(sql, params);

		RxResult result = new RxResult();
		result.setFinishCode(200);

		return result;
	}

	public static class GoodsInfo {

		private String name; // 商品名称
		private String price; // 价格
		private String pin_pai; // 品牌
		private String bianhao; // 编号
		private String dianpu; // 店铺
		private String maozhong; // 毛重
		private String huohao; // 货号
		private String chima; // 尺码
		private String banxing; // 版型
		private String zhuyaocaizhi;// 主要材质

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getPrice() {
			return price;
		}

		public void setPrice(String price) {
			this.price = price;
		}

		public String getPin_pai() {
			return pin_pai;
		}

		public void setPin_pai(String pin_pai) {
			this.pin_pai = pin_pai;
		}

		public String getBianhao() {
			return bianhao;
		}

		public void setBianhao(String bianhao) {
			this.bianhao = bianhao;
		}

		public String getDianpu() {
			return dianpu;
		}

		public void setDianpu(String dianpu) {
			this.dianpu = dianpu;
		}

		public String getMaozhong() {
			return maozhong;
		}

		public void setMaozhong(String maozhong) {
			this.maozhong = maozhong;
		}

		public String getHuohao() {
			return huohao;
		}

		public void setHuohao(String huohao) {
			this.huohao = huohao;
		}

		public String getChima() {
			return chima;
		}

		public void setChima(String chima) {
			this.chima = chima;
		}

		public String getBanxing() {
			return banxing;
		}

		public void setBanxing(String banxing) {
			this.banxing = banxing;
		}

		public String getZhuyaocaizhi() {
			return zhuyaocaizhi;
		}

		public void setZhuyaocaizhi(String zhuyaocaizhi) {
			this.zhuyaocaizhi = zhuyaocaizhi;
		}
	}

}

示例5 – 批量创建任务

重要前提:Rule的【job switch】要设置为ON,并且【规则有效期】一定要设置成有效期间, 否则规则不能批量执行。

Step1: 由【示例4】创建的两个Rule都能正常运行,这样就可以批量的创建任务了。

通过左侧Menu进入【Task Progress】页面,选择user、app、scenario、rule,点击【创建】按钮。

Step2: 创建2个Rule1的任务,每个任务需要设置v1和v2两个参数。

Step3: 创建任务之后,点击【查询】按钮,这是会显示2个任务的状态信息,再点击【查询详细】按钮,查看2个任务的详细信息。

Step4: 当Rule1的两个任务状态是【FINISHED】时,在Rule列表里选择Rule2,再点击【查询】按钮,可以查看Rule2的10任务的状态。

当Rule2的10个任务状态是【FINISHED】时,说明任务都执行完了,可以查看数据库表里的信息。