本文将介绍使用selenium爬取某宝优惠券的方法,之所以使用selenium是因为我不会js逆向,如果你已经参透了淘宝联盟的js逆向方法,那么直接使用接口调数据就行了。
由于淘宝联盟需要先登录,为了避免每次打开selenium都要重新登录,我们让selenium接管已经登录过账号的chrome浏览器进程进行爬虫。
在打开的浏览器中输入某宝联盟首页,然后扫码登录即可阿里妈妈https://pub.alimama.com/portal/v2/pages/promo/goods/index.htm
其实我感觉爬虫真的没什么难度,因为前端再写的时候一般都会把重复的元素放到一个循环里面去创建,优惠券卡片也不例外,因此我们只需要找到数组,逐个提取其中的元素即可。
很容易发现,装有优惠券卡片的数组的
class=GoodsList__CardList-sc-84so0w-1 chSSLp
我们只需要找个这个div,然后逐个遍历数据提取信息即可。
我们首先来确定一下,本文主要爬取一张卡片的以下六个信息,分别是
图片url
优惠券标题
券前价格
券后价格
佣金率
佣金
对应上述六个数据,建立一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GoodItem {
// 优惠券标题
private String title;
// 图片url
private String imgUrl;
// 券前价格
private Double prePrice;
// 券后价格
private Double postPrice;
// 佣金率
private Double commissionRate;
// 佣金
private Double commission;
}
获取图片url和优惠券标题比较容易,直接通过类名去一个一个找就行了,这边主要介绍一下获取价格的方法。
这里拿券前价格举例子
我们可以看到价格是由四个标签组成的,每个标签都有不同的类名,我们只需要将后面三个标签对应的数字按照字符串拼接起来,然后解析成浮点型就行了。
private Double getPrice(WebElement element) {
StringBuilder sb = new StringBuilder();
sb.append(element.findElement(By.className("union-number-format-integer")).getText());
sb.append(element.findElement(By.className("union-number-format-pointer")).getText());
sb.append(element.findElement(By.className("union-number-format-decimal")).getText());
Double price = Double.parseDouble(sb.toString());
return price;
}
获取佣金率和佣金的逻辑也是相似的,只需要在调用getPrice()的时候传入特定的元素就行了
佣金率和佣金对应的class是一样的,所以我们一次拿两个div,第一个解析成佣金率,第二个解析成佣金
这里面还有一个坑,有一些商品价格超过1000,前端页面会添加一个逗号,导致解析失败,我们需要将字符串中所有的逗号都去掉
private Double getPrice(WebElement element) {
StringBuilder sb = new StringBuilder();
sb.append(element.findElement(By.className("union-number-format-integer")).getText().replaceAll(",", ""));
sb.append(element.findElement(By.className("union-number-format-pointer")).getText());
sb.append(element.findElement(By.className("union-number-format-decimal")).getText());
Double price = Double.parseDouble(sb.toString());
return price;
}
当我们爬取完一页数据后,需要点击下一页,并且待页面加载完后在开始获取数据
完整的代码如下
@Slf4j
@Service
public class SeleinumServiceImpl implements SeleinumService {
private final String DRIVER_PATH = "E:/写作/优惠券项目/驱动/chromedriver.exe";
@Override
public List getGoodInfo() {
// 加载chrome驱动
System.setProperty("webdriver.chrome.driver", DRIVER_PATH);
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("debuggerAddress", "127.0.0.1:9222");
// 启动浏览器
WebDriver driver = new ChromeDriver(options);
// 设置最长等待时间
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// 实例化一个列表存放数据
List rstList = new ArrayList<>();
// 开始遍历卡片数据
// 遍历100组数据暂停
for(int i=0; i<100; ) {
WebElement element = driver.findElement(By.className("GoodsList__CardList-sc-84so0w-1"));
List divList = element.findElements(By.className("union-good-card-wrap"));
log.info("获取" + divList.size() + "个优惠券卡片");
for(int j=0; j< divList.size(); j++) {
GoodItem item = new GoodItem();
// 图片url
item.setImgUrl(divList.get(j).findElement(By.className("union-good-card-good-img-wrap-mediumn"))
.findElement(By.tagName("a")).findElement(By.tagName("img")).getDomAttribute("src"));
// 优惠券标题
item.setTitle(divList.get(j).findElement(By.className("union-good-card-title"))
.findElement(By.tagName("span")).getText());
// 券前价格
item.setPrePrice(getPrice(divList.get(j)
.findElement(By.className("union-good-card-coupon-reserve-price-mediumn"))));
// 券后价格
item.setPostPrice(getPrice(divList.get(j)
.findElement(By.className("union-good-card-coupon-final-price"))));
List commissionList = divList.get(j).findElements(By.className("union-good-card-commision-info-item"));
// 佣金率
item.setCommissionRate(getPrice(commissionList.get(0)));
// 佣金
item.setCommission(getPrice(commissionList.get(1)));
log.info(JSON.toJSONString(item));
i++;
if(i == 100) {
log.info("100条数据获取完毕");
return rstList;
}
}
// 切换到下一页
driver.findElement(By.className("GoodsList__Pagination-sc-84so0w-2"))
.findElement(By.className("mux-pagination-icon-next")).click();
log.info("进入到下一页");
}
return rstList;
}
// 获取券前券后价格
private Double getPrice(WebElement element) {
StringBuilder sb = new StringBuilder();
sb.append(element.findElement(By.className("union-number-format-integer")).getText().replaceAll(",", ""));
sb.append(element.findElement(By.className("union-number-format-pointer")).getText());
sb.append(element.findElement(By.className("union-number-format-decimal")).getText());
Double price = Double.parseDouble(sb.toString());
return price;
}
}
最后看一下程序运行时的效果
可以看出来,selenium获取卡片的速度还是相对较慢的,有点像人在操作。
好啦,本文内容到此结束啦,有什么想法欢迎评论区和我讨论。