- 目标网站分析
网站名称 URL :
网站名称:“全国防疫政策一键查询”
网站URL:
https://app.21jingji.com/html/2021yiqing_cxcx/index2022.html
该网站采用反爬虫技术:
JavaScript动态加载数据
展示网站的反爬虫存在:
网站截图如下:
网页实时审查元素:
防疫政策相关的所有内容位于“id=result”的div内
查看源代码时(直接对网站发出请求)所得:
发现原本存储防疫政策的块内内容不存在,直接请求网页无法获得数据。
- 操作过程
工作思路
因为在审查元素时和检查源代码时发现内容差异,因此判断数据为动态加载,因此使用浏览器查看该网站的脚本文件:
可以发现网站对于选择城市后的处理过程,发现在选择城市后,脚本对‘js/citylist.json’发出请求,那我们同样对这个url进行一次请求,加上前置的网站url:
“https://app.21jingji.com/html/2021yiqing_cxcx/js/citylist.json”
得到如下内容:
可以看到这是一个中国各城市(地区)以及其对应的id,这对后面获得对应疫情数据应该有帮助,我们继续查看JavaScript代码:
如上,根据citylist中的内容和网页上点击的情况,调用InitPolicy(),经查看,代码如下:
可以看到网站对一个接口发出请求,而且参数正是上面我们所获得的citylist中对应的城市(地区)id,但这里参数包含两个城市,出发地和目的地,但我们所需要的是单个城市出入政策,根据规律,我们仅使用一个城市id作为参数,发现请求能够成功,返回单个城市的出入政策以及风险地区:
那么接下来的工作就是根据一开始获得的citylist.json文件中对应城市id,对所有城市请求得到所有城市的防疫政策即可,结果如下:
源代码分析
网站源代码:
经由审查能看到所有具体政策存储在id=result的div下,出发地政策放在id=startItem的div下,目的地政策放在id=destinationItem的div下。
网站JavaScript源代码:
根据citylist.json中城市和省份对应id,和网页上选择的省和城市作对比,首先对页面上展示出发地和目的地的容器内容进行更改,之后调用initPolicy()。
InitPolicy()根据选择的城市对接口发出请求,然后根据所返回的数据对网页内容进行异步渲染,即更改id=result的div的html内容,具体过程为添加了id=startItem和id=destinationItem的div的内容,包括了出入政策以及中高风险地区(如果存在)。
- 爬取数据
保存格式
.json文件
数据量大小
总计4MB数据。
- 源代码分析
-
url = 'https://app.21jingji.com/html/2021yiqing_cxcx/index2022.html' # 城市表 citylist = 'https://app.21jingji.com/html/2021yiqing_cxcx/js/citylist.json' # 出行政策以及风险地区 policy = 'https://m.21jingji.com/dynamic/zhanyi2020/getCityPolicy?'
get_city_list()获取城市列表:
req = requests.get(citylist)
req.encoding = 'utf-8'
data = json.loads(req.text)
with open('js/citylist.json', 'w') as f:
json.dump(data, f)
print("citylist写入完毕!")
city_list = []
# 第一层是以省为单位
for each in data:
# 第二层存储在第一层的'list'字段中
for city in each['list']:
city_list.append(city['id'])
# print(city)
return city_list
因为此处已将citylist.json保存至本地,因此直接打开了该文件,根据该json文件的格式,我们除掉第一层(保存省份id),直接获取所有的城市id。
get_policy_list()获取所有城市的政策:
policy_list = []
for each in tqdm(city_list):
each_policy = get_policy(policy, each)
# print(each['id']+each['name'])
sleep(0.01)
policy_list.append(each_policy)
with open('js/policylist.json', 'w') as f:
json.dump(policy_list, f)
print("policylist写入完毕!")
根据上面获得的城市id,对每一个城市调用get_policy
get_policy()如下:
# ['city_id',城市编号
# 'province',省名
# 'city',市名
# 'leave_policy',出境政策
# 'leave_policy_date',出境政策发布日期
# 'back_policy',入境政策
# 'stay_info',住宿须知
# 'back_policy_date',入境政策发布日期
# 'poi_list',风险地区
# "area": 社区名称,
# "type": "1"中风险,“2”高风险
# "sid": 位置,
# "province": 所属省,
# "city": 所属市(地区),
# "adcode": 同cityid,
# "district_adcode": 社区编号
# 'leave_policy_list',
# 'back_policy_list',
# 'stay_info_list'同上(格式化数据),
# 'city_code',同上
# 'district'空值]
# 获取某个城市的政策已经风险地区等级
def get_policy(policy, cityid):
head = {'User-Agent': 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, ''like '
'Gecko) Chrome/18.0.1025.166 Safari/535.19 '}
para = {'cityid': cityid}
req = requests.get(url=policy, params=para, headers=head)
req.encoding = 'utf-8'
status = json.loads(req.text)['status']
# 没有发布防疫政策的地区不含有data字段,status为0
if status != 1:
pass
else:
data = json.loads(req.text)['data']
city_policy = {'cityid': cityid, 'data': data}
return city_policy
注释为所获取数据的格式以及各个字段含义,get_policy()发出请求,根据请求返回的status(状态)字段判断该城市是否有发布对应政策,如果没有则pass。
返回政策。