weixu 发表于 2025-2-6 13:10:38

爬虫实战带上代码讲解(以爬取好大夫为例)

前言:
我感觉之前讲的爬虫,太纸面化了,需要给一些实例来帮助理解。毕竟爬虫这项技能,我们经常可能用到,通常用于爬虫数据来训练模型。
延续上一篇文章所说将爬虫分为四个主要部分:
获取网页源代码
解析网页并提取数据
实现自动化抓取逻辑
保存数据到文件(如 execl)
第一步:获取网页源代码
要获取网页内容,最直接的方式是使用 requests 库。帮助我们发送 HTTP 请求并获取网页内容。以下是一个基本示例:
import requestsurl = "https://example.com"# 目标网页地址headers = {    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",   "Cookie": "your_cookie_value"}# 发送请求并获取响应response = requests.get(url, headers=headers)html_content = response.text# 获取网页的 HTML 源代码参数说明:
url:目标网页的地址。
headers:请求头,用于伪装成真实浏览器,避免被识别为爬虫。关键字段包括:
User-Agent:指定用户代理信息。
Cookie:某些网站需要登录后才能访问完整内容,Cookie 可以帮助模拟登录状态。
得按照实践情况来添加参数
小贴士:
用F12,查看页面请求时的Cookies,便于构造请求头。
使用 response.status_code 确认请求是否成功(200 表示成功)。
第二步:解析网页并提取数据
获取网页源代码后,需要从中提取出关键信息。这一步可以借助 BeautifulSoup。
示例:
def parse_html(html):    soup = BeautifulSoup(html, 'html.parser')    old_disease_info = soup.find('p', class_='diseaseinfo') #疾病信息    disease_info=get_all_text(old_disease_info)    #print(disease_info)    header_title = soup.find('h1', class_='header-title')    text = header_title.get_text(strip=True)    type = text.split('- ')[-1]#问诊类型    #print(type)    meta_og_url = soup.find('meta', property='og:url')    disease_url = meta_og_url.get('content') #病例url    #print(disease_url)    a_tag = soup.find('a', attrs={'target': '_blank', 'rel': 'noreferrer'})    doctor_url = a_tag.get('href')#医生url    #print(doctor_url)    card_info_text = soup.find('div', class_='card-info-text')    doctor_info=get_all_text(card_info_text) #医生简介    #print(doctor_info)    speciality_div = soup.select_one('div')    if speciality_div:      doctor_speciality = get_all_text(speciality_div)# 医生擅长    else:      doctor_speciality='无说明'    #print(doctor_speciality)    doctor_card_service = soup.find('div', class_='doctor-card-service clearfix')    doctor_service = get_all_text(doctor_card_service) #医生服务质量    #print(doctor_service)    suggestions_marginLeft0 = soup.find('section', class_='suggestions')    doctor_suggestions = get_all_text(suggestions_marginLeft0) #医生建议    #print(doctor_suggestions)    msgboard_js_msgboard = soup.find('section', class_='msgboard')    message= get_all_text(msgboard_js_msgboard) #医生与患者交流    #print(message)    return disease_info,type,disease_url,doctor_url,doctor_info,doctor_speciality,doctor_service,doctor_suggestions,message这部分是用于解析解析单个病例详细页

该页面的所需内容如下
根据以下图片来进行解析网页并提取数据

就简单拿示例来进行讲解:
1.soup = BeautifulSoup(html, 'html.parser')
html.parser 作为解析器,将 html 解析成 soup 对象,方便后续提取数据。
2.old_disease_info = soup.find('p', class_='diseaseinfo')# 疾病信息
disease_info = get_all_text(old_disease_info)
查找<p>标签,并且该标签的 class 是 "diseaseinfo",即疾病描述信息。
get_all_text(old_disease_info) 是一个自定义函数,它会递归提取该标签及其子标签的所有文本信息。之所以这里创建get_all_text()函数是因为<p>的信息不是连续的而是分布在里面的子标签里面
其代码为
def get_all_text(element):    text = ''    for content in element.contents:      if content.name is None:            text += content      else:            text += get_all_text(content)# 递归获取子元素的文本    return text如下

