本文使用Python爬取了中国食品药品检定研究院2016年1月-2018年7月24日批准公式的所有长春长生生产的疫苗。
长春长生疫苗问题牵动广大民众,然而有的疫苗接种本上只有疫苗批次,却不显示具体哪家企业生产的。本文通过爬取中国食品药品检定研究院的公示信息,将2016-2018经中国食品药品检定研究院公示的生物制品批发签发的检验信息整理到一个Excel中,并筛选出长春长生的数据,供大家参考。
本文所有代码见https://github.com/pjgao/webCrawler
数据地址:
1. 长生产品批次,https://github.com/pjgao/webCrawler/blob/master/data/20160101-20180724%E6%A3%80%E9%AA%8C%E4%BA%A7%E5%93%81.xlsx
2. 所有批次,https://github.com/pjgao/webCrawler/blob/master/data/%E9%95%BF%E6%98%A5%E9%95%BF%E7%94%9F%E5%85%AC%E5%8F%B8%E4%BA%A7%E5%93%81.xlsx
使用工具:
- requests
- BeautifulSoup
需要python环境并安装以下库,cmd下使用pip安装:
pip install requests
pip install beautifulsoup4
1. 导入库
import requests
from bs4 import BeautifulSoup
from tqdm import tqdm#进度条工具
import re
import pandas as pd
2. 获取每张表格所在的URL
- 用浏览器打开网页:http://www.nifdc.org.cn/directory/web/WS02/CL0108/,按F12使用开发者工具,点击图示按钮后再点到表格上查看表格对应的标签信息
这里我们发现表格对应的标签id为tabel5
,故用在BeautifulSoup
对象中解析id = 'table5'
- 接下来在
table5
中查找所有的公示表格网页对应的链接url,在html中,使用标签a
表示链接,因此这里我们使用table.find_all('a')
。 查找到的每个链接i中url在属性'href'
,故i.attrs['href']
即为跳转到的url,该网站的url使用的相对地址,因此要加上主站地址,最终是pageURL+i.attrs['href']
- 所有的URL使用字典保存,字典的key为上图中的签发日期,使用正则
r'\d.*日'
从i.contents[0].contents[0]
提取到相应的信息
def check_link(url):
try:
r = requests.get(url)
r.raise_for_status() #抛出异常
r.encoding = r.apparent_encoding #解决编码问题
return r.text #读取文本多有r.text 读取图片多用r.content二进制
except:
print('无法链接服务器!!!')
def get_urls_from_pageURL(pageURL):
rs = check_link(pageURL)
soup = BeautifulSoup(rs,'html.parser')
table = soup.find(id='table5')
urls = {re.search(r'\d.*日',i.contents[0].contents[0]).group(): pageURL+i.attrs['href'] for i in table.find_all('a')}
#词典名为链接文本中的日期'\d.*日'匹配数字为开头以日为结束的日期如2018年6月1日至2018年6月15日
#标签'a'为html中的链接'href'为链接值
return urls
url_2018 = 'http://www.nifdc.org.cn/directory/web/WS02/CL0108/'
url_2017 = 'http://www.nifdc.org.cn/directory/web/WS02/CL0873/'
url_2016 = 'http://www.nifdc.org.cn/directory/web/WS02/CL0833/'
urls = {}
for url in [url_2018,url_2017,url_2016]:
urls.update(get_urls_from_pageURL(url))
3. 从URL读取公示数据
3.1 获取表格
这一步比较繁琐,因为数据涉及2016年到2018年的数据,存在以下问题:
1. 每张表格的列名不一定相同,如'序号'
和'序 号'
,'制品名称'
和'产品名称'
等。
2. 不同公示表格的信息列不同,如有的表格中含'批签发机构'
有的则不含该项。
3. 列名不一定在第一行,可能在第若干行才是列名
%%time
df = pd.DataFrame()
for i,u in tqdm(enumerate(urls.keys())):
d = pd.read_html(urls[u],header=0)[16]#经测试,网页中表格在第16个位置,header=0表示使用第0行做为列名
d.rename(columns={'制品名称':'产品名称','序 号':'序号','批签发证号':'证书编号','批量/进口量':'签发量','报告书编号':'报告编号'},inplace=True)
#第一遍爬取后发现表格中的列名存在异名,在此替换成同名
if i > 0 :#i为0时df是空的
#为了判断本次读取到的表格的列名是否是上次多数相同,不同年份的表格列数不同,故测试选择了大小为5
x = set(d.columns.values)
y = set(df.columns.values)
if len(x.intersection(y)) < 5:#如果当前表格与以往表格列名重复度低于5则存在问题
#列名不匹配有两种情况
location = get_index(d,'制品名称')
if location:
#html表格元素有问题,列名不在第一行,在后面的某行
d = d.iloc[location[0][0]:,location[0][1]:].reset_index(drop=True).T.set_index(0).T.reset_index(drop=True)
d.rename(columns={'制品名称':'产品名称','序 号':'序号','批签发证号':'证书编号','批量/进口量':'签发量','报告书编号':'报告编号'},inplace=True)
else:
#根本不存在该表格
print(u,d.shape,'error')
continue
df = pd.concat([df,d])
运行过程输出信息:
58it [06:44, 6.97s/it]
2016年9月16日至9月31日 (42, 10) error
59it [06:44, 6.86s/it]
2016年9月1日至9月15日 (42, 10) error
75it [08:32, 6.84s/it]
CPU times: user 5min 33s, sys: 1.33 s, total: 5min 35s
Wall time: 8min 32s
其中2016年9月的数据并没有直接贴到网站上,而是以附件形式上传的。
3.2 筛选长生公司的产品
df.dropna(how='all',axis=1,inplace=Tru
df.drop(['序号','拒签量'],axis=1,inplace=True)
df_cs = df.loc[df.生产企业.apply(lambda x:x.find('长生')>=0)]
df.to_excel('20160101-20180724检验产品.xlsx',index=False,encoding='gbk')
df_cs.to_excel('长春长生公司产品.xlsx',index=False,encoding='gbk')
4. 数据分析
df_cs.产品名称.value_counts()
对产品统计发现共有7种疫苗被批准:
1. 冻干人用狂犬病疫苗(Vero细胞)
2. 水痘减毒活疫苗
3. 冻干甲型肝炎减毒活疫苗
4. 吸附无细胞百白破联合疫苗
5. 冻干水痘减毒活疫苗
6. 流感病毒裂解疫苗
7. ACYW135群脑膜炎球菌多糖疫苗
用pyecharts
绘制长生公司批准个疫苗占比的饼状图:
致谢:
1. https://www.douban.com/note/683907959/
2. https://github.com/fuckcqcs/fuckcqcs/