用Python实现一个面向主题的网络爬虫程序,并完成以下内容:
(注:每人一题,主题内容自选,所有设计内容与源代码需提交到博客园平台)
一、主题式网络爬虫设计方案(15分)
1.主题式网络爬虫名称
爬取微博博主的信息和博文
2.主题式网络爬虫爬取的内容与数据特征分析
2.主题式网络爬虫爬取的内容与数据特征分析
对指定的微博博主信息与博文分类爬取
3.主题式网络爬虫设计方案概述(包括实现思路与技术难点)
3.主题式网络爬虫设计方案概述(包括实现思路与技术难点)
先爬取页面的HTML,其中城市对应简称是需要另外的接口查询得出,然后使用正则表达式,取出城市名和简称的值
二、主题页面的结构特征分析(15分)
1.主题页面的结构特征
1.主题页面的结构特征
https://kyfw.12306.cn/otn/login/init
2.Htmls页面解析
3.节点(标签)查找方法与遍历方法
(必要时画出节点树结构)
三、网络爬虫程序设计(60分)
爬虫程序主体要包括以下各部分,要附源代码及较详细注释,并在每部分程序后面提供输出结果的截图。
1.数据爬取与采集
爬虫程序主体要包括以下各部分,要附源代码及较详细注释,并在每部分程序后面提供输出结果的截图。
总代码
1 from login import Login 2 import os 3 import json 4 import time 5 from collections import deque, OrderedDict 6 7 class Station: 8 """ 查询车票信息 """ 9 10 def __init__(self): 12 self.session = Login.session 13 self.headers = Login.headers 14 15 16 def station_name_code(self): 17 """ 18 功能:获取每个站点的名字和对应的代码,并保存到本地 19 :return: 无 20 """ 21 filename = 'station_name.txt' 22 23 url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js' 24 resp = self.session.get(url, headers=self.headers) 25 if resp.status_code == 200: 26 print('station_name_code():获取站点信息成功!') 27 with open(filename, 'w') as f: 28 for each in resp.text.split('=')[1].split('@'): 29 if each != "'": 30 f.write(each) 31 f.write('\n') 32 else: 33 print('station_name_code() error! status_code:{}, url: {}' 34 .format(resp.status_code, resp.url)) 35 36 def save_station_code(self, filename): 37 """ 38 功能:从站点文件中提取站点与其对应的代码,并保存到文件中 39 :return: 40 """ 41 42 if not os.path.exists(filename): 43 print('save_station_code():',filename,'不存在,正在下载!') 44 self.station_name_code() 45 46 file = 'name_code.json' 47 name_code_dict = {} 48 with open(filename, 'r') as f: 49 for line in f: 50 # 对读取的行都进行split操作,然后提取站点名和其代码 51 name = line.split('|')[1] # 站点名字 52 code = line.split('|')[2] # 每个站点对应的代码 53 # 每个站点肯定都是唯一的 54 name_code_dict[name] = code 55 56 # 把name,code保存到本地文件中,方便以后使用 57 with open(file, 'w') as f: 58 # 不以ascii码编码的方式保存 59 json.dump(name_code_dict, f, ensure_ascii=False) 60 61 62 def query_ticket(self): 63 """ 64 功能:查票操作 65 :return: 返回查询到的所有车次信息 66 """ 67 68 data = self._query_prompt() 69 if not data: 70 print('query_ticket() error: {}'.format(data)) 71 _, from_station, to_station = data.keys() 72 train_date = data.get('train_date') 73 from_station_code = data.get(from_station) 74 to_station_code = data.get(to_station) 75 76 query_param = 'leftTicketDTO.train_date={}&' \ 77 'leftTicketDTO.from_station={}&' \ 78 'leftTicketDTO.to_station={}&' \ 79 'purpose_codes=ADULT'\ 80 .format(train_date, from_station_code, to_station_code) 81 82 url = 'https://kyfw.12306.cn/otn/leftTicket/queryZ?' 83 84 full_url = url + query_param 85 resp = self.session.get(full_url, headers=self.headers) 86 if resp.status_code == 200 and resp.url == full_url: 87 print('query_ticket() 成功!然后进行车票清理工作!') 88 self._get_train_info(resp.json(), from_station, to_station) 89 90 else: 91 print('query_ticket() error! status_code:{}, url:{}\norigin_url:{}' 92 .format(resp.status_code, resp.url, full_url)) 93 94 def _get_train_info(self, text, from_station, to_station): 95 """ 96 功能:提取出查询到的列车信息 97 :param text: 包含所有从起点站到终点站的车次信息 98 :return: 返回所有车次信息 99 """ 100 if not text: 101 print('_query_train_info() error: text为:', text) 102 # 把json文件转变成字典形式 103 result = dict(text) 104 # 判断有无车次的标志 105 if result.get('data').get('map'): 106 train_info = result.get('data').get('result') 107 train_list = deque() 108 for item in train_info: 109 split_item = item.split('|') 110 item_dict= {} 111 for index, item in enumerate(split_item,0): 112 print('{}:\t{}'.format(index, item)) 113 if split_item[11] == 'Y': # 已经开始卖票了 114 item_dict['train_name'] = split_item[3] # 车次名 115 item_dict['depart_time'] = split_item[8] # 出发时间 116 item_dict['arrive_time'] = split_item[9] # 到站时间 117 item_dict['spend_time'] = split_item[10] # 经历时长 118 item_dict['wz'] = split_item[29] # 无座 119 item_dict['yz'] = split_item[28] # 硬座 120 item_dict['yw'] = split_item[26] # 硬卧 121 item_dict['rw'] = split_item[23] # 软卧 122 item_dict['td'] = split_item[32] # 特等座 123 item_dict['yd'] = split_item[31] # 一等座 124 item_dict['ed'] = split_item[30] # 二等座 125 item_dict['dw'] = split_item[33] # 动卧 126 train_list.append(item_dict) 127 # 无法买票的车次,有可能是已卖光,也有可能是还不开卖 128 elif split_item[0] == '': 129 print('_query_train_info():车次{}的票暂时不能购买!' 130 .format(split_item[3])) 131 else: 132 print('_query_train_info():车次{}还未开始卖票,起售时间为:{}' 133 .format(split_item[3], split_item[1])) 134 # 调用方法来打印列车结果 135 self._print_train(train_list, from_station, to_station) 136 else: 137 print('_get_train_info() error: 从{}站到{}站有没列车!' 138 .format(from_station, to_station)) 139 140 def _print_train(self, train_info, from_station, to_station): 141 """ 142 功能:打印查询到的车次信息 143 :param train_info: 提取出来的车次信息 144 :return: 145 """ 146 147 if not train_info: 148 print('_print_train() error: train_info是None!') 149 return 150 151 print('从{}到{}还有余票的列车有:'.format(from_station, to_station)) 152 for item in train_info: 153 if 'G' in item['train_name']: # 高铁 154 self._print_high_train_info(item) 155 elif 'D' in item['train_name']: # 动车 156 self._print_dong_train_info(item) 157 else: 158 self._print_train_info(item) 159 160 def _print_high_train_info(self, item): 161 """ 162 功能:打印高铁车次信息 163 :param item: 所有高铁车次 164 :return: 165 """ 166 print('车次:{:4s}\t起始时间:{:4s}\t到站时间:{:4s}\t' 167 '经历时长:{:4s}\t特等座:{:4s}\t一等座:{:4s}\t二等座:{:4s}' 168 .format(item['train_name'], item['depart_time'],item['arrive_time'], 169 item['spend_time'],item['td'], item['yd'], item['ed'])) 170 171 def _print_dong_train_info(self, item): 172 """ 173 功能:打印动车的车票信息 174 :param item: 所有动车车次 175 :return: 176 """ 177 print('车次:{:4s}\t起始时间:{:4s}\t到站时间:{:4s}\t' 178 '经历时长:{:4s}\t一等座:{:4s}\t二等座:{:4s}\t软卧:{:4s}\t动卧:{:4s}' 179 .format(item['train_name'], item['depart_time'], item['arrive_time'], 180 item['spend_time'],item['yd'],item['ed'], item['rw'], item['dw'])) 181 def _print_train_info(self,item): 182 """ 183 功能:打印普通列出的车次信息 184 :param item: 所有普通车次 185 :return: 186 """ 187 print('车次:{:4s}\t起始时间:{:4s}\t到站时间:{:4s}\t经历时长:{:4s}\t' 188 '软卧:{:4s}\t硬卧:{:4s}\t硬座:{:4s}\t无座:{:4s}' 189 .format(item['train_name'], item['depart_time'], item['arrive_time'], 190 item['spend_time'],item['rw'], item['yw'], item['yz'], item['wz'])) 191 def _query_prompt(self): 192 """ 193 功能: 与用户交互,让用户输入:出发日期,起始站和终点站并判断其正确性 194 :return: 返回正确的日期,起始站和终点站 195 """ 196 197 time_flag, train_date = self._check_date() 198 if not time_flag: 199 print('_query_prompt() error:', '乘车日期不合理,请检查!!') 200 return 201 # 创建有序字典,方便取值 202 query_data = OrderedDict() 203 from_station = input('请输入起始站:') 204 to_station = input('请输入终点站:') 205 206 station_flag = True 207 filename = 'name_code.json' 208 with open(filename, 'r') as f: 209 data = dict(json.load(f)) 210 stations = data.keys() 211 if from_station not in stations or to_station not in stations: 212 station_flag = False 213 print('query_prompt() error: {}或{}不在站点列表中!!' 214 .format(from_station, to_station)) 215 # 获取起始站和终点站的代码 216 from_station_code = data.get(from_station) 217 to_station_code = data.get(to_station) 218 query_data['train_date'] = train_date 219 query_data[from_station] = from_station_code 220 query_data[to_station] = to_station_code 221 222 if time_flag and station_flag: 223 return query_data 224 else: 225 print('query_prompt() error! time_flag:{}, station_flag:{}' 226 .format(time_flag, station_flag)) 227 228 229 230 def _check_date(self): 231 """ 232 功能:检测乘车日期的正确性 233 :return: 返回时间是否为标准的形式的标志 234 """ 235 236 # 获取当前时间的时间戳 237 local_time = time.localtime() 238 local_date = '{}-{}-{}'.\ 239 format(local_time.tm_year, local_time.tm_mon, local_time.tm_mday) 240 curr_time_array = time.strptime(local_date, '%Y-%m-%d') 241 curr_time_stamp = time.mktime(curr_time_array) 242 # 获取当前时间 243 curr_time = time.strftime('%Y-%m-%d', time.localtime(curr_time_stamp)) 244 245 # 计算出预售时长的时间戳 246 delta_time_stamp = '2505600' 247 # 算出预售票的截止日期时间戳 248 dead_time_stamp = int(curr_time_stamp) + int(delta_time_stamp) 249 dead_time = time.strftime('%Y-%m-%d', time.localtime(dead_time_stamp)) 250 print('合理的乘车日期范围是:({})~({})'.format(curr_time, dead_time)) 251 252 train_date = input('请输入乘坐日期(year-month-day):') 253 # 把乘车日期转换成时间戳来比较 254 # 先生成一个时间数组 255 time_array = time.strptime(train_date, '%Y-%m-%d') 256 # 把时间数组转化成时间戳 257 train_date_stamp = time.mktime(time_array) 258 # 获取标准的乘车日期 259 train_date_time = time.strftime('%Y-%m-%d', time.localtime(train_date_stamp)) 260 # 做上面几步主要是把用户输入的时间格式转变成标准的格式 261 # 如用户输入:2018-2-22,那么形成的查票URL就不是正确的 262 # 只有是: 2018-02-22,组合的URL才是正确的! 263 # 通过时间戳来比较时间的正确性 264 if int(train_date_stamp) >= int(curr_time_stamp) and \ 265 int(train_date_stamp) <= dead_time_stamp: 266 return True, train_date_time 267 else: 268 print('_check_date() error: 乘车日期:{}, 当前系统时间:{}, 预售时长为:{}' 269 .format(train_date_time, curr_time, dead_time)) 270 return False, None 271 272 273 274 def main(): 275 filename = 'station_name.txt' 276 station = Station() 277 station.station_name_code() 278 station.save_station_code(filename) 279 station.query_ticket() 280 281 if __name__ == '__main__': 282 main()
我们填好所有必要信息时,点击查询按钮,得到的结果如下:
在所有结果中我们只看到了3条信息,最主要的还是第一条,爬取了深圳到北京的车票
2.对数据进行清洗和处理
3.文本分析(可选):jieba分词、wordcloud可视化
4.数据分析与可视化
(例如:数据柱形图、直方图、散点图、盒图、分布图、数据回归分析等)
5.数据持久化
3.文本分析(可选):jieba分词、wordcloud可视化
4.数据分析与可视化
(例如:数据柱形图、直方图、散点图、盒图、分布图、数据回归分析等)
5.数据持久化
6.附完整程序代码
四、结论(10分)
1.经过对主题数据的分析与可视化,可以得到哪些结论?
2.对本次程序设计任务完成的情况做一个简单的小结。
1.经过对主题数据的分析与可视化,可以得到哪些结论?
2.对本次程序设计任务完成的情况做一个简单的小结。