java爬虫12306_java爬虫12306,爬取所有的站点和车次,并导入postgreSQL数据库
準備
安裝postgreSQL數據庫,和可視化工具pgadmin3,或者其他數據庫
實現功能,抓取12306全部的站點,并實現通過站點查詢出所有經過次站點的車次,通過車次查出次列車經過的城市
分析
分析12306,找合適的接口,最符合要求的是查詢車次的這張頁面,但是有驗證碼,無形增加了難度
經過分析,合適的頁面是車票預訂的頁面,查詢兩個站點直接的車次,用火狐自帶的f12工具,點擊查詢清晰的看到只有一條get請求
再看響應的內容,json,根據經驗這是我們想要東西
通過這條鏈接,我們可以得到兩站點之間的車次信息,我們只需要車次的名稱就好了,通過字符串或者正則都可以,正則不太熟,我用的是字符串
分析怎么才能把全國的站點和車次都抓取到,并且實現彼此查詢的功能?
站點和城市多對多關系,理應建立三張表,用中間表關聯.最后放棄了三表的想法,使用一張表聯合主鍵實現
只要獲取到全國的城市站點,通過for循環兩兩測試,不就可以得到全部的火車車次了,并且兩列都是主鍵,同時還解決了兩個城市之間車次重復的問題,
1 CREATE TABLE public.t_city2(3 city_name character varying(64) NOT NULL,4 train_num character varying(64) NOT NULL,5CONSTRAINT t_city_pkey PRIMARY KEY (city_name, train_num)6 )
下一步要找到全國的火車站點
從頁面的一條js中,找到一條連接?https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9049
一條json數據
接下來,開始寫程序,解析數據
數據庫鏈接(換了臺電腦改成了mysql數據庫
publicclass DbUtil {
private static final String DBDRIVER="com.mysql.jdbc.Driver";
private static final String url= "jdbc:mysql://127.0.0.1:3306/lianxi";
private static final StringUSER ="root";
private static final String PASSWORD= "123";publicConnection getConn() throws ClassNotFoundException, SQLException {
Class.forName(DBDRIVER);
Connection conn= DriverManager.getConnection(url, USER, PASSWORD);returnconn;
}publicvoid closeConn(Connection conn) throws SQLException {if (conn != null) {
conn.close();
}
}public static void main(String args[]) {
DbUtil dbUtil=new DbUtil();
Connection conn=null;
try {
conn=dbUtil.getConn();
System.out.println(conn+"數據庫連接成功");
} catch (ClassNotFoundException|SQLException e) {
e.printStackTrace();
System.out.println("失敗");
}finally {
try {
dbUtil.closeConn(conn);
} catch (SQLException e) {// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
數據庫用sqlyog直接建好表
創建一個CityMd類
public classCityMd {privateString cityName;privateString trainName
、、、、、、
創建方法,實現數據庫數據添加
public classDbChange {public intadd(Connection conn,CityMd cityMd) {
String sql="insert into t_city values(?,?,null)";int n=0;try{
PreparedStatement p=conn.prepareStatement(sql);
p.setString(1, cityMd.getCityName());
p.setString(2, cityMd.getTrainName());
n=p.executeUpdate();
p.close();
}catch(SQLException e) {//TODO Auto-generated catch block//e.printStackTrace();
}finally{try{
conn.close();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}returnn;
}
}
抓取全部的站點,返回一個二維數組,通過之前的url,發現參數有出發地和目的地,并且是字母編號的形式,所以把城市和編號同時抓取下來
分析json數據,每個成熟以@分隔,其次又以|分隔,所以可以用字符串分隔,正則很方便,不過熟悉沒用
public classCityUtil {public String[][] getCity() throwsException{String cityurl="https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9048";
HttpGet httpget=newHttpGet(cityurl);
CloseableHttpClient httPclient=HttpClients.createDefault();
CloseableHttpResponse Response=httPclient.execute(httpget);
HttpEntity entity=Response.getEntity();
String result=EntityUtils.toString(entity,"utf-8");//System.out.println("請求結果"+result);
int l1=result.indexOf("'");int l2=result.length();
String city=result.substring(l1+1, l2);
String[] c=city.split("@");//導入二維數組
int l=c.length-1;
String[][] str=new String[l][2];for(int i=1;i
String[] cc=c[i].split("[|]");//System.out.println(cc[1]+" "+cc[2]);
str[i-1][0]=cc[1];
str[i-1][1]=cc[2];
}
returnstr;
}
}
這樣就得到了一個全部站點的數組
接下來寫兩地間車次的方法,兩地之間肯定會有很多火車,所以返回數組
public classGetUtil {publicString[] getList(String url) {
CloseableHttpClient httPclient=HttpClients.createDefault();
HttpGet httpgett=newHttpGet(url);CloseableHttpResponse Response;
String result1=null;try{
Response=httPclient.execute(httpgett);
HttpEntity entity=Response.getEntity();
result1=EntityUtils.toString(entity,"utf-8");//System.out.println("請求結果"+result1);
} catch(ClientProtocolException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}JSONObject jSONObject=JSONObject.fromObject(result1);
Object listObject=jSONObject.get("data");
jSONObject=JSONObject.fromObject(listObject);
JSONArray json=jSONObject.getJSONArray("result");//存放火車列次的數組
String[] strs=newString[json.size()];for (int i = 0; i < json.size(); i++) {
String str=json.getString(i);
String[] arr=str.split("[|]");
strs[i]=arr[3];
}
returnstrs;
}
}
剩下的就是,整合起來,開始測試
get請求的url參數有四個,第一個是時間,第二個是出發點,第三個是目的地,最后一個成人票這個不是關鍵直接寫死
時間也寫死,但是有一個問題就是,每天的車次肯定會有差異,這樣只看一看的車次數據肯定不精準。想一個辦法就是,跑完數據之后,換個時間再抓一次,反正重復的自己會跳過去、、、、、、、
一定注意要加時間間隔,開始跑沒有加,沒一會12306就給限制請求了
GetUtil getUtil=new GetUtil(); //兩地之間的車次
DbUtil dbUtil=new DbUtil(); //獲取conn
Zhuanhua zh=new Zhuanhua(); //集合轉數組
String trainurl="https://kyfw.12306.cn/otn/leftTicket/queryO?";
String train_date="leftTicketDTO.train_date=2018-03-20";
String from_station=null;
String to_station=null;
String newurl=null;//獲取conn//Connection conn=dbUtil.getConn();
DbChange db=newDbChange();//獲取全部城市和它代號
CityUtil cu=newCityUtil();
String[][] str=cu.getCity();//循環所有情況
int count=0;
Connection conn=null;for(int i=10; i
Thread.sleep(1000);for(int j=0; j
Random r= newRandom();int nnn=r.nextInt(6);
Thread.sleep(nnn*2000);//拼接鏈接請求鏈接
from_station=str[i][1];
to_station=str[j][1];//排除出發和目的是一個
if(from_station.equals(to_station)) {continue;
}
newurl=trainurl+train_date+"&leftTicketDTO.from_station="
+from_station+"&leftTicketDTO.to_station="+to_station+"&purpose_codes=ADULT";//調用方法,獲取兩地之間的火車數組
String[] ss=getUtil.getList(newurl);for(int k=0;k
CityMd cityMd=new CityMd(str[i][0],ss[k]);
conn=dbUtil.getConn();int nn=db.add(conn, cityMd);
System.out.println("運行第"+k+"次出發地:"+str[i][0]+"==>目的地:"+str[j][0]);
count+=nn;
}
}
} System.out.println("共計導入數據"+count);
最終數據庫實現
總結
以上是生活随笔為你收集整理的java爬虫12306_java爬虫12306,爬取所有的站点和车次,并导入postgreSQL数据库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 无线连接 服务器,服务器无线远程连接
- 下一篇: 安卓毕业设计源码,基于Android的商