前言:这两周在做 Web 课的大作业,顺便琢磨了一下如何使用 Java 从网上获取一些数据,现在写这篇博客记录一下。
PS:这里仅限交流学习用,如利用代码进行恶意攻击他网站,和作者无关!!!

Java爬取解析携程景点信息

网上用 Java 做数据爬取的案例不少,但是很少是能用的,有些是几年前能用,但是现在不行了,有些则是只有一个思路,在上网查阅许多资料之后我琢磨出了一个可行的爬取去哪儿景点信息的方案。

使用工具:

  • HttpClient:发出请求
  • Jsoup:解析页面
  • MyBatis:数据保存

所需 Maven 依赖:

     <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.20</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.4</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>RELEASE</version><scope>test</scope></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><version>4.4.14</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency><dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version></dependency><dependency><!-- jsoup HTML parser library @ https://jsoup.org/ --><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.11.3</version></dependency><!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.1.0</version></dependency>

一、发出请求

我这里使用 HttpClient 来发 Get 请求获取我想要的信息,代码如下:

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.DefaultHttpParams;
import org.apache.commons.httpclient.params.HttpMethodParams;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;/*** @author 小关同学* @create 2021/11/9*/
public class Request {public static String doGet(String url) {// 输入流InputStream is = null;BufferedReader br = null;String result = null;// 创建httpClient实例HttpClient httpClient = new HttpClient();// 设置http连接主机服务超时时间:15000毫秒// 先获取连接管理器对象,再获取参数对象,再进行参数的赋值httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(15000);// 创建一个Get方法实例对象GetMethod getMethod = new GetMethod(url);// 设置get请求超时为60000毫秒getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);// 设置请求重试机制,默认重试次数:3次,参数设置为true,重试机制可用,false相反getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, true));DefaultHttpParams.getDefaultParams().setParameter("http.protocol.cookie-policy", CookiePolicy.BROWSER_COMPATIBILITY);try {Thread.sleep(5000);// 执行Get方法int statusCode = httpClient.executeMethod(getMethod);System.out.println("请求状态码:"+statusCode);// 判断返回码
//            if (statusCode != HttpStatus.SC_OK) {// 如果状态码返回的不是ok,说明失败了,打印错误信息
//                System.err.println("Method faild: " + getMethod.getStatusLine());
//            } else {// 通过getMethod实例,获取远程的一个输入流is = getMethod.getResponseBodyAsStream();// 包装输入流br = new BufferedReader(new InputStreamReader(is, "UTF-8"));StringBuffer sbf = new StringBuffer();// 读取封装的输入流String temp = null;while ((temp = br.readLine()) != null) {sbf.append(temp).append("\r\n");}result = sbf.toString();
//            }} catch (Exception e) {e.printStackTrace();} finally {// 关闭资源if (null != br) {try {br.close();} catch (Exception e) {e.printStackTrace();}}if (null != is) {try {is.close();} catch (Exception e) {e.printStackTrace();}}// 释放连接getMethod.releaseConnection();}return result;}
}

这里的发出请求代码网上多得是,我这个是借鉴了网上的代码,然后稍作修改而来的,主要修改了判断返回状态码那里,使得一次请求不成功之后程序不会立即停止,而是继续发出请求,直到获取到想要的数据。

PS:这里按理说最好改一下请求头什么的,来避过反爬机制,但是我懒得改了,就让它那样了(能用就行)。

二、对 HTML 页面进行解析(重点)

对 HTML 页面进行解析的话就要使用 Jsoup 了,这里先介绍一下这个 Jsoup 是什么。

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

Jsoup 的主要功能

