文章目录

  • 前言
  • 最终效果
    • 效果演示
      • 爬取所有记录
      • 根据条件爬取
    • 项目仓库
  • 项目前准备
    • 技术选型
    • 开发工具
    • 用到的jar包
  • 爬取所有数据
    • 页面分析
      • 第一步:获取所有省份列表
    • 代码实现
      • 进入主页
      • 解析页面
        • 获得所有省的文档模型
        • 循环,获取各省的信息
        • 获得市列表
        • 获得总页数和翻页时的路径规则
        • 进入拍卖品列表页并取出值
        • 解析`json`数据获得详情路径,并提取数据
      • 持久化数据库
        • 定义POJO
        • 构造数据库操作工厂类
        • 执行插入
  • 根据搜索条件爬取数据
    • 分析
      • 从EXCEL中获得查询关键字
  • 总结

前言

最近在学习过程中接到一个任务,要求爬取阿里拍卖中法院拍卖的所有拍卖品。用了点时间完成了任务,并分享出来作为经验供学习、交流。若文中有任何不妥之处请提出。

最终效果

效果演示

爬取所有记录

  • 控制台打印记录
  • 数据库记录

根据条件爬取

  • 控制台打印记录
  • 数据库记录

项目仓库

  • github地址:https://github.com/virtuex/all-spilder
  • git仓库:https://github.com/virtuex/all-spilder.git

项目前准备

技术选型

  • HTML解析技术:Jsoup
    jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

想了解Jsoup相关内容,请移步:https://jsoup.org/

开发工具

  • IDEA 2018.2
  • MYSQL 5.7
  • Chrome

用到的jar包

  • easy-poi:3.0.3
  • mysql-connector-java:5.1.40
  • jsoup:1.8.3
  • fastjson:1.2.47

爬取所有数据

页面分析

  • 首先我们通过http://sf.taobao.com/court_list.htm?spm=a213w.3065169.sfhead2014.3.3e1f1a333zcUBm,进入要爬取的入口页,入口页的结构如下图所示:

    页面的机构是以省份为区分,在省下包含市,市包含法院,而法院中的链接市我们要获取信息的下一个入口。为了实现目的,爬虫的处理逻辑如下:
1.进入入口页,获取所有省份的DOM
2.对第一个省份继续进行处理,获取市级列表
3.对第一个市进行处理,获取法院列表和链接地址
4.进入第一个法院,获得拍卖品列表
5.进入商品详情页获取需要的信息
6.重复5,知道所有拍卖品爬完
7.重复4-6,直至所有法院爬完
8.重复3-7,直至所有市爬完
9.重复2-8,直至所有省份爬完
10.结束

第一步:获取所有省份列表

  • 首先用Chrome浏览器检查爬虫入口页面元素,这样能够清晰直观看出页面的布局,如下图所示

    图中,1是省份的div,包裹在一个class="provinces clearfix"div中,在1中还包含了2,3,4这些元素,这些都是我们接下来需要的内容。
    图中,2是省份的名称,包裹在一个class="province"div中,通过查看多个省份的内容可以发现,这个模型中只会有省份名称这个中文,所以取出这个信息时可以通过正则表达式对中文进行匹配,从而取出值。
    图中,3是省份下的市列表,包裹在一个class="province"div中。每个市在一个单独的class="city"div中。图中的4,市该市下的法院列表,法院列表中每个法院的链接才是我们需要的内容,通过访问链接才能进入该法院的拍卖品列表,每个法院的链接规则如下:

    获取也很简答,只用获取a标签中的href属性即可,但访问时为了比较明显,要在前面加个http:
    点击链接,进入拍卖品列表,如下:

    都这样的结构也很简单,第一想法时使用上述相同的方法直接一条一条爬取就行。但如果这样你会发现,并爬不到数据,后来才发现,所有的数据并不是在页面请求时就直接加载的,所有的数据会以json的格式放在一个script标签中,如下图所示:

    这样就更简单了,直接通过Json解析,然后取出详情链接就可以了。但需要注意的是,数据只是这一页中的,事实上数据可能有很多页。所以这时的想法就是看下每页的请求路径有什么规律,通过对比就发现翻页时的路径类似于http://sf.taobao.com/court_item.htm?spm=a213w.7398554.pagination.1.5edd2fc2Vn735Z&user_id=2364124517&auction_start_seg=-1&page=2,而页码对应的就是page的值,所以我们只需要从页面上获取总计页数,通过循环就可以得到每页的数据。

    方法很简单,只需要获取到class="page-total"的em标签的text内容即可
    接下来就是详情页,详情页我们只抓三个部分的内容

    其中标题和变卖价的规则如下:


    这里根据之前的规则就可以取出值了。接下来通过代码来实现这一过程

