手上的项目有个业务要求:采集某市的酒店余房情况,淡季一天更新一次,旅游高峰期半小时更新一次。
正常情况是要有个接口,酒店相关人员负责定时发送酒店余房情况,但这时候我觉得,可以用爬虫玩玩,反正也不会爬虫,学下爬虫也不亏,项目是用java语言SSM框架写的,所以想尝试用java语言。
上网了解了一下,java基础爬虫语法很简单。
//首先输入要爬的网页
URL url = new URL("https://hotels.ctrip.com/hotel/guangzhou32");
//建立连接
URLConnection urlConnection = url.openConnection();
HttpURLConnection connection = (HttpURLConnection) urlConnection;
// 建立一个读取流从连接中读取
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
String current; // 爬虫爬下来的每一行数据
while ((current = in.readLine()) != null) {
System.out.pritln(current);
}
为什么爬携程的?我爬了美团的好像是不让我爬,405权限问题blabla的。
那么这样爬到了什么呢?
爬到的是目标网址的前端界面源码,我们可以在网址输入目标网址去看看,按F12->Sources->双击guangzhou32
刚刚在java Console输出的正是这三千多行前端代码。接下来我们可以对这三千多行前端代码进行处理。
在我孜孜不倦的浏览(瞎几把看)下,终于发现了隐藏在这三千多行中的好东西。
第2857行是这样的
这一行很长,有25个hotelid,25个amount,也正是这一个界面给我们显示的25家酒店的信息,amount是数量的意思,这时候我猜想这个amout会不会就是酒店剩余房间,所以我隔了两分钟去刷新一下,果然amount变少了几个(话说八百多间空房,这一定是家大酒店吧)。所以说这一行有用,得做笔记。
第2977行是这样的
这一行可不简单,也是25家酒店的信息,包含酒店id,酒店名,酒店纬度lat,酒店经度lon(有经纬度就可以调用百度地图了嘻嘻),酒店详细信息url(相当于你点这家酒店打开的网页),酒店图片url(只有一张,是封面),酒店地址address,酒店评分score(就是4.8,4.9那玩意儿),住过该酒店的人推荐率dpscore(话说为什么要用dp,我总觉得dp是动态规划),酒店评论数dpcount,酒店星级star,酒店星级描述stardesc,酒店名缩写shortName。还挺多信息hhhh。这里我放一家酒店的例子出来。
{"id":"470094","name":"成都瑞城名人酒店","lat":"30.67645069","lon":"104.0723634",
"url":"/hotel/470094.html?isFull=F#ctm_ref=hod_sr_map_dl_txt_1",
"img":"//dimg13.c-ctrip.com/images/200t15000000xdh2l1ECD_R_300_225.jpg",
"address":"青羊区人民中路二段68号。 ( 文殊院、骡马市商业区天府广场、盐市口商业区)",
"score":"4.8","dpscore":"93","dpcount":"24653","star":"hotel_stars04","stardesc":"国家旅游局评定为四星级",
"shortName":"瑞城名人","isSingleRec":"false"},
接下来我们就提取信息好了,这一步我是自己写的字符串查找,效率挺低的,但这不是重点,先用着!
话不多说 直接上源码,提取相关信息到List >
package reptiletest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
public class Reptile {
public static void main(String[] args) throws IOException {
System.out.println(getHotelInfoByCity("beijing1"));
}
public static List> getHotelInfoByCity(String city) throws IOException {
//首先输入要爬的网页
URL url = new URL("https://hotels.ctrip.com/hotel/" + city);
//建立连接
URLConnection urlConnection = url.openConnection();
HttpURLConnection connection = (HttpURLConnection) urlConnection;
// 建立一个读取流从连接中读取
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
String current; // 爬虫爬下来的每一行数据
boolean hadfindhtllst = false; // 是否找到关键行1
boolean hadfindhotelPositionJSON = false; // 是否找到关键行2
char[] htllist = new char[10005]; // 内含hotelid,酒店剩余房间数
String roomnum = null; // 存放目标char[]->String
char[] hotelPositionJSON = new char[50005]; // 内涵hotelid,酒店全称,酒店地址,酒店精确经纬度
String hotelinfo = null; // 存放目标char[]->String
while ((current = in.readLine()) != null) {
if (hadfindhtllst == false) { // 如果还没找到关键行
if (current.length() < 19)
continue; // 如果该行长度小于19 直接跳过
current.getChars(12, 19, htllist, 0);
if (charsequals(htllist, "htllist", 7)) {
hadfindhtllst = true; // 找到了关建行
current.getChars(12, current.length(), htllist, 0);
roomnum = new String(htllist, 0, current.length() - 12);
}
}
if (hadfindhotelPositionJSON == false) {// 如果还没找到关键行
if (current.length() < 25)
continue; // 如果改行长度小于25 直接跳过
current.getChars(8, 25, hotelPositionJSON, 0);
if (charsequals(hotelPositionJSON, "hotelPositionJSON", 17)) {
hadfindhotelPositionJSON = true; // 找到了关建行
current.getChars(8, current.length(), hotelPositionJSON, 0);
hotelinfo = new String(hotelPositionJSON, 0, current.length() - 8);
}
}
}
// 经过爬虫后,有用的信息只有 String roomnum(内含酒店id,酒店剩余房间数)
// String hotelinfo(内含酒店id,酒店全名,酒店位置,酒店经纬度,酒店评分)
// System.err.println(roomnum);
// System.err.println(hotelinfo);
if(roomnum == null || hotelinfo == null)
return null;
List> lists = new ArrayList>();
// 添加酒店id,酒店城市,酒店余房数量
for (int i = 0, cs = 0, now = 0; i < roomnum.length(); i++) { // cs为"出现的次数
if (roomnum.charAt(i) == '\"') {
cs++;
if (cs % 8 == 3) {
List list = new ArrayList();
String temp = "";
while (roomnum.charAt(++i) != '\"') temp += roomnum.charAt(i);
i--;
list.add(temp);
if(city.equals("chengdu28")) {
list.add("成都");
}
else if(city.equals("guangzhou32")) {
list.add("广州");
}
else if(city.equals("beijing1")) {
list.add("北京");
}
else {
list.add("未知城市");
}
lists.add(list);
}
if (cs % 8 == 7) {
String temp = "";
while (roomnum.charAt(++i) != '\"') temp += roomnum.charAt(i);
i--;
lists.get(now++).add(temp);
}
}
}
// 添加酒店名,纬度lat,经度lon,酒店照片,酒店地址,酒店评分,酒店接待能力(星级)
for (int i = 0, cs = 0, now = 0; i < hotelinfo.length(); i++) {
if(hotelinfo.charAt(i) == '\"') {
cs++;
if(cs%56 == 7 || cs%56 == 11 || cs%56 == 15
|| cs%56 == 23 || cs%56 == 27
|| cs%56 == 31 || cs%56 == 43) {
String temp = "";
while (hotelinfo.charAt(++i) != '\"') temp += hotelinfo.charAt(i);
i--;
lists.get(now).add(temp);
}
if(cs%56 == 43)now++;
}
}
return lists;
}
// 查询char[]型 与 String型是否相等
public static boolean charsequals(char[] left, String right, int length) {
boolean result = true;
for (int i = length - 1; i >= 0; i--) {
result &= left[i] == right.charAt(i);
}
return result;
}
}
之后我把这些信息放到数据库(代码没放出来)里面,由于业务要求是定时采集信息,所以下一篇我准备写下SSM的定时器,就几行代码。
ps:https://hotels.ctrip.com/hotel/guangzhou32 是广州酒店信息
https://hotels.ctrip.com/hotel/chengdu28 是成都酒店信息
https://hotels.ctrip.com/hotel/beijing1 是北京酒店信息(不愧是首都!)
其他的可以自己搞搞,不麻烦。