这次为大家带来的是一个综合性较强的小项目,该项目流程较多且不需要用数据解析也能完成。由于该项目涉及到他人的隐私信息,故不会有对应信息的截图,敬请理解!
项目案例实现|Python爬虫 04:爬取药监局官网中化妆品生产许可信息
项目来源
本项目来源B站UP主路飞学城视频:视频链接点这里
项目需求
- 爬取所有生产化妆品的公司的企业名称和生产许可证编号
- 爬取每一家企业的详情页的生产许可证信息详细数据
具体分析
1. 指定URL
1.1 初始页面目标URL的获取
打开药监局化妆品生产许可信息管理系统服务平台:http://scxk.nmpa.gov.cn:81/xk/
打开抓包工具对页面发送请求,在响应数据中尝试搜索具体公司名称发现为空,初步判断企业名称等数据为ajax请求,打开对应数据抓取工具,再次发请求后拿到对应的数据包。
在headers板块中可获得对应需求1的目标URL,将其粘贴至代码段即可。在该板块中可以继续获得如下信息:
- 发送的请求为post请求
- 得到的响应数据为json格式
- post请求携带的参数有:
on: true
page: 1
pageSize: 15
productName:
conditionType: 1
applyname:
applysn:
1.2 详情页面目标URL的获取
下面随机点开一家公司的详情页,以首页的第一家公司为例。
详情页URL:http://scxk.nmpa.gov.cn:81/xk/itownet/portal/dzpz.jsp?id=2bb25030a84e4c13a1dfff05d837cba1
再打开两家公司的URL:
- http://scxk.nmpa.gov.cn:81/xk/itownet/portal/dzpz.jsp?id=61679e8db15b4746a193dadeb29b3913
- http://scxk.nmpa.gov.cn:81/xk/itownet/portal/dzpz.jsp?id=35eb560d1cc742daa2fb71c878b9da35
通过对比能够很明显看出三者的区别仅为后面id参数值不同,据此可猜测这个id就是每一家公司详情页URL的一个参数。
下面利用抓包工具对第一家公司详情页发请求,在拿到的数据包里的响应数据中查找公司名称等数据,发现依旧无结果返回,则这些数据极有可能也存在一个ajax包里。打开对应抓包工具,再次发请求,拿到对应数据包。
在headers板块中即可获得需求2的URL,同时还可以获取以下信息:
- 发送的请求同样为post请求
- 返回的响应数据为json格式
- post请求携带的参数为企业的id值
2. UA伪装
依旧与以前一样进行UA伪装即可,此处不再写出
3. 对概览页目标信息的获取
根据前面的分析可初步得知,为了爬取到我们所需要的信息,首先需要获取每一家企业的id值。用抓包工具获取在概览页中请求到的ajax包里的响应数据,并用json解析器解析后,得如下结果(以下只列出一部分结果,数目太多占篇幅)
{
“filesize”: “”,
“keyword”: “”,
“list”: [{
“ID”: “2bb25030a84e4c13a1dfff05d837cba1”,
“EPS_NAME”: “艾一生命科技(广东)有限公司”,
“PRODUCT_SN”: “粤妆20210025”,
“CITY_CODE”: null,
“XK_COMPLETE_DATE”: {
“date”: 21,
“day”: 4,
“hours”: 0,
“minutes”: 0,
“month”: 0,
“nanos”: 0,
“seconds”: 0,
“time”: 1611158400000,
“timezoneOffset”: -480,
“year”: 121
},
“XK_DATE”: “2026-01-20”,
“QF_MANAGER_NAME”: “广东省药品监督管理局”,
“BUSINESS_LICENSE_NUMBER”: “91440300MA5F0H5AXJ”,
“XC_DATE”: “2021-01-21”,
“NUM_”: 1
},
可见所有数据都封装在一个字典中,而公司对应的信息则封装在字典下的’list’键值的value值中。该value值为一个列表,里面封装了一个字典,只需将该字典里的ID对应的value值提取出来即完成了信息的获取。
表现在代码中,即为
ids_list = [] # 存储企业对应的id值
home_json = requests.post(url=url, data=data_home, headers=header).json()
for dic in home_json['list']:
ids_list.append(dic['ID'])
4. 对详情页目标信息的提取
获取了id值后,就获取了详情页目标URL的参数,接下来就可以对详情页对应的URL发请求了。由于在之前将概览页展示的所有企业的id值存储在一个列表中,那么通过循环即可实现一一对这些公司的详情页对应URL发请求,表现在代码中,即为
final_list = [] # 存储最终获取的数据
url_post = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in ids_list:
data_detail = {'id': id}
detail_json = requests.post(url=url_post, data=data_detail, headers=header).json()
final_list.append(detail_json)
5. 持久化存储
因为获取的响应数据为json格式,故调用json模块对获取的数据进行存储。
fp = open('./企业信息.json', 'w', encoding='utf-8')
json.dump(final_list, fp=fp, ensure_ascii=False)
6. 翻页操作
以上进行的只是对概览页第一页进行数据获取,那么要想进行分页操作要怎么实现呢?
在对概览页目标URL的捕获阶段,我们使用抓包工具获取了该post请求携带的参数,其中的参数page: 1
即表示当前为第一页概览页,只要改变这个value值即可实现翻页操作,具体在完整代码中展示。
项目代码
# -*- coding: utf-8 -*-
# @author : QIN
# @time : 2021/1/11 13:04
# @function :
import requests
import json
if __name__ == '__main__':
url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
header = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
}
ids_list = [] # 存储首页企业的id数据
final_list = [] # 全部数据存储
for page in range(1, 6): # 一共有365页,这里只爬取前五页
page = str(page)
data_home = {
'on': 'true',
'page': page,
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn': '',
}
home_json = requests.post(url=url, data=data_home, headers=header).json()
for dic in home_json['list']:
ids_list.append(dic['ID'])
# print(ids_list)
url_post = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in ids_list:
data_detail = {'id': id}
detail_json = requests.post(url=url_post, data=data_detail, headers=header).json()
final_list.append(detail_json)
fp = open('./企业信息.json', 'w', encoding='utf-8')
json.dump(final_list, fp=fp, ensure_ascii=False)
print('数据下载成功!')
运行结果
由于这里只爬取到第五页,这里我们将第五页最后一家公司的详情页点开进行核对,发现爬取到的信息无误,即本案例完成!
注意事项
本案例更加接近日常生活中的爬虫需求,在本例中进行了两次请求的发送,其中第一次请求发送是为了服务第二次请求发送。在传参的时候,注意看仔细不要漏,否则不会请求出结果。