1.爬虫设计

本文用Python语言实现网络爬虫抓取特定数据,使用Beautiful Soup进行HTML的解析。Beautiful Soup是一个HTML/XML解析器,主要功能是解析和提取HTML/XML中的数据,提取数据时,类似于正则表达式的功能。Beautiful Soup将整个文档载入,解析整个DOM树,其时空开销都比较大,性能不如lxml。Beautiful Soup为不同的解析器提供了相同的接口,但解析器本身是有区别的。使用不同的解析器解析同一篇文档,可能会产生不同结构的树型文档。本系统的页面解析使用的是lmxl解析器。lmxl解析器是Python的一个解析库,支持HTML和XML的解析,同时也支持Xpath解析方式,而且解析效率非常高。

医院和医生的信息页面分别为图1和图2。

图1 医院信息展示页面

图2 医生信息展示页面

2.URL分析

URL(Uniform Resource Locator)中文名称使统一资源定位器,表示网站的页面地址。

(1)首先,对医院的URL进行分析,经过分析,发现医院页面分地址有如下规律:

URL = “https://www.guahao.com/hospital/” + hospital_id

(2)其次,对科室页面URL进行分析,结果如下:

URL = “https://www.guahao.com/department/” + department_id + "?isStd=”

(3)医生页面地址:

URL = “https://www.guahao.com/expert/”+doctor_id +”?hospDeptId=” + department_id + ”&hospitalId=” + hospital_id

(4)医生全部评论URL设置了反爬虫手段,每页评论信息的URL中都有一个sign参数和timestamp参数:

https://www.guahao.com/commentslist/e-“ + doctor_id + ”/all-0?pageNo=” + pageNum + ” &sign=” + sign_value + “&timestamp=” + timestamp_value

研究发现,跳转到下一页的URL中的sign和timestamp参数在本页面中的两个<input>标签中已经获取,如图3。

图3 含sign和timestamp参数的input标签

3.DOM分析

对于一个HTML文档,通过分析文档,根据DOM的上下文标签的关系,可以分析得到特定标签下的所需信息。以医生信息页面(如图4)为例,医生信息在<div class= "grid-section expert-card fix-clear">中,医生姓名、职称、所属科室等信息在其子标签下。

图4 医生信息页面源文件

首先通过城市获取到该城市的医院列表,进而得到每个医院的URL,再从医院信息页,获取所有科室和门诊信息,跳转到门诊信息页就可以得到医生列表,通过医生的URL,再跳转到医生信息页面,就可以解析得到医生详细信息了。

评论信息页面的抓取是通过医生详细信息页面跳转得到的。评论抓取过程中遇到抓取几页评论信息之后,反馈信息为“评价信息不存在”的情况(如图5),分析是因为网站设置了反爬功能。在通过URL访问页面时,添加“User-Agent”和“Cookie”属性,来“假装”浏览器操作。

图5 错误反馈信息

在使用网络爬虫进行抓取信息时,有些医生存在部分信息(简介、评论、擅长等)缺失,需要单独进行判断;另外,医院列表、医生列表、评论等都是分页显示,所以在抓取信息时,需要处理分页,特别是判断是否已是最后一页。例如在评论分页列表中,非尾页页面有跳转至下一页的<a>标签(如图6),通过判断页面中是否含有该标签即可得知此时是否是尾页。

图6 下一页<a>标签

4.数据库设计