代码实现

本文前面已经附带了github的地址,原项目使用gradle构建,需要的朋友可以参考。所以本部分以关键部分代码讲解为主。

进入主页

Document document = Jsoup.connect(ConValues.SF_URL_ENTRANCE).timeout(5000).get();

这一步比较简单,需要注意的是最好加上timeout()方法设置超时时间,不然可能会出现java.net.SocketTimeoutException:Read timed out异常。通过这一步就可以得到文档对象,来完成下列操作。

解析页面

获得所有省的文档模型

Elements select = document.select("div[class=provinces clearfix]");

循环,获取各省的信息

Elements elements = element.select("div[class=province"); Matcher valueByReg = DataUtil.getValueByReg(ConValues.ZHONG_WEN_REG, elements.toString());
if(valueByReg.find()){//省province = valueByReg.group(0);}

获得市列表

 Elements citys = var1.select("dl[class=city]");

获得总页数和翻页时的路径规则

 Elements var4 = var3.select("a");for(Element var5:var4){if(var5.hasAttr("rel")){basePageUrl = "http:"+var5.attr("href").trim();break;}}String pageText = var3.select("span[class=page-skip]").select("em[class=page-total]").text();if(pageText.length()>0){totalPage = Integer.parseInt(pageText);}

进入拍卖品列表页并取出值

//对每一页进行处理
String pageUrl = basePageUrl.substring(0,basePageUrl.length()-1)+i;
Document document2 = Jsoup.connect(pageUrl).timeout(5000).get();//获得该页所有商品信息,该页的所有商品信息是以json的格式存放在<script>标签中的String oriData = document2.getElementById("sf-item-list-data").toString();//接下来对数据进行处理//1.找到“>”标签的位置int start = oriData.indexOf(">");//2.找到"</"标签的位置int end = oriData.indexOf("</");//截取出值String data = oriData.substring(start+1, end);

解析json数据获得详情路径,并提取数据

JSONArray data1 = (JSONArray) JsonUtil.convertJsonStrToMap(data).get("data");
Iterator<Object> iterator = data1.iterator();
while(iterator.hasNext()){JSONObject next = (JSONObject)iterator.next();//需要记录的urlString detailUrl = "http:"+next.get("itemUrl");//获取标题Document document3 = Jsoup.connect(detailUrl).timeout(5000).get();String detailTitle = document3.select("div[class=pm-main clearfix]").select("h1").text();//获取变卖价String detailPrice = document3.select("span[class=J_Price]").first().text();System.out.println("抓取信息:");System.out.println(province+"  "+city+"  "+countryName+"  "+detailTitle+"  "+detailPrice+"   "+detailUrl);
}

持久化数据库

定义POJO

public class AuctionItem {private String province;//省份private String city;//城市private String countryName;//法院private String detailTitle;//标题private String detailPrice;//变卖价private String detailUrl;//资源路径//getter and setter.............
}

构造数据库操作工厂类

public class DbOpFactory {// JDBC 驱动名及数据库 URLstatic final String JDBC_DRIVER = "com.mysql.jdbc.Driver";static final String DB_URL = "jdbc:mysql://localhost:3306/spider-data";private static DbOpFactory instance;public static DbOpFactory getInstance(){if(instance==null){return new DbOpFactory();}else{return instance;}}private DbOpFactory(){init();}// 数据库的用户名与密码,需要根据自己的设置static final String USER = "root";static final String PASS = "xda265856";Connection conn = null;Statement stmt = null;ResultSet rs = null;public void init() {// 注册 JDBC 驱动try {Class.forName("com.mysql.jdbc.Driver");// 打开链接System.out.println("连接数据库...");conn = DriverManager.getConnection(DB_URL,USER,PASS);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}}/*** 持久化拍卖信息* @param auctionItem*/public void insertAuction(AuctionItem auctionItem){try{// 执行查询// System.out.println(" 实例化Statement对象...");stmt = conn.createStatement();String sql;sql = String.format("INSERT INTO TB_AUCTION_ITEM(AUCTION_PROVINCE,AUCTION_CITY,AUCTION_COUNTRY_NAME,AUCTION_DETAIL_TITLE,AUCTION_PRICE,AUCTION_DETAIL_URL) VALUES('%s','%s','%s','%s','%s','%s')",auctionItem.getProvince(),auctionItem.getCity(),auctionItem.getCountryName(),auctionItem.getDetailTitle(),auctionItem.getDetailPrice(),auctionItem.getDetailUrl());stmt.executeUpdate(sql);}catch(SQLException se){// 处理 JDBC 错误se.printStackTrace();}catch(Exception e){// 处理 Class.forName 错误e.printStackTrace();}public void close(){// 完成后关闭try {stmt.close();conn.close();} catch (SQLException e) {e.printStackTrace();}}
}

执行插入

DbOpFactory instance = DbOpFactory.getInstance();
instance.init();
........
AuctionItem auctionItem = new AuctionItem();
auctionItem.setProvince(province);
auctionItem.setCity(city);
auctionItem.setCountryName(countryName);
auctionItem.setDetailTitle(detailTitle);
auctionItem.setDetailPrice(detailPrice);
auctionItem.setDetailUrl(detailUrl);
instance.insertAuction(auctionItem);
System.out.println("存入数据库成功");
........
instance.close();

至此爬虫完成,上述代码有不详尽的地方请到github上查看源码。

根据搜索条件爬取数据

根据条件的爬虫时根据搜索栏中输入的关键字进行查询,这里将需要查询的信息放到EXCEL表格中,方便获取。

分析

总体思路于爬取所有的内容时相同的。

  • 在搜索框中输入关键字
  • 搜索结果页面的地址如下
    值为http://sf.taobao.com/list/0.htm?auction_start_seg=-1&q=%CE%E2%BD%AD%CB%C9%C1%EA%D5%F2%BD%AD%D8%C7%C2%B78%BA%C5A16-1&page=1,令人费解的是%CE%E2%BD%AD%CB%C9%C1%EA%D5%F2%BD%AD%D8%C7%C2%B78%BA%C5A16-1这串字符串。猜测应该是搜索的关键字但具体是什么就不得而知。通过了解发现这是URL字符转义(想了解更多,请自行百度),所以我们通过下面的方法可以实现对URL的字符转义功能
 /*** url字符转码
*/public static String getURLEncode(String urlValue){String urlEncode= null;try {urlEncode = java.net.URLEncoder.encode(urlValue, "gb2312");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return urlEncode;}

我们通过对路径的拆分、拼接,即可得到完整的路径

 //查询地址前面部分
public static String SEARCH_ADDRESS_PREFIX = "http://sf.taobao.com/item_list.htm?q=";
//查询地址后面部分
public static String SEARCH_ADDRESS_SUFFIX = "&spm=a213w.3064813.9001.1";

从EXCEL中获得查询关键字

这里使用Easy-poi获取excel中的内容

  • 定义pojo
public class SearchAttribute {@Excel(name = "序号")private String id;@Excel(name = "地址")private String address;@Excel(name = "所有人")private String owner;@Excel(name = "产证号1")private String certNum1;@Excel(name = "产证号2")private String certNum2;@Excel(name = "产证号3")private String certNum3;//getter and setter...................
}
  • 获取信息
public class PoiUtils {public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws  Exception {ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);List<T> list = null;try {list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);} catch (NoSuchElementException e) {throw new Exception("模板不能为空");} catch (Exception e) {e.printStackTrace();throw new Exception(e.getMessage());}return list;}
}
//得到Excel中的信息
List<SearchAttribute> allAttributes = PoiUtils.importExcel("file/paimai.xlsx", 0, 1, SearchAttribute.class);