  1. 从一个 URL,文件或字符串中解析 HTML;
  2. 使用 DOM 或 CSS 选择器来查找、取出数据;
  3. 可操作 HTML 元素、属性、文本。

我们这里使用 Jsoup 解析前面获取到的 HTML 页面信息,代码如下:

import com.entity.Spot;
import com.entity.SpotDetail;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;/*** @author 小关同学* @create 2021/11/6* 爬取去哪儿旅行的数据*/
public class SpotDataCrawler {/*** 爬取对应城市景点简介信息* @param city* @return*/public static Set<Spot> requestSpotData(String city, int page) {String ascii = CodeTransition.stringToASCII(city);String target = Request.doGet("http://piao.qunar.com/ticket/list.htm?keyword="+ascii+"&region=&from=mpl_search_suggest&page="+page);Document root_document = null;assert target != null;//是404页面时,返回nullif (target.contains("<h2>非常抱歉,您访问的页面不存在。</h2>")){return null;}root_document = Jsoup.parse(target);//获取需要数据的divElement e = root_document.getElementById("search-list");//得到网页上的景点列表(包含两个样式集合)Elements yy = e.getElementsByClass("sight_item sight_itempos");Elements yy2 = e.getElementsByClass("sight_item");yy.addAll(yy2);//图片//#search-list > div:nth-child(1) > div > div.sight_item_show > div > a//标题//#search-list > div:nth-child(1) > div > div.sight_item_about > h3 > a//地点//#search-list > div:nth-child(1) > div > div.sight_item_about > div > p > span//简介//#search-list > div:nth-child(1) > div > div.sight_item_about > div > div.intro.color999//热度//#search-list > div:nth-child(1) > div > div.sight_item_about > div > div.clrfix > div > span.product_star_level > em > spanSet<Spot> spotList = new HashSet<>();for (int i = 0; i < yy.size(); i++) {Spot spot = new Spot();//得到每一条景点信息Element Info = yy.get(i);//分析网页得到景点的标题(使用选择器语法来查找元素)//景点名称信息Element nameStr = Info.selectFirst(" div > div.sight_item_about > h3 > a");String name = nameStr.html();spot.setName(name);System.out.println("景点名称:" + name);//景点图片信息Element pictureStr = Info.selectFirst("div > div.sight_item_show > div > a");String picture = pictureStr.html();int index = picture.indexOf(" alt");String url = picture.substring(20,index-1);spot.setPicture(url);System.out.println("景点图片地址:" + url);//价格Element priceStr = Info.selectFirst("div > div.sight_item_pop > table > tbody > tr:nth-child(1) > td > span > em");if (priceStr!=null){String price = priceStr.html();if (!price.isEmpty()){spot.setPrice(Double.parseDouble(price));System.out.println("价格:" + price);}}//景点地点信息Element addressStr = Info.selectFirst(" div > div.sight_item_about > div > p > span");String address = addressStr.html();spot.setArea(address);System.out.println("景点地点:" + address);//景点简介Element infoStr = Info.selectFirst(" div > div.sight_item_about > div > div.intro.color999");String info = infoStr.html();spot.setInfo(info);System.out.println("景点简介:" + info);//景点在网页中对应的id//#search-list > div:nth-child(1) > div > div.sight_item_about > div > p > aElement spotIdInWebStr = Info.selectFirst("div > div.sight_item_about > div > p > a");int start = spotIdInWebStr.toString().indexOf("data-sightid=\"");int end = spotIdInWebStr.toString().indexOf("\">地图");String spotWebId = spotIdInWebStr.toString().substring(start+14,end);spot.setSpotWebId(spotWebId);System.out.println("对应详情页面的id:"+spotWebId);spotList.add(spot);}return spotList;}/*** 爬取景点的详细信息* @param name 景点名称* @param webId 景点对应的webId* @return 返回相关信息*/public static Object[] requestSpotDetailData(String name,int id,String webId){Object[] result = new Object[2];Spot spot = new Spot();List<SpotDetail> list = new ArrayList<>();String ascii = CodeTransition.stringToASCII(name);String url = "http://piao.qunar.com/ticket/detail_" + webId + ".html?st="+ascii+"#from=mpl_search_suggest";String target = Request.doGet(url);assert target != null;//是404页面时,返回nullif (target.contains("<h2>非常抱歉,您访问的页面不存在。</h2>")){System.out.println("非常抱歉,您访问的页面不存在");return null;}Document root_document;root_document = Jsoup.parse(target);//分析网页得到景点的标题(使用选择器语法来查找元素)spot.setId(id);//特色看点->推荐理由(保存在Spot里面)//#mp-charact > div > div.mp-charact-intro > div.mp-charact-desc > p:nth-child(1)//#mp-charact > div > div.mp-charact-intro > div.mp-charact-desc > p:nth-child(2)String reason = "";Element reasonStr1 = root_document.selectFirst("#mp-charact > div:nth-child(1) > div.mp-charact-intro > div.mp-charact-desc > p");if (reasonStr1!=null){reason = reasonStr1.html();}Element reasonStr2 = root_document.selectFirst("#mp-charact > div > div.mp-charact-intro > div.mp-charact-desc > p:nth-child(1)");if (reasonStr2!=null){reason = reasonStr2.html();}spot.setInfoDetail(reason);System.out.println("推荐理由:" + reason);//开放时间Element openTimeStr = root_document.selectFirst("#mp-charact > div:nth-child(1) > div.mp-charact-time > div > div.mp-charact-desc > p");if (openTimeStr!=null){String openTime = openTimeStr.html();spot.setOpenTime(openTime);System.out.println("开放时间:" + openTime);}//景点详细信息//#mp-charact > div:nth-child(2)//#mp-charact > div:nth-child(2)Element infoDetailStr = root_document.selectFirst("#mp-charact > div:nth-child(2)");//如果没有图片等详细介绍if (infoDetailStr==null){result[0] = spot;result[1] = null;return result;}//得到网页上的景点列表(包含两个样式集合)Elements yy = infoDetailStr.getElementsByClass("mp-charact-event");System.out.println(yy.size());for (int i = 0; i < yy.size(); i++) {SpotDetail spotDetail = new SpotDetail();//得到每一条景点信息Element Info = yy.get(i);//#mp-charact > div:nth-child(2) > div:nth-child(3) > div > img//#mp-charact > div:nth-child(2) > div:nth-child(3) > divElement pictureStr = Info.selectFirst("div > img");if (pictureStr!=null){int index = pictureStr.toString().indexOf(">");String pictureUrl = pictureStr.toString().substring(10,index-1);spotDetail.setPicture(pictureUrl);System.out.println("景点图片地址:" + pictureUrl);}//#mp-charact > div:nth-child(2) > div:nth-child(2) > div > div.mp-event-desc > h3Element titleStr = Info.selectFirst("div > div.mp-event-desc > h3");if (titleStr!=null){String title = titleStr.html();spotDetail.setTitle(title);System.out.println("图片标题:"+title);}Element pictureDetailStr = Info.selectFirst("div > div.mp-event-desc > p");if (pictureDetailStr!=null){String pictureDetail = pictureDetailStr.html();spotDetail.setInfo(pictureDetail);System.out.println("图片详情:"+pictureDetail);}list.add(spotDetail);}result[0] = spot;result[1] = list;return result;}public static void main(String[] args) throws InterruptedException {//测试爬取景点简要信息
//        for (int i = 1;i <= 10;i++){//            //生成1-10的随机数
//            int random = (int)(1+Math.random()*(10-1+1));
//            String str = random+"00";
//            Thread.sleep(Integer.parseInt(str));
//            System.out.println("=======================第"+i+"页=====================");
//            Object result = requestSpotData("西安",i);
//            if (result==null){//                i--;
//            }else{//                System.out.println(result.toString());
//            }
//        }//测试爬取景点详细信息Object[] param = SpotDataCrawler.requestSpotDetailData("陕西历史博物馆",1,"383907200");System.out.println("===========================分割线==================================");if (param!=null){System.out.println(((Spot)param[0]).toString());for (SpotDetail detail:(List<SpotDetail>)param[1]){System.out.println(detail.toString());}}}}

1、对API查询接口的操作

现在进入了重头戏,首先,根据去哪儿景点查询页面获取到景点查询的API接口,如下图:


我们可以看到,去哪儿的景点查询API是这样的
http://piao.qunar.com/ticket/list.htm?keyword=要查询景点的UTF-8编码形式&region=&from=mpl_search_suggest

这里我们要查询的景点是西安,所以得把“西安”这个 keyword 转换为 UTF-8 编码的形式,转换代码如下:

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;/*** @author 小关同学* @create 2021/11/7* 中文转URL*/
public class CodeTransition {public static String stringToASCII(String param) {String result = "";try {result = URLEncoder.encode(param,"utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result;}public static void main(String[] args){System.out.println(stringToASCII("西安"));}}

2、对请求到的页面的解析操作

然后我们来对页面进行解析(这里只做部分讲解),
先将获取到的页面进行解析,如下图:

这里使用 Jsoup.parse 方法进行解析,把 String 类型的页面数据解析成 Document 对象,方便我们下一步操作。

3、HTML 中元素的定位

比如我们要获取景点的名称,如下图:

使用浏览器的调试功能定位到相应的 HTML 代码上,然后右键,选择 Copy 下的 Copy Selector 来获取代码在页面中的位置,如下:
#search-list > div:nth-child(1) > div > div.sight_item_about > h3 > a

然后使用 Element 对象定位元素,进而获取到信息,详细的过程我也不讲太多了,自己看代码去。

三、保存获取到的数据


现在我们获取并解析得到了数据,我们现在可以把它们放到数据库里面去了,这里我使用了 MyBatis 框架进行数据持久化操作,代码我就不贴了,想看完整代码的去我 Github 上面看。

最后

项目地址如下:
Github 地址:https://github.com/guanchanglong/DataCrawler
麻烦各位可否在看代码的时候顺手给一颗星 ^ _ ^,举手之劳感激不尽。

PS:可以到我的个人博客查看更多内容
个人博客地址:小关同学的博客

Java爬取解析去哪儿景点信息相关推荐

  1. JAVA爬取某房产网房源信息

    本文为开发流程记录,排版和言语存在的问题请大家谅解,如有错误或者建议,请指出,谢谢. 之前写过一个使用Python+Beautiful Soup爬故事会,然后存文件的小爬虫,当时没有记录,最近公司不忙 ...

  2. java爬取智联招聘职位信息

    第一次写爬虫,案例比较简单,就在智联招聘网站上爬取职位信息. 技术点:IO流,集合,Jsoup使用,以及前端的知识 代码如下: package com.wty.utils;import java.io ...

  3. Java爬虫 爬取某招聘网站招聘信息

    Java爬虫 爬取某招聘网站招聘信息 一.系统介绍 二.功能展示 1.需求爬取的网站内容 2.实现流程 2.1数据采集 2.2页面解析 2.3数据存储 三.获取源码 一.系统介绍 系统主要功能:本项目 ...

  4. Java 爬虫:是时候 Get 新技能了,使用 Java 爬取网页信息

    如果你想利用自己的技术做出一点有意思的产品来,那么爬虫.算法和 AI 等技术可能是一个不错的突破口.今天,我们就来介绍下使用 Java 爬取页面信息的几种思路. 说起爬虫,自从 Python 兴起之后 ...

  5. 是时候 Get 新技能了:使用 Java 爬取网页信息

    如果你想利用自己的技术做出一点有意思的产品来,那么爬虫.算法和 AI 等技术可能是一个不错的突破口.今天,我们就来介绍下使用 Java 爬取页面信息的几种思路. 说起爬虫,自从 Python 兴起之后 ...

  6. java爬取论坛信息_Java爬取校内论坛新帖

    Java爬取校内论坛新帖 为了保持消息灵通,博主没事会上上校内论坛看看新帖,作为爬虫爱好者,博主萌生了写个爬虫自动下载的想法. 嗯,这次就选Java. 第三方库准备 Jsoup Jsoup是一款比较好 ...

  7. java 爬取京东商品详细信息 价格

    java 爬取京东商品详细信息 价格 获取图中的价格,因为京东的价格有反扒机制,价格通过js加载的,直接通过页面标签是获取不到的,所以我们要去要相应的js. http://p.3.cn/prices/ ...

  8. Python爬虫——使用 lxml 解析器爬取汽车之家二手车信息

    本次爬虫的目标是汽车之家的二手车销售信息,范围是全国,不过很可惜,汽车之家只显示100页信息,每页48条,也就是说最多只能够爬取4800条信息. 由于这次爬虫的主要目的是使用lxml解析器,所以在信息 ...

  9. python爬取2019年计算机就业_2019年最新Python爬取腾讯招聘网信息代码解析

    原标题:2019年最新Python爬取腾讯招聘网信息代码解析 前言 初学Python的小伙们在入门的时候感觉这门语言有很大的难度,但是他的用处是非常广泛的,在这里将通过实例代码讲解如何通过Python ...

最新文章

  1. VLC架构及流程分析
  2. TF之LSTM:基于Tensorflow框架采用PTB数据集建立LSTM网络的自然语言建模
  3. Angular Universal 学习笔记
  4. 项目管理平台(总结篇一)
  5. 解决python读取pickle报错ValueError: unsupported pickle protocol: 5
  6. ApacheCN Java 译文集 20210921 更新
  7. 2020 年,Serverless 将给大前端带来什么样的变化?
  8. 百度北汽结盟:2021年量产L4级无人车
  9. In App Purchases 入门
  10. jQuery微博登录
  11. python, c/c++去掉文本的换行符
  12. 入行自媒体怎么快速变现,详细教程,教你手把手运营
  13. unity vr是加一个摄像机就行吗_梦工厂和皮克斯员工创办的Baobab谈互动叙事:传统动画与VR动画的探索...
  14. SpringBoot Elasticsearch工具类封装
  15. 6657生成调试工程pdkProjectCreate说明
  16. 图形界面介绍Create Pin Blockage
  17. 数据分析和数据挖掘的概念和理念
  18. 移动端flexible解决ipad和ipad Pro不适配
  19. 怎么样玩转信息研究方法指南学习笔记
  20. ArrayList一边遍历一边删除?给大家介绍几种方法

热门文章

  1. 原生CSS设置预加载图片之前的默认背景图
  2. 在Ubuntu 16.04.04 LTS上调研QUIC开源项目minq笔记
  3. 在CentOS 6.9 x86_64的nginx 1.12.2上开启标准模块ngx_http_auth_request_module实录
  4. 在Ubuntu 14.04 64bit上使用dig
  5. 使用AutoCAD 2021创建真实世界的土木设计项目
  6. s-sed(stream editor) 文本填充和编辑 基本使用
  7. 内核方式挂载cephfs
  8. opencv 1 图像载入、显示和输出
  9. Java虚拟机垃圾收集算法
  10. Python笔记:字典的fromkeys方法创建的初始value同内存地址问题