SET FOREIGN_KEY_CHECKS=0;-- ----------------------------
-- Table structure for t_doctor_beijing
-- ----------------------------
DROP TABLE IF EXISTS `t_doctor_beijing`;
CREATE TABLE `t_doctor_beijing` (`doc_id` int(11) NOT NULL AUTO_INCREMENT,`doc_name` varchar(30) DEFAULT NULL,`doc_sex` varchar(6) DEFAULT NULL,`doc_title` varchar(50) DEFAULT NULL,`doc_hospital_name` varchar(50) DEFAULT NULL,`doc_department_name` varchar(50) DEFAULT NULL,`doc_clinic_name` varchar(50) DEFAULT NULL,`doc_labels` varchar(100) DEFAULT NULL,`doc_score` float(11,1) DEFAULT NULL,`doc_hits` int(11) DEFAULT NULL,`doc_impression` varchar(200) DEFAULT NULL,`doc_goodat` varchar(2500) DEFAULT NULL,`doc_about` varchar(2500) DEFAULT NULL,`doc_hospital_number` varchar(50) NOT NULL,`doc_clinic_number` varchar(50) NOT NULL,`doc_number` varchar(50) NOT NULL,PRIMARY KEY (`doc_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1DEFAULT CHARSET=utf8;

5.爬虫代码

(1)根据城市获取该城市医院的编号

import requests
from bs4 import BeautifulSoup
import codecs, sys
import retarget = codecs.open('hospitalUrl_Tianjin.txt', 'w', encoding="utf8")def writeFile(urls):for url in urls:target.writelines('\'' + url + '\',\r\n')def getHospitalUrl():reslist = []BaseUrl = 'https://www.guahao.com/hospital/3/北京/all/不限/p'i = 1while True :print('page '+ str(i))pageUrl = BaseUrl + str(i)html = requests.get(pageUrl)html.encoding = 'utf-8'soup = BeautifulSoup(html.text, 'lxml')data = soup.select('#g-cfg > div.g-grid2-l > div.g-hospital-items.to-margin > ul > li')for item in  data:hos = list(item.find_all('a', class_='cover-bg seo-anchor-text'))hosUrl = hos[0]['monitor-hosp-id']reslist.append(hosUrl)l = len(list(soup.find_all('a', class_='next J_pageNext_gh')))if l <= 0 :break# if i > 2 :#     breaki = i + 1print(reslist)return reslistif '__main__' == __name__:urls = getHospitalUrl()writeFile(urls)

(2)根据医院url获取该医院医生信息并存入MySQL。

import requests
from bs4 import BeautifulSoup
import re
import time
import pymysqldb = pymysql.Connect(host="localhost",port=3306,user="root",password="123456",db="databeijing",charset="utf8"
)# 医生类
class Doctor:name = None # 姓名title = "" # 职称hospital = None # 所属医院department = None # 所属科室clinic = None # 所属门诊score = 0 # 评分hits = 0 # 访问量lables = [] # 标签s_labels = ""impression = ""goodat = '暂无' # 擅长about = '暂无' # 简介number = ''def writeToDatabase(doctors, hos_id, cli_id): # 将医生信息写入数据库# try:cur = db.cursor()  # 获取游标sql = 'INSERT INTO t_doctor_qingdao(doc_name, doc_title, doc_hospital_name, doc_department_name, doc_clinic_name, doc_labels, doc_score,' \' doc_hits, doc_impression, doc_goodat, doc_about, doc_hospital_number, doc_clinic_number,doc_number) values(%s, %s, %s, %s, %s, %s, %s,%s, %s, %s, %s, %s, %s, %s)'for doctor in doctors :cur.execute(sql, (doctor.name, doctor.title, doctor.hospital, doctor.department, doctor.clinic, doctor.s_labels, doctor.score, doctor.hits, doctor.impression, doctor.goodat, doctor.about, hos_id, cli_id, doctor.number))db.commit()  # 提交# except:#     print('ERROR: writeToDatabase')##################  get_detail_doctor  ########################
# 由医生详细信息url,获取医生详细信息
def get_detail_doctor(doc_url, doctors, hospital_name, department_name, clinic_name):try:strHtml_detail = requests.get(doc_url)strHtml_detail.encoding = 'utf-8'soup_detail = BeautifulSoup(strHtml_detail.text, 'lxml')data_detail = soup_detail.select('#g-cfg > div.grid-group > div > div') # 个人详细信息页doc = Doctor()info = list(data_detail[0].children)[3] # 医生基本信息try :status = list(list(data_detail[1].children)[3].children) # 医生评价等信息except :print('ERROR: status')doc.name = info.h1.strong.text # 姓名titles = info.h1.find_all('span')  # 职称for title in titles:if title.text.strip() != '点赞':doc.title = doc.title + title.text.strip()if len(doc.title) <= 0 :doc.title = '无'doc.hospital = hospital_namedoc.department = department_namedoc.clinic = clinic_nametry :if (status[1].a.strong.text.strip() == '.0'): # 评分doc.score = 0else :doc.score = status[1].a.strong.text.strip()except :doc.score = 0try :num = status[3].strong.text.strip() # 访问量if (num == '暂无'):doc.hits = 0elif '万' in num:doc.hits = float(num[0:len(num)-1]) * 10000else:doc.hits = numexcept :try :doc.hits = int(list(soup_detail.find_all('p', class_='user-count'))[0].span.text.strip())except :doc.hits = 0lab = list(info.find_all("div", class_="keys")[0].children) # 主治标签i = 0if len(lab)>0 :for label in lab:if (i % 2 == 1):tmp = label.text.strip()doc.s_labels = doc.s_labels + '/' + tmpi = i + 1goodat = list(list(info.find_all("div", class_="goodat"))[0].find_all('a'))if len(goodat) > 0:doc.goodat = goodat[0]['data-description'].strip() # 擅长else:goodat = list(list(info.find_all("div", class_="goodat"))[0].find_all('span'))if len(goodat) > 0:doc.goodat = goodat[0].text.strip()  # 擅长about = list(list(info.find_all("div", class_="about"))[0].find_all('a'))if len(about) > 0:doc.about = about[0]['data-description'].strip()  # 简介else:about = list(list(info.find_all("div", class_="about"))[0].find_all('span'))if len(about) > 0:doc.abour = about[0].text.strip()  # 简介data_impression = soup_detail.select('#comment-filter > div:nth-child(1) > a.active > ul > li')if len(data_impression) > 0 :for item in data_impression :doc.impression = doc.impression + item.text.strip() + '/'if '?' in doc_url:i = doc_url.index('?')doc.number = doc_url[30:i]else:doc.number = doc_url[30:]output_detail_doctor(doc)doctors.append(doc)except :print('ERROR')###############  get_urls_simple  ###########################
#由门诊url,获取该门诊下全部医生的url
#医生列表无分页
def get_urls_simple(data, doctors, hospital_name, department_name, clinic_name):for item in data:url_detail = item.dl.dt.a['href']get_detail_doctor(url_detail, doctors, hospital_name, department_name, clinic_name)##############  get_urls_more  ###########################
#由门诊url,获取该门诊下全部医生的url
#医生列表数超过首页数目,需进入更多页面查看
def get_urls_more(cli_url, doctors, hospital_name, department_name, clinic_name):strHtml = requests.get(cli_url)strHtml.encoding = 'utf-8'soup = BeautifulSoup(strHtml.text, 'lxml')if len(soup.find_all('div', class_='pagers')) > 0: # 医生列表分页显示i = 1while True:url_page = cli_url + '?pageNo=' + str(i)strHtml = requests.get(url_page)strHtml.encoding = 'utf-8'soup_page = BeautifulSoup(strHtml.text, 'lxml')data = soup_page.select('#g-cfg > div.results > div.list > div')  # 医生列表for item in data:  # 该页医生循环try:url_detail = item.div.dl.dt.a['href']get_detail_doctor(url_detail, doctors, hospital_name, department_name, clinic_name)except:print('ERROR.100: get_urls_more')if len(soup_page.find_all('a', {'monitor':'page,page,page_down'})) <= 0:breaki = i + 1else: # 不需要分页data = soup.select('#g-cfg > div.results > div.list > div')  # 医生列表for item in data: # 该页医生循环try:url_detail = item.div.dl.dt.a['href']get_detail_doctor(url_detail, doctors, hospital_name, department_name, clinic_name)except:print('ERROR.100: get_urls_more')###########################  get_list_doctor  ############################
#由门诊url,获取该门诊全部医生的url
def get_list_doctor(cli_url, doctors, hospital_name, department_name, clinic_name):strHtml = requests.get(cli_url)strHtml.encoding = 'utf-8'soup = BeautifulSoup(strHtml.text, 'lxml')data = soup.select('#anchor > div.g-hddoctor-list.g-clear.js-tab-content > div') # 医生列表if (soup.find_all('div', class_='more')):get_urls_more(soup.find_all('div',class_='more')[0].a['href'], doctors, hospital_name, department_name, clinic_name)else :get_urls_simple(data, doctors, hospital_name, department_name, clinic_name)###################  output_detail_doctor  ##############################
#输出医生详细信息
def output_detail_doctor(doctor):print(doctor.name + '  :  ' + doctor.hospital + '  :  ' + doctor.department + '  :  ' + doctor.clinic + '  :  ' + str(doctor.number) + '  :  ' + str(doctor.hits))
print('============================================================================================')####################  output_list_doctors  ###########################
#输出列表中全部医生的详细信息
def output_list_doctors(doctors):for doctor in doctors:output_detail_doctor(doctor)####################  get_department  ###############################
#由医院url,获取该医院全部科室及该科室下全部门诊的url
def get_department(hos_id, hospital_name):hos_url = 'https://www.guahao.com/hospital/' + hos_idstrhtml = requests.get(hos_url)strhtml.encoding = 'utf-8'soup = BeautifulSoup(strhtml.text, 'lxml')data = soup.select('#departments > div.grid-content > ul > li')for department in data: # 科室循环department_name = department.label.text.strip()  # 科室名称print('=================================================================================================')print(department_name)# if department_name == '特色科室':clinics = department.p.find_all('span')  # 科室下门诊部 listfor clinic in clinics:doctors = [] # 暂存医生详细信息if clinic.a.get('title') == None:clinic_name = clinic.a.text.strip()  # 门诊名称else:clinic_name = clinic.a.get('title')  # 门诊名称cli_id = clinic.a['monitor-div-id'] # 门诊编号clinic_url = clinic.a['href']  # 门诊链接print('     ' + clinic_name)get_list_doctor(clinic_url, doctors,  hospital_name, department_name, clinic_name)writeToDatabase(doctors, hos_id, cli_id)#############  main  ######################
if __name__ == '__main__':urls = ['002d5d5e-b917-4c5a-8fc0-f394c293fd0a000', # 此为爬取的医院的编号]BaseUrl = 'https://www.guahao.com/hospital/'for hos_id in urls :url_hospital = BaseUrl + hos_idstrhtml = requests.get(url_hospital)strhtml.encoding = 'utf-8'soup = BeautifulSoup(strhtml.text, 'lxml')data = soup.select('#g-cfg > div.grid-group > section > div.info > div.detail.word-break')hospital_name = data[0].h1.strong.a.text.strip()  # 医院名称print(hospital_name)get_department(hos_id, hospital_name)

6.数据结果示例

【Python爬虫】之 抓取“微医”上的医生信息相关推荐

  1. Python爬虫入门教程 24-100 微医挂号网医生数据抓取

    1. 微医挂号网医生数据写在前面 今天要抓取的一个网站叫做微医网站,地址为 https://www.guahao.com ,我们将通过python3爬虫抓取这个网址,然后数据存储到CSV里面,为后面的 ...

  2. python爬虫批量抓取新浪微博用户ID及用户信息、微博内容

    老师给的任务,要对批量的微博文本进行舆情分析.第一步就是数据的抓取.在网上搜了一下大多是基于手机版网页(wap/cn)的爬虫,但是我在电脑上打不开这些网站. 由于自己也是网页小白,所以就参考着别人的代 ...

  3. Python爬虫新手教程:微医挂号网医生数据抓取

    1. 写在前面 今天要抓取的一个网站叫做微医网站,地址为 https://www.guahao.com ,我们将通过python3爬虫抓取这个网址,然后数据存储到CSV里面,为后面的一些分析类的教程做 ...

  4. Python爬虫新手教程:微医挂号网医生数据抓取!

    1. 写在前面 今天要抓取的一个网站叫做 微医 网站,地址为 https://www.guahao.com ,我们将通过python3爬虫抓取这个网址,然后数据存储到CSV里面,为后面的一些分析类的教 ...

  5. python 爬虫实战 抓取学校bbs相关板块的发帖信息

    1. 前言 之前也因为感兴趣, 写过一个抓取桌面天空里面喜欢的动漫壁纸的爬虫代码.这两天突然看到有人写了这么一篇文章: 爬取京东本周热卖商品基本信息存入MySQL 觉得蛮有趣的, 正好临近找工作的季节 ...

  6. python爬网页数据用什么_初学者如何用“python爬虫”技术抓取网页数据?

    原标题:初学者如何用"python爬虫"技术抓取网页数据? 在当今社会,互联网上充斥着许多有用的数据.我们只需要耐心观察并添加一些技术手段即可获得大量有价值的数据.而这里的&quo ...

  7. Python爬虫实战---抓取图书馆借阅信息

    Python爬虫实战---抓取图书馆借阅信息 原创作品,引用请表明出处:Python爬虫实战---抓取图书馆借阅信息 前段时间在图书馆借了很多书,借得多了就容易忘记每本书的应还日期,老是担心自己会违约 ...

  8. python抓取视频违法吗,科学网—【python爬虫】抓取B站视频相关信息(一) - 管金昱的博文...

    昨天我在B站上写了这么一篇文章,但是被他们锁住了.无奈之下我复制到知乎上先保存起来 在这篇名为<三天学会用python进行简单地爬取B站网页视频数据>文章中我主要提到了两点在已知aid的情 ...

  9. Python爬虫采集抓取:Python3.x+Fiddler 采集抓取 APP 数据

    随着移动互联网的市场份额逐步扩大,手机 APP 已经占据我们的生活,以往的数据分析都借助于爬虫采集爬取网页数据进行分析,但是新兴的产品有的只有 APP,并没有网页端这对于想要提取数据的我们就遇到了些问 ...

最新文章

  1. python转csv_python脚本如何将Excel文件转为csv文件(代码)
  2. Ubuntu伪分布式hadoop安装
  3. 阿里云发布 Spring Boot 新脚手架,真香
  4. SAP S/4HANA使用ABAP获得生产订单的状态 1
  5. phpize增加php模块
  6. 【Spring学习笔记-MVC-17】Spring MVC之拦截器
  7. 从王者荣耀里我学会的前端新手指引
  8. win10语音语言服务器,win10系统:朗读女语音库(发音人)安装方法说明
  9. centos rpm安装zabbix
  10. EMNLP'21 | 基于稀疏子网络的领域迁移
  11. SzNOI语法百题之总目录
  12. 网络工程师干货:华为设备故障管理命令大全
  13. 什么是电磁兼容标准?IEC、EN、TC77、CISPR等的区别!
  14. Revit二次开发——HelloRevitApp
  15. macbook安装佳能C3020打印机驱动安装错误代码701
  16. 港科百创 |【香港科大科创产业联盟】正式成立,创始理事相聚第四期科创午餐会...
  17. python模拟支付宝扫码登录_Python接入支付宝进行PC端支付
  18. 冯绍峰晒与韩寒片场邋遢照 自称工地领薪水
  19. unity 球体表面平均分割点
  20. Kubernetes实战[2]: 服务发现机制与Cluster DNS的安装(无CA认证版)

热门文章

  1. matlab极性电容叫什么,电解电容与无极性电容之间的区别是什么
  2. Java学习日记:set集合(不多用)
  3. Mac 本地下载安装Nginx
  4. 基于Android平台的虚拟试衣关键技术研究
  5. WriteError Resulting document after update is larger than 16777216
  6. 计算机图形学-光栅渲染概述
  7. 云ERP的创新到底在哪?
  8. ANSYS Workbench 16 - 黄志新(图书阅读总结)
  9. Android热更新十:自己写一个Android热修复
  10. 看看 NF_HOOK 宏