剩下的内容和爬取所有内容相同在此不做赘述

总结

  • 总体上来说功能实现了,但还有很多细节需要优化
  • 如果有任何问题欢迎留言探讨

使用Jsoup技术获取`阿里拍卖`中法院拍卖的所有拍卖品相关推荐

  1. 百度人脸识别技术应用003---百度云离线SDK_Android版_功能分析_获取摄像头视频流中的图片_发给百度人脸识别API_识别搜索_或先同步百度云人脸信息_然后本地人脸识别

    1.基于上一节,我们已经把百度云上的人脸库,建好了,然后我们可以从,百度云官网拿到, 离线人脸识别的SDK. SDK下载_文字识别SDK_语音识别SDK-百度AI开放平台 这里可以点进去下载 2.然后 ...

  2. Embedding技术在房产推荐中的应用

    本文概览: 1. 房产业务及推荐介绍 58同城和安居客是国内第一大房产信息平台.作为房产信息平台主要有两种业务场景.两类服务对象.第一类是服务于经纪人,当房产经纪人有卖房需求的时候,可以把房源发布在平 ...

  3. 如何基于NTP协议获取阿里云实时时间

    以下文章来源于:公_众_号开源电子网 读取更多技术文章,请扫码关注 关注公众号,后台回复:资料 (免费领取项目文件) NTP协议简介: NTP服务器(Network Time Protocol(NTP ...

  4. 《纯技术分析阿里云OS和Android之间的关系》补充

    前面一份投稿的错误很抱歉,rom本身就找错了,用了一个AOSP-like的刷机包.好在这份内容错误的投稿多少起到了抛砖引玉的作用,当天晚上,就给出了正确的rom的分析<纯技术分析阿里云OS和An ...

  5. 阿里旺旺分析系列一:实时获取阿里旺旺聊天消息,实现旺旺客服机器人

    目前网络上有为数不多的几款客服机器人.比如某某旺旺机器人.实现的大概机制是获取淘宝旺旺收到的聊天消息.从本地数据库中搜索答案.然后再自动或者手动回复. 本文详细讲述如何分析阿里旺旺和某某旺旺机器人软件 ...

  6. 中科院赵军:知识图谱关键技术及其在推荐系统中的应用

    不到现场,照样看最干货的学术报告! 嗨,大家好.这里是学术报告专栏,读芯术小编不定期挑选并亲自跑会,为大家奉献科技领域最优秀的学术报告,为同学们记录报告干货,并想方设法搞到一手的PPT和现场视频--足 ...

  7. 漏洞分析与检测技术在物联网安全中的应用

    这里写自定义目录标题 一.引言 二.相关理论 1.物联网系统安全挑战 1.1 网络层安全挑战 1.2 硬件感知层安全挑战 1.3 中间件层安全挑战 1.4 应用功能层安全挑战 2.漏洞挖掘技术 三.漏 ...

  8. COM:下一代微生物组技术在作物生产中的应用——局限性以及基于知识的解决方案的需求

    下一代微生物组技术在作物生产中的应用--局限性以及基于知识的解决方案的需求 Next generation microbiome applications for crop production - ...

  9. 综述 | 语言分析技术在社会计算中的应用

    综述 | 语言分析技术在社会计算中的应用 刘知远 社媒派SMP 2016-01-27 中国中文信息学会社会媒体处理专委会 传统社会科学研究中的数据主要通过调查问卷或口头采访等方式获取,既耗时耗力,数据 ...

最新文章

  1. Arabidopsis thaliana 拟南芥 长read SRX533608
  2. python从入门到入土表情包-Python开发个人专属表情包网站,表情在手,天下我有...
  3. android getevent参数,android getevent、sendevent、input keyevent 使用说明
  4. Discuz!NT数据库读写分离方案
  5. [python]如何清屏?也就是实现clear?
  6. 年轻人的第一篇V语言笔记
  7. c语言高级工程师试题,2020年.Net中高级开发工程师笔试题带答案(3)
  8. 【渝粤教育】广东开放大学 服务标准化 形成性考核 (41)
  9. sprintf函数的用法linux,Linux 常用函数——sprintf函数
  10. windows平台使用Docker搭建分布式hadoop集群
  11. Java错误:找不到或无法加载主类
  12. webp格式是什么?webp格式怎么打开?webp格式怎么转换gif/jpg/png等?
  13. “我爱淘”冲刺阶段Scrum站立会议9
  14. 多目标优化算法:多目标非洲秃鹫优化算法(Multi-objective Africans Vultures Optimization Algorithm,MOAVOA)提供MATLAB代码及参考文献
  15. java 绑定mac地址_如何JAVA实现使用TrueLicense在证书中绑定PC的MAC地址,以防止拷贝应用...
  16. WIN7 直装版安装教程
  17. 2023广州大米展会
  18. C语言实现LDPC的校验矩阵,非正则LDPC码在AWGN信道中的性能研究
  19. 【2629】Identity Card 【比较坑 / 水题】
  20. 轻松制作短视频网页工具:锐视RyView 帮助文档

热门文章

  1. 3dmax骨骼的绑定
  2. iOS 各种技术点网址
  3. 支持向量机专题——线性可分支持向量机
  4. 「小程序JAVA实战」小程序的个人信息作品,收藏,关注(66)
  5. 牛客-郊区春游(状态压缩dp)(java)(c++)
  6. 10053 事件详解
  7. MATLAB在线工具,画图啥的都不用安装了,超级实用
  8. 如何在win7下装ubuntu(硬盘版安装)
  9. selenium调用IE浏览器
  10. Java面试题有哪些