java抓取网页内容( 点赞再看,养成习惯(一)网页提取框架)

优采云 发布时间: 2021-12-21 17:06

  java抓取网页内容(

点赞再看,养成习惯(一)网页提取框架)

  

  喜欢再看,养成习惯

  大家好,我叫Labrami,今天更新了文章,带来了很干的干货。

  前言

  说起爬虫爬取网页数据,相信大家的第一反应都是python。Python 确实很适合这一点,但是许多具有多年经验的 Java 开发人员不一定知道它。其实Java也可以作为爬虫使用。最著名的是Jsoup网页提取框架。

  结婚了

  多年前,我做了一个贵金属信息类型网站。我需要实时显示各个交易所的最新黄金和白银价格。当时有第三方API接口服务商提供此类数据。你需要付钱。后来百度去Jsoup抓取网页的数据,然后找了个大网站根据它抓取数据,然后自己把表格展示在页面上,省了一大笔钱。

  昨晚心血来潮,想写一篇文章的文章,于是去官网看了一下,决定给优采云找个房间

  Jsoup食用指南

  Jsoup真的很简单,简单到不想介绍开发过程,直接看官网,十分钟搞定它的API。

  官网有介绍指南和例子,大家自己去看看吧。

  官网地址:/

  干货

  对于干货部分,我会用实战的方式来详细讲解。相信通过这个实战例子,大家可以轻松掌握技术。

  1、准备要爬取的网页

  优采云找房子-深圳站-新房:/loupan/pg

  2、新的Maven项目

  

  3、pom 文件添加依赖

  

com.google.guava

guava

30.0-jre

org.projectlombok

lombok

1.18.18

org.jsoup

jsoup

1.14.3

com.alibaba

easyexcel

2.2.10

复制代码

  4、创建一个Pojo类将数据映射到excel表

  因为从网页抓取的地址最后会保存在excel文件中,我们使用阿里巴巴的EasyExcel,所以需要在pom文件中引入依赖,并且需要创建一个pojo类来映射导出的文件。

  @Data

@Accessors(chain = true)

public class House {

@ExcelProperty("楼盘名称")

private String title;

@ExcelProperty("访问网页")

private String detailPageUrl;

@ExcelProperty("楼盘图片")

private String imageUrl;

@ExcelProperty("所在地址")

private String address;

@ExcelProperty("户型")

private String houseType;

@ExcelProperty("房产类型")

private String propertyType;

@ExcelProperty("状态")

private String status;

@ExcelProperty("建筑面积")

private String buildingArea;

@ExcelProperty("总价")

private String totalPrice;

@ExcelProperty("单价(元/㎡(均价))")

private String singlePrice;

@ExcelProperty("标签")

private String tag;

}

复制代码

  5、Main方法执行业务代码

  这里有几点需要注意:

  优采云短时间频繁访问同一个ip会触发人机验证,所以每次分页执行我们都需要让线程休眠一段时间。Jsoup 抓取 html 元素。如果优采云的网页有改动,程序可能无法正常抓取数据。程序不抓取楼盘详细信息,感兴趣的同学可以根据抓取到的详情页url进行二次开发。如果你要抓取的网站需要登录信息等特殊请求参数,Jsoup也支持设置,具体请参考官网API。

  @SneakyThrows

