前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 爬虫统计当地所有医院信息

Python 爬虫统计当地所有医院信息

作者头像
TTTEED
发布2020-07-09 14:54:13
1.7K0
发布2020-07-09 14:54:13
举报
文章被收录于专栏:用户6811391的专栏

这是上周五在微信群里看到的问题:

之前曾尝试过对知乎和微博热榜的简单爬虫,算是小有经验但仍需锻炼,于是趁着这个机会,主动包揽了爬虫代码,并在这回顾整理一番。

对于爬虫的概念,往复杂说会涉及很多技术点,但最核心的点很简单:爬虫就是按照我们给定的规则自动去网上把数据抓取下来。对应到上面的需求,想统计当地所有医院的信息,同时已经筛选好相关网站了,那么我们只要对应其网站对抓取数据的过程进行设计编码,即可最终实现爬虫任务。

#1 初步探索

那首先,我们先去瞅瞅要爬的网站什么样。对方选定的是家庭医生在线网,想要获取网站上列出的所有西安地区医院的信息。

代码语言:javascript
复制
# 家庭医生在线网、陕西西安地区链接
https://yyk.familydoctor.com.cn/area_305_0_0_0_1.html

通过链接,我们点开的网页如下:

网页展示面向的对象是人,它的设计是方便观众点击浏览。那么传统方式上我们如果想统计这些数据,就可以逐个点开来复制粘贴汇总到一起。

但爬虫是由计算机实现的,它并不需要这些加过装饰、设计的页面,它只关心其中最重要的数据。我们既然要为爬虫制定规则,那么直接围绕着页面的代码和数据来进行分析是最高效的。

右键点击页面,选择“显示网页源代码”:

刚我们提到网页对数据进行装饰设计,网页源代码所展示的就是网页如何通过前端代码(HTML,JavaScript,CSS)加工数据的过程;而爬虫要做的就是在这些代码中提取出目标数据。既然加工数据应用到了前端语言,那么爬虫自然也需要些前端基础才好操作。

比如上图中,当我们下拉到 1369 行时看到了第一组目标数据:从莲湖区到高陵县,每个地区前面的 href="链接" 都对应了各地区的链接。因为我们想要获取西安本地所有医院信息,那么西安所有地区的医院名单肯定是要抓取的。当然,我们也可以继续向下看,页面中针对不同地区列举了相应的具体医院信息。但这些数据都只是摘要,且页面中并没有完全展示所有医院信息,所以我们先忽略这些不全的具体数据,目光回到刚刚收集到的地区链接上。

我们点开莲湖区的链接 ,同样查看网页源代码,向下翻找有用信息。

代码语言:javascript
复制
#莲湖区链接
https://yyk.familydoctor.com.cn/area_305_0_0_0_1.html

翻找过程可以比对着页面中所展示的医院信息来快速定位,很快我们发现 2197 行开始,正是对“西安市红会医院”信息的展现。但别高兴太早,我们想要的信息是医院名称、医院地址、医院等级以及咨询电话,但很不凑巧,信息中缺失了医院地址。眼尖的话可以看到“西安市红会医院”字样前面还有个链接,没错,这就是具体到该医院的专页了,我们继续打开搜寻。

代码语言:javascript
复制
# 西安市红会医院链接
https://yyk.familydoctor.com.cn/17097/

这下数据比较清晰了,473行源代码开始,我们想要的医院类型、等级、地址、咨询电话逐一列在眼前,把这些数据取到任务就完成了。

#2 设计爬虫规则

探索完整个过程,接下来我们要按照相应的流程来进行编码设计。上述过程可以分解为三步:

  1. 在最初给定的陕西西安的链接中获取各地区链接
  2. 通过每个地区链接,获取该地区每家医院的专页链接
  3. 通过医院的专页链接,抓取医院的具体信息

具体的爬虫代码实现规则,则要依赖 requests 库,该库如其名是一个常用的 http 请求库,用来发起 http 请求并获取响应结果,整个过程出奇简单:

代码语言:javascript
复制
# 导入 requests 库
import requests
# 模拟 headers
headers = {"User-Agent":"","Cookie":""}
# 最初的西安医院链接
xian_url="https://yyk.familydoctor.com.cn/area_305_0_0_0_1.html"
# 通过 requests 的 get 获取访问链接返回结果
content = requests.get(xian_url,headers=headers)
# 打印返回结果
print(content.text)

通过 requests.get() 获取到的返回结果与我们在网页上查看源代码大致差不多,下图是我运行代码获取的结果:

接下来是通过 BeautifulSoup 库来对获取的返回结果进行解析,简单说就是它可以根据代码的规则便捷定位提取我们的目标数据。

代码语言:javascript
复制
# 导入 BeautifulSoup
from bs4 import BeautifulSoup
# 使用该库解析上面代码中得到的返回结果 content
xian_soup = BeautifulSoup(content.text,"html.parser")
# 根据返回结果中特定的代码位置找到相应的数据
target = xian_soup.find("div",class_="selection")
# 找到所有的地区
area_list = target.find_all("li")[1:]
# 为不同地区新建字典用于后续储存
area_dict={}
# 对获取到的地区进行遍历
for area in area_list:
    # 首先拿到地区名字
    area_name = area.get_text(strip=True)
    print (area_name)
    # 获取该地区前面对应的链接
    print (area.a["href"])
    area_dict[area_name]=area.a["href"]
# 输出地区字典
print(area_dict)

运行代码,我们获得如下打印的结果:

这样我们拿到了第一步中西安各地区对应的链接。接下来我们同样是继续用 requests 和 BeautifulSoup 对各地区链接进行请求与解析,拿到地区内所有医院对应的链接。这里要注意的是,同一地区内所有的医院一个页面可能展示不完,这时要对后续页面进行请求获取。

因为每个地区都会重复这个获取过程,我们可以将该过程写成一个函数:

代码语言:javascript
复制
# 给出地区链接 zone_url 和用来存医院链接d额字典 hospital_dict
def get_hospital(zone_url, hospital_dict):
    headers2 = {"User-Agent": "", "Cookie": ""}
    # 通过 requests 库获取地区链接的返回结果
    zone_content = requests.get(zone_url, headers=headers2)
    # 通过 BeautifulSoup 来解析返回结果
    zone_soup = BeautifulSoup(zone_content.text, "html.parser")
    # 定位到医院链接位置
    zone_target = zone_soup.find_all("div", class_="listItem")
    # 接下来将筛选出的医院链接存到 hospital_dict 字典中
    for item in zone_target:
        name = item.a.get_text(strip=True)
        # print(name)
        a_label = item.find_all("a")[0]
        # print(a_label['href'])
        hospital_dict[name] = a_label['href']
        # print()
    # 检测是否存在下一页
    next_url = None
    next_page = zone_soup.find("div", class_="endPage")
    if next_page:
        next_link = next_page.find("a", class_="next")
        if next_link:
            next_url = next_link["href"]
    # 将获取到的医院链接地址字典和下一页的检测结果返回
    return hospital_dict, next_url

针对每个地区,我们都使用该函数进行相应地操作,如果该地区存在第二页,则继续调用该函数对下一页进行提取:

代码语言:javascript
复制
hospitals = {}
for zone in area_dict:
    hospitals,next_page = get_hospital(area_dict[zone],hospitals)
    # 如果存在下一页
    while next_page:
        # 继续使用该函数进行提取
        hospitals,next_page = get_hospital(next_page,hospitals)
    print(f"{zone} 医院信息已获取完毕...")
# 最终打印所有的地区链接字典
print(hospitals)

拿到所有医院对应的专页链接后,我们继续用 requests 和 BeautifulSoup 来请求和解析,从中提取我们想要获取的医院信息。通常我们都会将结果结果存入 Excel 表格中,那么就需要相应的库比如 pandas 来将数据写入 Excel 表格。

代码语言:javascript
复制
import requests
from bs4 import BeautifulSoup
from pandas import DataFrame

excel_dict={}
df = DataFrame(columns=["医院名称","医院类型","医院等级","医院地址","咨询电话"])
for hospital in hospitals:
    print("医院名称:",hospital)
    hospital_link=hospitals[hospital]
    headers = {"User-Agent":"","Cookie":""}
    hospital_info = requests.get(hospital_link,headers=headers)

    hospital_soup = BeautifulSoup(hospital_info.text,"html.parser")
    temp = hospital_soup.find_all("div",class_="introPic")
    if temp:
        hospital_intro = temp[0]
        #print(hospital_intro)
        detail = hospital_intro.find_all("dl")
        #print(detail)
        temp_dict={}
        for subitem in detail:
            title = subitem.dt.get_text(strip=True)
            if "医院地址" in title:
                answer = subitem.dd.get_text(strip=True)[:-4]
            else:
                answer = subitem.dd.get_text(strip=True)
            temp_dict[title]=answer
            print(title,answer)
    excel_dict[hospital]=temp_dict
    print(temp_dict)
    # 将目标数据存入 df 数据结构中
    df=df.append({"医院名称":hospital,"医院类型":temp_dict["医院类型:"],"医院等级":temp_dict["医院等级:"],"医院地址":temp_dict["医院地址:"],"咨询电话":temp_dict["咨询电话:"]},ignore_index=True)
    print("======================")
    print(df)
# 将数据写入 result.xls 表格    
df.to_excel("result.xls")
print("结果写入 result.xls")

将以上步骤串联起来,运行代码,即可得到最终爬虫结果:

#3 过程回顾

由于整个过程经过三轮请求、解析返回结果,导致代码运行时间较长,从获取到的表格数据中看,总共拿到了 219 条数据,但代码运行时长 6 分钟(最快一次)到 20 分钟(最慢一次)。时间长度也取决于网络状况,毕竟要全部请求并解析完才会出结果。

因为整个过程比较长,且前后依赖性较强,我并没有用 Pycharm 在一份 py 代码中来编辑运行,而是用 Jupyter Notebook 分步骤来步步执行的。这样执行完第一部分后,再设计第二部分时就可以直接拿第一部分的结果用,避免再跑一轮代码浪费时间。

我是采用的 pandas 库来写入表格,但运行到最后一步发现,这个写代码的电脑里装了 pandas 库却没装 xlwt 库,写入 Excel 表格的函数是依赖该库的。得出的教训就是,为了避免浪费时间,可以先准备简单的数据串一下流程。

最终写入表格时,起初我采用直接将医院数据字典转化为 DataFrame 格式,结果输出的表格行列正好反着,也是赶着最后一点完成任务,对网上关于行列互换的方法没能深入研究。为了完成任务,采用了比较简单粗暴的逐行写入 DataFrame 的方法,这个后续有机会可以再琢磨琢磨。

好久没写 Python,乍一写都有些手生了,惭愧。。

公众号后台回复 医院爬虫 可以获取 GitHub 代码下载链接,py 代码和 ipynb 代码均已上传。

以上,感谢你的阅读~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TTTEED 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档