3.header_title = soup.find('h1', class_='header-title')
text = header_title.get_text(strip=True)
type = text.split('- ')[-1]# 问诊类型
查找 <h1> 标签,class 为 "header-title",表示文章标题。
get_text(strip=True) 获取标题文本内容,并去除两端的空白字符。
text.split('- ')[-1] 提取问诊类型(格式为 "描述 - 问诊类型" 这样的格式)。
如下

4.meta_og_url = soup.find('meta', property='og:url')
disease_url = meta_og_url.get('content')# 病例url
查找标签,property 属性为 "og:url",通常用于存储当前页面的 URL。
get('content') 提取 content 属性值,即病例的 URL。
如下

5.a_tag = soup.find('a', attrs={'target': '_blank', 'rel': 'noreferrer'})
doctor_url = a_tag.get('href')# 医生url
get('href') 获取医生的个人主页链接。
6.card_info_text = soup.find('div', class_='card-info-text')
doctor_info = get_all_text(card_info_text)# 医生简介
查找 div 标签,class 为 "card-info-text",其中包含医生的简要信息。
get_all_text(card_info_text) 提取该 div 的所有文本信息。
也是因为其信息在子标签里面
如下

7.speciality_div = soup.select_one('div')
if speciality_div:
doctor_speciality = get_all_text(speciality_div)# 医生擅长
else:
doctor_speciality = '无说明'
有些病例url里面医生是没有填写擅长的,所以如果 speciality_div 存在,则提取文本,否则返回 "无说明"。
如下

8.doctor_card_service = soup.find('div', class_='doctor-card-service clearfix')
doctor_service = get_all_text(doctor_card_service)# 医生服务质量
查找 div 标签,class 为 "doctor-card-service clearfix",存储医生的服务评分或质量评价。
get_all_text(doctor_card_service) 提取该 div 内的文本。
如下

9.suggestions_marginLeft0 = soup.find('section', class_='suggestions')
doctor_suggestions = get_all_text(suggestions_marginLeft0)# 医生建议
查找 section 标签,class 为 "suggestions",表示医生的建议。
get_all_text(suggestions_marginLeft0) 提取医生对患者的建议文本。
(因为我现在忘记密码了,是未登录状态,因此展示不了)
10.msgboard_js_msgboard = soup.find('section', class_='msgboard')
message = get_all_text(msgboard_js_msgboard)# 医生与患者交流
查找 section 标签,class 为 "msgboard",表示医生和患者的留言互动。
get_all_text(msgboard_js_msgboard) 提取交流信息的文本。
(因为我现在忘记密码了,是未登录状态,因此展示不了)
第三步:实现自动化抓取逻辑
为了抓取页面信息,可能需要采用构造分页参数等操作。
如本次爬取好大夫病例时,我们可以看到

在源代码下结构为

我们需要爬取里面的每种类型的病例
因此我们目的是从 HTML 页面中提取大类和小类的信息,并将它们组织到一个字典中。字典的结构如下:
{    '大类1': [      {'name': '小类1', 'url': '小类URL?p='},      {'name': '小类2', 'url': '小类URL?p='},      ...    ],    '大类2': [      {'name': '小类1', 'url': '小类URL?p='},      ...    ],    ...}因此帮忙写下以下代码
def gain_typeurl(type_web_html):    soup = BeautifulSoup(type_web_html, 'html.parser')    type_dict={}    big_types = soup.find_all('ul', class_=lambda x: x is None )    print(big_types)    for big_type in big_types:      big_type_tag = big_type.find(class_="izixun-department-title")      big_type_name = big_type_tag.text.strip()      type_dict = []      small_type_tags = big_type.find(class_="izixun-department-list")      small_tags = small_type_tags.find_all('a')      for small_tag in small_tags:            small_type_name = small_tag.text.strip()            small_type_url =small_tag['href']            type_dict.append({                'name': small_type_name,                'url': small_type_url+"?p=",            })    print(type_dict)    return type_dict但是每一类在线诊断,不只有一个页面,还是有10页