public static void main(String[] args) {

AtomicInteger pageIndex = new AtomicInteger(1);

int pageSize = 10;

List dataList = Lists.newArrayList();

// 贝壳找房深圳区域网址

String beikeUrl = "https://sz.fang.ke.com";

// 贝壳找房深圳市楼盘展示页地址

String loupanUrl = "https://sz.fang.ke.com/loupan/pg";

// 用Jsoup抓取该地址完整网页信息

Document doc = Jsoup.connect(loupanUrl + pageIndex.get()).get();

// 网页标题

String pageTitle = doc.title();

// 分页容器

Element pageContainer = doc.select("div.page-box").first();

if (pageContainer == null) {

return;

}

// 楼盘总数

int totalCount = Integer.parseInt(pageContainer.attr("data-total-count"));

// 分页执行

for (int i = 0; i < totalCount / pageSize; i++) {

log.info("running get data, the current page is {}", pageIndex.get());

// 贝壳网有人机认证,不能短时间频繁访问,每次翻页都让线程休眠10s

Thread.sleep(10000);

doc = Jsoup.connect(loupanUrl + pageIndex.getAndIncrement()).get();

// 获取楼盘列表的ul元素

Element list = doc.select("ul.resblock-list-wrapper").first();

if (list == null) {

continue;

}

// 获取楼盘列表的li元素

Elements elements = list.select("li.resblock-list");

elements.forEach(el -> {

// 楼盘介绍

Element introduce = el.child(0);

// 详情页面

String detailPageUrl = beikeUrl + introduce.attr("href");

// 楼盘图片

String imageUrl = introduce.select("img").attr("data-original");

// 楼盘详情

Element childDesc = el.select("div.resblock-desc-wrapper").first();

Element childName = childDesc.child(0);

// 楼盘名称

String title = childName.child(0).text();

// 楼盘在售状态

String status = childName.child(1).text();

// 产权类型

String propertyType = childName.child(2).text();

// 楼盘所在地址

String address = childDesc.child(1).text();

// 房间属性

Element room = childDesc.child(2);

// 户型

String houseType = "";

// 户型集合

Elements houseTypeSpans = room.getElementsByTag("span");

if (CollectionUtils.isNotEmpty(houseTypeSpans)) {

// 剔除文案:【户型:】

houseTypeSpans.remove(0);

// 剔除文案:【建面:xxx】

houseTypeSpans.remove(houseTypeSpans.size() - 1);

houseType = StringUtil.join(houseTypeSpans.stream().map(Element::text).collect(Collectors.toList()), "/");

}

// 建筑面积

String buildingArea = room.select("span.area").text();

// div - 标签

Element descTag = childDesc.select("div.resblock-tag").first();

Elements tagSpans = descTag.getElementsByTag("span");

String tag = "";

if (CollectionUtils.isNotEmpty(tagSpans)) {

tag = StringUtil.join(tagSpans.stream().map(Element::text).collect(Collectors.toList()), " ");

}

// div - 价格

Element descPrice = childDesc.select("div.resblock-price").first();

String singlePrice = descPrice.select("span.number").text();

String totalPrice = descPrice.select("div.second").text();

dataList.add(new House().setTitle(title)

.setDetailPageUrl(detailPageUrl)

.setImageUrl(imageUrl)

.setSinglePrice(singlePrice)

.setTotalPrice(totalPrice)

.setStatus(status)

.setPropertyType(propertyType)

.setAddress(address)

.setHouseType(houseType)

.setBuildingArea(buildingArea)

.setTag(tag)

);

});

}

if (CollectionUtils.isEmpty(dataList)) {

log.info("dataList is empty returned.");

return;

}

log.info("dataList prepare finished, size = {}", dataList.size());

// 调用导出逻辑,将数据导出到excel文件

export(pageTitle, dataList);

}

复制代码

  6、EasyExcel 导出逻辑

  /**

* 将爬取的数据写入到excel中

* @param pageTitle

* @param dataList

*/

private static void export(String pageTitle, List dataList) {

WriteCellStyle headWriteCellStyle = new WriteCellStyle();

//设置头居中

headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

//内容策略

WriteCellStyle contentWriteCellStyle = new WriteCellStyle();

//设置 水平居中

contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);

HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

// 这里需要设置不关闭流

EasyExcelFactory.write("D:\深圳楼盘汇总.xlsx", House.class).autoCloseStream(Boolean.FALSE).registerWriteHandler(horizontalCellStyleStrategy).sheet(pageTitle).doWrite(dataList);

}

复制代码

  7、成就展示

  

  

  有兴趣的朋友可以自己尝试一下!

  源代码:GitHub

  原创不容易,请多多点赞,非常感谢!

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线