最开始的数据获取也有两个思路 虽然我的数据不是这么来的
一 、http://www.zuobus.com/line-overview.php?c=2 这个网站有所有的上海的公交线路
二、百度poi 你去遍历整个上海去搜索公交线路也会有的 (不详说了)
今天公司给我一个数据列表 为上海的公交线路,让我爬取。我主要是想记录一下爬取的思路,以备后期查看。
这是我的原始数据话不多说开始上数据。
首先我找到了百度,去搜索01线。因为第一时间就找到了百度嘛。
我发现第一时间还是给返回了线路和公交站,但是我发现其实后台返回的还是百度地图的数据。那为了更具体的去搜索这个数据我就又找到了百度地图。
通过这个也面我发现还是找到了公交的线路 默认给我返回他们优先级高的线路列表了。
到这我就会想起我最开始的本质了 ,爬虫嘛 。无非就是看数据请求。我就f12打开了chrome浏览器的开发者调试功能呢。这个简单和大家介绍一下。我也不是很专业,先点击红色clear一下。然后在重新搜索一下01线。这就能看见你本次所有操作浏览器都进行了哪些请求了。
其实我看见有两条请求是我比较关注的,一是看请求规律,二是看请求数据的返回内容。我看见其实还是有大部分的返回数据让我很熟悉的。请求调试如果不会的就请百度chromef12开发者调节吧。
至此发现确实有返回数据 ,但是它是怎么来的呢,这也是一个问题。比如这请求链接是get请求。参数最明显的就是这个uid了。
数据请求链接: http://map.baidu.com/?qt=bsl&tps=&newmap=1&uid=96fc636df35285eb841541ed&c=289
我突然想说这个uid是咋么来的,不可能是我直接打01线,就编码发过去的吧,这个思路不可能。那就是先把01线这个搜索条件发过去了。然后服务器返回数据,让我浏览器在重新请求的逻辑。那我们就找一找前面是否有这么一个请求给我返回来这个uid。
第一条请求
这个是我浏览器第一条请求 你会发现里面有大量的信息,比如经纬度,其实每次请求都会带来我们大量的信息传给服务器做数据积累和分析,大数据就是这么来的。不多说了 溜了溜了~~~
看了看其实这个请求最关键的就是&wd=01%E7%BA%BF这个参数 这个明显就是我的01线的请求信息了。估计接触过百度的请求的都应该看得出来了。这都是经验所得,其实看01%E7%BA%BF 就明显是编码的转换,URLdecode把中文转成了编码发的请求。
这时候我们该看看他返回的数据了,返回的数据一看都是json都太麻烦了,这时候怎么办呢。其实是有在线的json可视化工具的。超级方便~~~http://www.bejson.com/ 这个网站的json工具都超级好用的。我一般拿这个网站做json格式化、视图、java实体类的转换 自动生成 贼棒。
我们会看见 其实这次搜索返回12条搜索记录的在content的目录层级下面。为什么会找到content呢 诶 其实也是经验所得,你们也可以都翻一翻,这些如果你想做爬虫的话都是必备的。
我发现第一条就是我想要的线路 ,而第二条又是我想要的线路。(程序员的第一条 默认指的是0 第二条才是 1)
里面的uid 和name全部都有。那么我将名字给变成uid的逻辑就有了,这里就需要一点简单的判断了。那么返回10个,我怎么确定是我想要的呢,那么我就或许数据 然后整体遍历前3个数据。 如果带有线路名称 或者是终点站和启发站的话。我默认就是对的。这个是从文本读取数据 到在写入另外一个文本的逻辑。 读取线路名称 根据线路名称去爬取。但是这么直接爬取是不是会有问题。我经常调用百度数据api,我直接用poi检索的方式去搜不就好了嘛。
http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-placeapi 这个是百度api文档 自己看哈
也就是以下的链接方式。
http://api.map.baidu.com/place/v2/search?query="+name+"&tag=公交线路®ion=上海&output=json&ak=.......
AK我没有放出来 你们自己用自己的哈 这个是申请的。话不多说贴代码了
//是根据文件线路名称 转出UID的方法
public void NametoUID() throws Exception{
//读取的数据流
FileInputStream inputStream = new FileInputStream("Baidu_Busline/busline.txt");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//写出的数据流
String url01 = "Baidu_Busline/busline_result2.txt";
FileWriter qwe= new FileWriter(url01,true);
String str = null;
//按行读取
while ((str=bufferedReader.readLine())!=null) {
String id = str.split("\\|")[0];
String name = str.split("\\|")[1];
String station1=str.split("\\|")[2];
String station2=str.split("\\|")[3];
//爬虫用的jsoup
Document doc = Jsoup.connect("http://api.map.baidu.com/place/v2/search?query="+name+"&tag=公交线路®ion=上海&output=json&ak=xxxxxxxx自己的ak 申请的哦 ")
.header("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
.header("Accept-Encoding", "gzip, deflate")
.header("Accept-Language", "zh-CN,zh;q=0.9")
.header("Connection", "gzip, deflate")
.header("Referer", "http://map.baidu.com/?newmap=1&ie=utf-8&s=s%26wd%3D01%E8%B7%AF")
.header("Host", "map.baidu.com")
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
.ignoreContentType(true)
.ignoreHttpErrors(true)
.timeout(300000).get();
System.out.println(id+"|"+doc.text());
//这里用的ali的 fastjson 去将json转换成了对象
BaiDu baidu = JSON.parseObject(doc.text(), BaiDu.class);
int num =1;
if (baidu.getStatus().equals("0")) {
List<BaiDuList> results = baidu.getResults();
//这里一般前3个都是优先级高的 遍历那么多没用
// 但是有时候返回结果没有3个 就取最大值
int list_num=results.size()>3?3:results.size();
for (int i = 0; i < list_num; i++) {
BaiDuList baidulist1=results.get(i);
String name2=baidulist1.getName();
String uid =baidulist1.getUid();
System.out.println(name+"|||"+name2);
//这个是判断返回的数据名称是否含有 线路名 启点名称 终点名称 只要含有一个我就认为是对的
if (name2.contains(name)||name2.contains(station1)||name2.contains(station2)) {
qwe.write(id+"|"+uid+"|"+name2+"\n");
System.out.println(id+"|"+name2);
qwe.flush();
}
}
}
}
}
这部分就是出来了一份带有线路uid的文本文件了。
那有个uid就好办多了 ,因为uid是百度内部的一个唯一值,这部分就没啥大问题了。那就回到我最开始的思路了
http://map.baidu.com/?qt=bsl&tps=&newmap=1&uid=96fc636df35285eb841541ed&c=289
c=289 是上海 要找到 自己的城市的id
这个链接是根据uid去爬取数据的。
返回的数据是整个样子的,这个是正常的 。 我只想说一种情况。我之前一部分爬取会出现下面这样的数据。这个就明显不是线路,而是一个poi点了。这部分数据是有问题的,那怎么去除呢。我根据这个uid区搜索发现。后台返回的数据是没有stations这个字段的,那就可以根据这个字段去判断是否为线路。
22|63e1b82e5546292858109355|156路调度室
话不多说也上代码了 这部分代码是从文件根据uid去搜索出线路列表 并记录的一个逻辑。.
public static void UIDtoBusline() throws Exception{
try {
FileInputStream inputStream = new FileInputStream("Baidu_Busline/busline_result2.txt");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String url01 = "Baidu_Busline/busline_result3.txt";
FileWriter qwe= new FileWriter(url01,true);
String url02 = "Baidu_Busline/data2.txt";
FileWriter qwe2= new FileWriter(url02,true);
String str = null;
while ((str=bufferedReader.readLine())!=null) {
String id =str.split("\\|")[0];
String uid =str.split("\\|")[1];
String linename =str.split("\\|")[2];
Document doc = Jsoup.connect("http://map.baidu.com/?qt=bsl&tps=&newmap=1&uid="+uid+"&c=289")
.header("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
.header("Accept-Encoding", "gzip, deflate")
.header("Accept-Language", "zh-CN,zh;q=0.9")
.header("Connection", "gzip, deflate")
.header("Referer", "http://map.baidu.com/?newmap=1&ie=utf-8&s=s%26wd%3D01%E8%B7%AF")
.header("Host", "map.baidu.com")
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
.ignoreContentType(true)
.ignoreHttpErrors(true)
.timeout(300000).get();
System.out.println(uid+"|"+doc.text());
qwe2.write(id+"|"+uid+"|"+linename+"|"+doc.text()+"\n");
qwe.flush();
Bus_name_allline2 lines=JSON.parseObject(doc.text(), Bus_name_allline2.class);
if (!doc.text().contains("stations")) {
continue;
}
List<Stations> Station_list=lines.getContent().get(0).getStations();
LAT_LNG_convert convert = new LAT_LNG_convert();
for (int i = 0; i < Station_list.size(); i++) {
Stations stations=Station_list.get(i);
//System.out.println(stations.getGeo().split("\\|")[2]);
Map<String, Double> map = convert.convertMC2LL(Double.parseDouble(stations.getGeo().split("\\|")[2].split(",")[0]), Double.parseDouble(stations.getGeo().split("\\|")[2].split(",")[1].replace(";", "")));
qwe.write(id+"|"+uid+"|"+linename+"|"+(i+1)+"|"+stations.getName()+"|"+map.get("lng")+"|"+map.get("lat")+"\n");
qwe.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}catch (Error e) {
e.printStackTrace();
}
}
这就是我们要生成的数据了。
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|1|蓝村路南泉路|121.53052300417657|31.21774184729144
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|2|浦建路东方路|121.53038798885841|31.214036873792868
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|3|浦东南路宁阳路|121.52507900329444|31.21830282680859
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|4|浦东南路浦电路|121.52527698982885|31.22612685127513
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|5|浦东南路商城路|121.5213740320491|31.23690288296319
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|6|世纪大道浦东南路|121.51826598481512|31.24036489105574
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|7|延安东路浙江中路|121.4862190255772|31.235289895881795
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|8|延安中路石门一路|121.47229897322514|31.230067875583046
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|9|延安中路陕西北路|121.46226499050923|31.23030286040807
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|10|延安西路华山路|121.45092002073207|31.227068833787044
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|11|延安西路镇宁路|121.44503602981116|31.224458865865845
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|12|江苏路延安西路|121.43864898780467|31.222714883765736
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|13|江苏路愚园路|121.43686100051761|31.227570855178012
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|14|曹杨路谈家渡路|121.42826403712601|31.239303863376062
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|15|曹杨路白玉路|121.42611304458238|31.242830850110302
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|16|曹杨路南石路|121.41509101563605|31.252752845318675
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|17|曹杨路铜川路|121.41282297388446|31.260153868914298
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|18|曹杨路芝川路|121.411353974884|31.26493088368057
1|96fc636df35285eb841541ed|01线(蓝村路南泉路-上海西站)|19|上海西站|121.41019498111503|31.26795587339262
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|1|上海西站|121.4103510167821|31.267931875109614
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|2|曹杨路芝川路|121.41077798139098|31.266363871041225
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|3|曹杨路铜川路|121.41256399240592|31.26061087641531
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|4|曹杨路南石路|121.41448196450028|31.254302864194276
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|5|武宁新村|121.42791603357145|31.245109882471482
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|6|曹杨路谈家渡路|121.42796499122176|31.2396768376672
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|7|江苏路愚园路|121.43588795598922|31.22897785941288
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|8|江苏路延安西路|121.43852897418854|31.222766840851296
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|9|延安西路镇宁路|121.44549102155197|31.22451584003169
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|10|延安西路华山路|121.45102503264619|31.22687382982635
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|11|延安中路陕西北路|121.46130595954678|31.22992290107667
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|12|延安中路石门一路|121.47280400058285|31.229633877748295
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|13|延安东路浙江中路|121.4870019886597|31.235419886874126
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|14|世纪大道浦东南路|121.5172749741765|31.24030584247235
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|15|浦东南路潍坊路|121.52477995739018|31.2274798384288
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|16|浦东南路浦电路|121.52537697123209|31.225163860826832
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|17|浦东南路宁阳路|121.52491901508313|31.218249863568214
1|14c86f02f26a751cbda240ed|01线(上海西站-蓝村路南泉路)|18|蓝村路南泉路|121.53046596177667|31.217743854656018
这就是我最后生成的数据 有站点名称 站点的顺序编号 其实是分为两个方向的 他们的uid不同的。后面是经纬度。
这个差点忘了中间有两个坑,我根据geo去确定站点的经纬度 就是最后一个为站点的经纬度13497373.52,3633940.95。但是这个如果是经纬度的话。你会发现和正常的不同。 这个是墨卡托坐标,我也是在一个群里一个大哥告诉我的 (再此谢谢袋子大哥)。 然后会有经纬度的转换的。 我最后贴一部分代码 也是网上找到的 。你们自己看。那个是经纬度转换的逻辑。
package busline.bean1;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List;
public class LAT_LNG_convert {
private static Double EARTHRADIUS = 6370996.81;
private static Double[] MCBAND = {12890594.86, 8362377.87, 5591021d, 3481989.83, 1678043.12, 0d};
private static Double[] LLBAND = {75d, 60d, 45d, 30d, 15d, 0d};
private static Double[][] MC2LL = {{1.410526172116255e-8, 0.00000898305509648872, -1.9939833816331, 200.9824383106796, -187.2403703815547, 91.6087516669843, -23.38765649603339, 2.57121317296198, -0.03801003308653, 17337981.2}, {-7.435856389565537e-9, 0.000008983055097726239, -0.78625201886289, 96.32687599759846, -1.85204757529826, -59.36935905485877, 47.40033549296737, -16.50741931063887, 2.28786674699375, 10260144.86}, {-3.030883460898826e-8, 0.00000898305509983578, 0.30071316287616, 59.74293618442277, 7.357984074871, -25.38371002664745, 13.45380521110908, -3.29883767235584, 0.32710905363475, 6856817.37}, {-1.981981304930552e-8, 0.000008983055099779535, 0.03278182852591, 40.31678527705744, 0.65659298677277, -4.44255534477492, 0.85341911805263, 0.12923347998204, -0.04625736007561, 4482777.06}, {3.09191371068437e-9, 0.000008983055096812155, 0.00006995724062, 23.10934304144901, -0.00023663490511, -0.6321817810242, -0.00663494467273, 0.03430082397953, -0.00466043876332, 2555164.4}, {2.890871144776878e-9, 0.000008983055095805407, -3.068298e-8, 7.47137025468032, -0.00000353937994, -0.02145144861037, -0.00001234426596, 0.00010322952773, -0.00000323890364, 826088.5}};
private static Double[][] LL2MC = {{-0.0015702102444, 111320.7020616939, 1704480524535203d, -10338987376042340d, 26112667856603880d, -35149669176653700d, 26595700718403920d, -10725012454188240d, 1800819912950474d, 82.5}, {0.0008277824516172526, 111320.7020463578, 647795574.6671607, -4082003173.641316, 10774905663.51142, -15171875531.51559, 12053065338.62167, -5124939663.577472, 913311935.9512032, 67.5}, {0.00337398766765, 111320.7020202162, 4481351.045890365, -23393751.19931662, 79682215.47186455, -115964993.2797253, 97236711.15602145, -43661946.33752821, 8477230.501135234, 52.5}, {0.00220636496208, 111320.7020209128, 51751.86112841131, 3796837.749470245, 992013.7397791013, -1221952.21711287, 1340652.697009075, -620943.6990984312, 144416.9293806241, 37.5}, {-0.0003441963504368392, 111320.7020576856, 278.2353980772752, 2485758.690035394, 6070.750963243378, 54821.18345352118, 9540.606633304236, -2710.55326746645, 1405.483844121726, 22.5}, {-0.0003218135878613132, 111320.7020701615, 0.00369383431289, 823725.6402795718, 0.46104986909093, 2351.343141331292, 1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45}};
public static void main(String[] args) {
Map<String, Double> map = convertMC2LL(Double.parseDouble("13520260.28"), Double.parseDouble("3649283.66"));
System.out.println("百度坐标:"+map);
Double lng = map.get("lng");
Double lat = map.get("lat");
// LngLat lngLat_bd = new LngLat(lng, lat);
// System.out.println("高德坐标:"+bd_decrypt(lngLat_bd));
System.out.println(lng+"|"+lat);
}
/**
* 解析Jeo数据
* @param mocator
*/
public static ArrayList<String> parseJeo(String mocator) {
ArrayList<String> mocatorList = new ArrayList<String>();
if (null == mocator) return null;
/* 拆分数据 */
String[] geos = mocator.split("\\|");
int n = Integer.parseInt(geos[0]);
String center = geos[1];
String polylineMoca = geos[2]; //墨卡托坐标
String[] plm = polylineMoca.split("\\;");
/* 获取墨卡托边界 */
String geo = null;
if (n == 4) {
for (int i = 0; i < plm.length; i++) {
String[] geoPaths = plm[i].split("\\-");
if (geoPaths[0].equals("1")) {
geo = geoPaths[1];
}
}
}
// 墨卡托坐标解析
String[] geoPolyline = geo.split("\\,");
for (int i = 0; i < geoPolyline.length; i += 2) {
mocatorList.add(geoPolyline[i] + "#" + geoPolyline[i + 1]);
}
return mocatorList;
}
/**
* 墨卡托坐标转经纬度坐标
* @param x
* @param y
* @return
*/
public static Map<String, Double> convertMC2LL(Double x, Double y) {
Double[] cF = null;
x = Math.abs(x);
y = Math.abs(y);
for (int cE = 0; cE < MCBAND.length; cE++) {
if (y >= MCBAND[cE]) {
cF = MC2LL[cE];
break;
}
}
Map<String,Double> location = converter(x, y, cF);
location.put("lng",location.get("x"));
location.remove("x");
location.put("lat",location.get("y"));
location.remove("y");
System.out.println(location.get("x")+"|"+location.get("y"));
return location;
}
/**
* 墨卡托坐标转经纬度坐标
* @param x
* @param y
* @return
*/
public static Map<String, Double> convertMC2LL2(Double x, Double y) {
Double[] cF = null;
x = Math.abs(x);
y = Math.abs(y);
for (int cE = 0; cE < MCBAND.length; cE++) {
if (y >= MCBAND[cE]) {
cF = MC2LL[cE];
break;
}
}
Map<String,Double> location = converter(x, y, cF);
location.put("lng",location.get("x"));
location.remove("x");
location.put("lat",location.get("y"));
location.remove("y");
System.out.println(location.get("x")+"|"+location.get("y"));
return location;
}
/**
* 经纬度坐标转墨卡托坐标
* @param lng
* @param lat
* @return
*/
private static Map<String, Double> convertLL2MC(Double lng, Double lat) {
Double[] cE = null;
lng = getLoop(lng, -180, 180);
lat = getRange(lat, -74, 74);
for (int i = 0; i < LLBAND.length; i++) {
if (lat >= LLBAND[i]) {
cE = LL2MC[i];
break;
}
}
if (cE!=null) {
for (int i = LLBAND.length - 1; i >= 0; i--) {
if (lat <= -LLBAND[i]) {
cE = LL2MC[i];
break;
}
}
}
return converter(lng,lat, cE);
}
private static Map<String, Double> converter(Double x, Double y, Double[] cE) {
Double xTemp = cE[0] + cE[1] * Math.abs(x);
Double cC = Math.abs(y) / cE[9];
Double yTemp = cE[2] + cE[3] * cC + cE[4] * cC * cC + cE[5] * cC * cC * cC + cE[6] * cC * cC * cC * cC + cE[7] * cC * cC * cC * cC * cC + cE[8] * cC * cC * cC * cC * cC * cC;
xTemp *= (x < 0 ? -1 : 1);
yTemp *= (y < 0 ? -1 : 1);
Map<String, Double> location = new HashMap<String, Double>();
location.put("x", xTemp);
location.put("y", yTemp);
return location;
}
private static Double getLoop(Double lng, Integer min, Integer max) {
while (lng > max) {
lng -= max - min;
}
while (lng < min) {
lng += max - min;
}
return lng;
}
private static Double getRange(Double lat, Integer min, Integer max) {
if (min != null) {
lat = Math.max(lat, min);
}
if (max != null) {
lat = Math.min(lat, max);
}
return lat;
}
}
到此就告一段落了,诶 我不是专业的爬虫 我是大数据开发的,大家如果有什么问题可以私聊我。QQ454038065 带上CSDN
地理相关的大数据的活可以找我 人口热力什么的 宜出行,承接分析的工作和爬取的工作 谢谢啦