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

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

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

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

#1 初步探索

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

# 家庭医生在线网、陕西西安地区链接
https://yyk.familydoctor.com.cn/area_305_0_0_0_1.html

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

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

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

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

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

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

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

#莲湖区链接
https://yyk.familydoctor.com.cn/area_305_0_0_0_1.html

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

# 西安市红会医院链接
https://yyk.familydoctor.com.cn/17097/

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

#2 设计爬虫规则

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

  1.  在最初给定的陕西西安的链接中获取各地区链接

  2.  通过每个地区链接,获取该地区每家医院的专页链接

  3.  通过医院的专页链接,抓取医院的具体信息

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

# 导入 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 库来对获取的返回结果进行解析,简单说就是它可以根据代码的规则便捷定位提取我们的目标数据。

# 导入 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 对各地区链接进行请求与解析,拿到地区内所有医院对应的链接。这里要注意的是,同一地区内所有的医院一个页面可能展示不完,这时要对后续页面进行请求获取。

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

# 给出地区链接 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

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

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 表格。

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 代码均已上传。

以上,感谢你的阅读~

发布了73 篇原创文章 · 获赞 71 · 访问量 2万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 猿与汪的秘密 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览