js在线美化网址:https://www.html.cn/tool/js_beautify/
js在线解析网址:https://www.json.cn/#
思路分析:首先,进入首页https://www.8684.cn/,然后打开开发者工具,通过开发者工具观察网页变化,我们发现只有当我们【点击切换】的时候,才会有全国各地城市信息出现,如图:
图2
这是局部网页的刷新,所以可能是ajax接口传送或者js文件中存在,通过点击这两处,发现数据在JS文件中,如图:
各城市的数据
利用json美化工具,将数据进行美化,发现数据是伪json数据,可以通过处理变成json数据(其中数据有按拼音和省份两种,选择一种即可,哪种方便选择哪种),然后通过json在线解析工具,解析json数据,所得数据如下:
解析后的一小部分数据
这样就可以获得全国的所有城市,然后将所有的城市分别与https://{城市名称}.8684.cn/进行拼接获得对应城市的公交首页,然后分别向对应的数字和字母开头发送请求获得所有的公交车链接,最后再向所有的公交车详情页发送请求,最后解析获得线路名称,运行时间,票价信息,公交公司,更新时间,线路长度,上行站个数,上行站牌,下行站个数,下行站牌等信息;代码实现如下:
import requests
import re
from bs4 import BeautifulSoup
import json
import time
import os
from multiprocessing import Pool
# 定制请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36',
'Referer': 'https://www.8684.cn/'
}
def demo(city, url_begin, dirname):
filename = str(city) + '.txt'
filepath = os.path.join(dirname, filename)
fp = open(filepath, 'w', encoding='utf8')
url_city = url_begin.format(city)
# print(url_city)
# exit()
# 向一级页面发送请求,得到所有的以数字开头或者字母开头的列表
number_char_href_list = parse_first_page(url_city)
# 遍历这个列表,依次向每个url发送请求
for nchref in number_char_href_list:
# 向二级页面,发送请求,得到所有的以1,2,3...开头的所有公交的url
href_list = parse_second_page(url_city, nchref)
# 遍历所有的href_list,依次向每个公交url发送请求,挨个解析
for href in href_list:
parse_third_page(href, fp)
fp.close()
def parse_first_page(url_city):
r = requests.get(url=url_city, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
#得到所有的以数字开头的链接
number_a_list = soup.select('.bus_kt_r1 > a')
#得到所有的以字母开头的链接
char_a_list = soup.select('.bus_kt_r2 > a')
all_a_list = number_a_list + char_a_list
# print(len(all_a_list))
# exit()
all_href_list = []
#得到所有a对象的href属性
for oa in all_a_list:
href = url_city.rstrip('/') + oa['href']
all_href_list.append(href)
time.sleep(2)
# print(len(all_href_list))
# exit()
return all_href_list
def parse_second_page(url_city,nchref):
r = requests.get(url=nchref, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
#获得所有的以某某开头的链接
a_list = soup.select('#con_site_1 > a')
#获得所有的公交链接
href_list = []
for oa in a_list:
href = url_city.rstrip('/') + oa['href']
href_list.append(href)
time.sleep(2)
return href_list
def parse_third_page(href,fp):
r = requests.get(url=href, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
#获取线路名称
route_name = soup.select('.bus_i_t1 > h1')[0].string
print('正在爬取---%s---....' % route_name)
#获取运行时间
run_time = soup.select('.bus_i_content > p')[0].string
#获取票价信息
piao_info = soup.select('.bus_i_content > p')[1].string
#获取公交公司
company = soup.select('.bus_i_content > p > a')[0].string
#获取更新时间
update_time = soup.select('.bus_i_content > p')[3].string.lstrip('最后更新:')
#获取路线长度
try:
route_length = soup.select('.bus_label > p')[0].string.strip('全程公里。')
except Exception as e:
route_length = '没有长度'
#获取上行总站个数
up_total = soup.select('.bus_line_top > span')[0].string.strip('共站').strip()
up_site_name_list = []
#获取得到所有的站牌
all_a_list = soup.select('.bus_line_site > .bus_site_layer > div > a')
#获取得到上行所有站牌
up_a_list = all_a_list[:int(up_total)]
for oa in up_a_list:
up_site_name_list.append(oa.string)
try:
#获取下行总站个数
down_total = soup.select('.bus_line_top > span')[1].string.strip('共站').strip()
#获取下行站牌
down_a_list = all_a_list[int(up_total):]
down_site_name_list =[]
for oa in down_a_list:
down_site_name_list.append(oa.string)
except Exception as e:
down_total = '没有下行'
down_site_name_list = []
#将信息保存到字典中
item = {
'线路名称': route_name,
'运行时间': run_time,
'票价信息': piao_info,
'公交公司': company,
'更新时间': update_time,
'线路长度': route_length,
'上行站个数': up_total,
'上行站牌': up_site_name_list,
'下行站个数': down_total,
'下行站牌': down_site_name_list,
}
string = json.dumps(item, ensure_ascii=False)
fp.write(string + '\n')
time.sleep(2)
print('结束爬取--%s---' % route_name)
def main():
t1 = time.time()
url = 'https://js.8684.cn/citys/city_boxInf.min.js'
#发送请求获得响应
r = requests.get(url=url, headers=headers)
#获得内容
content = r.text
#解析这个js文件,获取所有的城市列表
string = content.split('=')[-1].rstrip(';')
#正则提取所有城市拼音
pattern = re.compile(r'([a-z]+):')
# print(string)
ret = pattern.findall(string)
#print(len(ret))#452个城市
url_begin = 'https://{}.8684.cn/'
pol = Pool(24)
dirname = 'gongjiao'
for city in ret:
pol.apply_async(demo, (city, url_begin,dirname))
#关闭池子,不允许添加任务
pol.close()
#让主进程等待
pol.join()
print('主进程-子进程全部结束')
t2 = time.time()
print(t2-t1)
if __name__ == '__main__':
main()
进程池 开24个进程,时间3个多小时,数据大小58.7M
网友评论