那我们需要有在url后加一个页面参数
def directory_pachong(url, name):    create_execl(name)    for i in range(1, 11):      turl = url      turl = "https:"+ turl + str(i)      print(url)      directory = get_html(turl)      directory_url = gain_url(directory)      z = 0      for d_url in directory_url:            html = get_html(d_url)            data = parse_html(html)            write_back_execl(data,name)            z = z + 1            l = (i - 1) * 90 + z            print(l)该函数的目标是:
循环抓取多个分页(10 页)
对每一页中的所有链接进行访问,并从每个链接的页面中提取数据。
提取的数据会被逐页、逐条写入到 execl文件中,最终保存所有爬取的数据。
接下来我们需要获取,该页面下的全部病历url

比如现在我们就进去了心血管内科
def gain_url(directory):    soup = BeautifulSoup(directory, 'html.parser')    directory_url=[]    list_items = soup.find_all('span', class_='fl')    for item in list_items:      a_tag = item.find('a')      url = a_tag.get('href')      directory_url.append(url)    print(directory_url)    return directory_urlgain_url(directory) 函数的目的是从传入的 HTML 页面中提取所有符合条件的 URL 链接,并将它们保存在一个列表中返回。通过查找页面中的 span 标签(类名为 'fl')来定位包含 url 的 a 标签,然后提取出每个 a 标签的 href 属性值。
第四步:保存数据到 execl文件
爬取的数据需要持久化存储。execl文件是一种常用的结构化存储方式,可通过 openpyxl 库实现。
首先来创建excel的
def create_execl(name):    wb = Workbook()    ws = wb.active    ws.title = name    excel_headers = ["疾病信息", "问诊类型", "病例url", "医生url", "医生简介", "医生擅长", "医生服务质量", "医生建议", "医生与患者交流"]    ws.append(excel_headers)    wb.save(name+".xlsx")创建一个新的 execl,并获取取当前活动工作表。
设置该名称为传入的 name。
添加一行表头,如代码中的就是表头为疾病信息、问诊类型、病例 URL、医生简介等字段。
将该保存为一个 execl文件,文件名为 name.xlsx。
接下来到添加数据到execl
def write_back_execl(data, name):    #wb = Workbook()    wb = load_workbook(name+".xlsx")    ws = wb.active    ws.append(data)    wb.save(name+".xlsx")该函数的目的是:
加载指定的 execl文件(name + ".xlsx")。
获取文件中的活动工作表。
将传入的 data 列表作为一行数据追加到该工作表的末尾。
保存修改后的 execl文件。
完整代码:
import requestsfrom bs4 import BeautifulSoupfrom openpyxl import Workbookfrom openpyxl.reader.excel import load_workbookheaders = {    "cookie": "自己添加",    "user-agent": "自己添加",}def gain_typeurl(type_web_html):    soup = BeautifulSoup(type_web_html, 'html.parser')    type_dict={}    big_types = soup.find_all('ul', class_=lambda x: x is None )    print(big_types)    for big_type in big_types:      big_type_tag = big_type.find(class_="izixun-department-title")      big_type_name = big_type_tag.text.strip()      type_dict = []      small_type_tags = big_type.find(class_="izixun-department-list")      small_tags = small_type_tags.find_all('a')      for small_tag in small_tags:            small_type_name = small_tag.text.strip()            small_type_url =small_tag['href']            type_dict.append({                'name': small_type_name,                'url': small_type_url+"?p=",            })    print(type_dict)    return type_dictdef gain_url(directory):    soup = BeautifulSoup(directory, 'html.parser')    directory_url=[]    list_items = soup.find_all('span', class_='fl')    for item in list_items:      a_tag = item.find('a')      url = a_tag.get('href')      directory_url.append(url)    print(directory_url)    return directory_urldef get_html(url):    response = requests.get(url, headers=headers)    return response.text# 获取所有文本def get_all_text(element):    text = ''    for content in element.contents:      if content.name is None:            text += content      else:            text += get_all_text(content)# 递归获取子元素的文本    return textdef parse_html(html):    soup = BeautifulSoup(html, 'html.parser')    old_disease_info = soup.find('p', class_='diseaseinfo') #疾病信息    disease_info=get_all_text(old_disease_info)    #print(disease_info)    header_title = soup.find('h1', class_='header-title')    text = header_title.get_text(strip=True)    type = text.split('- ')[-1]#问诊类型    #print(type)    meta_og_url = soup.find('meta', property='og:url')    disease_url = meta_og_url.get('content') #病例url    #print(disease_url)    a_tag = soup.find('a', attrs={'target': '_blank', 'rel': 'noreferrer'})    doctor_url = a_tag.get('href')#医生url    #print(doctor_url)    card_info_text = soup.find('div', class_='card-info-text')    doctor_info=get_all_text(card_info_text) #医生简介    #print(doctor_info)    speciality_div = soup.select_one('div')    if speciality_div:      doctor_speciality = get_all_text(speciality_div)# 医生擅长    else:      doctor_speciality='无说明'    #print(doctor_speciality)    doctor_card_service = soup.find('div', class_='doctor-card-service clearfix')    doctor_service = get_all_text(doctor_card_service) #医生服务质量    #print(doctor_service)    suggestions_marginLeft0 = soup.find('section', class_='suggestions')    doctor_suggestions = get_all_text(suggestions_marginLeft0) #医生建议    #print(doctor_suggestions)    msgboard_js_msgboard = soup.find('section', class_='msgboard')    message= get_all_text(msgboard_js_msgboard) #医生与患者交流    #print(message)    return disease_info,type,disease_url,doctor_url,doctor_info,doctor_speciality,doctor_service,doctor_suggestions,messagedef create_execl(name):    wb = Workbook()    ws = wb.active    ws.title = name    excel_headers = ["疾病信息", "问诊类型", "病例url", "医生url", "医生简介", "医生擅长", "医生服务质量", "医生建议", "医生与患者交流"]    ws.append(excel_headers)    wb.save(name+".xlsx")def write_back_execl(data, name):    #wb = Workbook()    wb = load_workbook(name+".xlsx")    ws = wb.active    ws.append(data)    wb.save(name+".xlsx")def directory_pachong(url, name):    create_execl(name)    for i in range(1, 11):      turl = url      turl = "https:"+ turl + str(i)      print(url)      directory = get_html(turl)      directory_url = gain_url(directory)      z = 0      for d_url in directory_url:            html = get_html(d_url)            data = parse_html(html)            write_back_execl(data,name)            z = z + 1            l = (i - 1) * 90 + z            print(l)if __name__ == "__main__":    #directory_pachong()    xurl = "https://www.haodf.com/bingcheng/list.html"    url_html=get_html(xurl)    all_type=gain_typeurl(url_html)    for big_type, small_types in all_type.items():      print(f"大类: {big_type}")      for small_type in small_types:            print(f"\t小类: {small_type['name']} - 链接: {small_type['url']}")            small_name=small_type['name']            small_url=small_type['url']            directory_pachong(small_url,small_name)存在缺陷
目前好大夫需要登录后才能查看,我之前一个师兄给了有一个vip账号,但是每天只能爬200条十分鸡肋。(就是来跟大家交流学习的作用而已)
一言两语
明天再战科目三了,十分紧张,一定得过啊。
页: [1]
查看完整版本: 爬虫实战带上代码讲解(以爬取好大夫为例)