【爬虫】丁香医生网站所有疾病信息

本文介绍了如何使用Selenium和BeautifulSoup处理JavaScript动态加载的医疗网站数据,通过模拟浏览器行为抓取心血管内科A、B开头的疾病列表和详情,展示了从网页抓取、解析到数据存储的完整过程。
摘要由CSDN通过智能技术生成

先看代码运行结果,为方便测试、减少循环次数,只爬取了心血管内科A、B开头的六种疾病数据。
在这里插入图片描述

总体思路

“查疾病”这部分内容按科室分类,各个科室网站不同。在最初尝试中,我使用 requests 库获取网页内容,但发现获取到的 HTML 源码与我在网站上通过开发者模式查看到的代码不一致。经过 GPT 解释,网站采用 JavaScript 进行动态加载内容,而我所获取的仅仅是初始加载时的 HTML 源码,不包含通过 JavaScript 动态生成的内容。

因此,我转向使用 Selenium 模拟浏览器行为。这也为我解决在网页不发生跳转的情况下,如何获取每个首字母下的疾病列表提供了思路。

为了获取每个科室下按疾病首字母分类的疾病列表,我编写了程序自动点击每个字母按钮,获取相应的动态源码。随后,我通过循环对这些源码进行解析,提取了详细介绍每个疾病的网站跳转链接。

最后的工作就是对每个疾病页面进行解析,获取了相关的疾病信息。

函数说明

  1. get_static_url_content_after_click(driver, url, button_text)

    • 作用:使用 Selenium WebDriver 模拟点击操作后获取静态网页内容。
    • 参数:
      • driver: WebDriver对象,用于控制浏览器。
      • url: 要打开的网页URL。
      • button_text: 页面中的按钮文本。
    • 返回:BeautifulSoup对象,包含解析后的页面内容。
  2. start_crawler(subject)

    • 作用:开始爬取特定科室的链接。
    • 参数:
      • subject: 科室的编号或名称。
    • 返回:包含科室链接列表的列表。
  3. get_static_url_content(url)

    • 作用:使用 Selenium WebDriver 获取指定 URL 的静态网页内容。
    • 参数:
      • url (str): 要获取内容的网页链接。
    • 返回:BeautifulSoup对象,用于解析HTML。
  4. get_disease_info(url)

    • 作用:获取特定疾病的详细信息。
    • 参数:
      • url: 疾病页面的链接。
    • 返回:包含疾病信息的字典或其他数据结构。
  5. write_to_file(file_name, content)

    • 作用:将内容写入指定的文件(追加模式)。
    • 参数:
      • file_name (str): 要写入的文件的名称。
      • content (dict): 要写入文件的内容,这里假设是一个字典。
    • 返回:无。

main函数调用逻辑

  1. 创建一个映射字典 subject_mapping 用于科室编号和名称的映射。
  2. 创建一个字典 all_subject_links 用于存储每个科室的疾病链接。
  3. 针对每个科室,调用 start_crawler 函数获取疾病链接,并存储在 all_subject_links 字典中。
  4. 对于每个科室,循环遍历其链接,然后对每个链接调用 get_disease_info 函数获取疾病信息,最后调用 write_to_file 函数将疾病信息以 JSON 格式写入文件中。

代码

from bs4 import BeautifulSoup
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import json


def get_static_url_content_after_click(driver, url, button_text):
    """
    使用 Selenium WebDriver 模拟点击操作后获取静态网页内容。

    Parameters:
    - driver: WebDriver对象,用于控制浏览器
    - url: 要打开的网页URL
    - button_text: 页面中的按钮文本

    Returns:
    - bsObj: BeautifulSoup对象,包含解析后的页面内容
    """
    # 打开网页
    driver.get(url)

    # 使用 WebDriverWait 等待页面元素加载
    button = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.XPATH,
                                          f"//div[@class='tag-button' and text()='{button_text}'] | //div[@class='tag-button active' and text()='{button_text}']"))
    )

    # 点击按钮
    button.click()

    # 获取动态加载后的内容
    page_source = driver.page_source

    # 使用BeautifulSoup解析页面
    bsObj = BeautifulSoup(page_source, 'lxml')

    return bsObj


def start_crawler(subject):
    """
    开始爬取特定科室的链接

    Parameters:
    - subject: 科室的编号或名称

    Returns:
    - subject_link: 包含科室链接列表的列表
    """
    # 设置 Chrome 驱动的路径
    chrome_path = "D:\\毕设\\构建数据阶段\\chromedriver-win64\\chromedriver.exe"

    # 创建 Chrome 驱动
    chrome_service = ChromeService(chrome_path)
    driver = webdriver.Chrome(service=chrome_service)

    url_template = 'https://dxy.com/diseases/%s'

    # 创建一个列表用于存储每个科室的链接列表
    subject_link = []

    for letter in "AB":
        url = url_template % (subject)
        try:
            # 尝试获取当前字母的页面
            bsObj = get_static_url_content_after_click(driver, url, letter)

            # 找到所有class为"section-card common-card-link"的a标签
            section_cards = bsObj.find_all('a', class_='section-card common-card-link')

            # 提取每个a标签中的href属性
            href_list = [card['href'] for card in section_cards]

            print(f'Subject: {subject}, Letter: {letter}')
            print(href_list)

            # 将链接添加到列表中
            subject_link.append(href_list)

        except TimeoutException:
            # 如果超时异常发生,说明当前字母不存在,跳过本轮循环
            print(f"Subject: {subject}, Letter: {letter} not found, skipping...")
            continue

    # 关闭浏览器
    driver.quit()

    return subject_link


def get_static_url_content(url):
    """
    使用 Selenium WebDriver 获取指定 URL 的静态网页内容。

    Parameters:
    - url (str): 要获取内容的网页链接。

    Returns:
    - bsObj: BeautifulSoup对象,用于解析HTML。
    """
    # 设置 Chrome 驱动的路径
    chrome_path = "D:\\毕设\\构建数据阶段\\chromedriver-win64\\chromedriver.exe"

    # 创建 Chrome 驱动
    chrome_service = ChromeService(chrome_path)
    driver = webdriver.Chrome(service=chrome_service)

    try:
        # 打开指定的 URL
        driver.get(url)

        # 获取网页内容
        page_content = driver.page_source

        # 使用BeautifulSoup解析HTML
        bsObj = BeautifulSoup(page_content, 'lxml')

    except Exception as e:
        print(f"获取网页内容时发生错误:{e}")
        bsObj = None

    # 关闭浏览器
    driver.quit()
    return bsObj


def get_disease_info(url):
    """
    获取特定疾病的详细信息

    Parameters:
    - link: 疾病页面的链接

    Returns:
    - disease_info: 包含疾病信息的字典或其他数据结构
    """

    bsObj = get_static_url_content(url)

    # 返回一个包含疾病信息的字典
    disease_info = {
        'name': None,
        'introduction': [],
        'symptoms': [],
        'causes': [],
        'diagnosis': [],
        'treatments': [],
        'lifestyle': [],
        'prevention': [],
    }

    # 提取疾病名称
    name_element = bsObj.find('div', class_='high-light tag-content-title')
    if name_element:
        disease_info['name'] = name_element.text.strip()

    # 提取疾病信息
    info_elements = bsObj.find_all('div', class_='html-parse tag-html')
    i = 0
    for info_element in info_elements:
        info_text = info_element.get_text(strip=True)

        # 根据i值确定存储字段
        if i == 0:
            disease_info['introduction'] = info_text.strip()
        elif i == 1:
            disease_info['symptoms'] = info_text.strip()
        elif i == 2:
            disease_info['causes'] = info_text.strip()
        elif i == 3:
            disease_info['diagnosis'] = info_text.strip()
        elif i == 4:
            disease_info['treatments'] = info_text.strip()
        elif i == 5:
            disease_info['lifestyle'] = info_text.strip()
        elif i == 6:
            disease_info['prevention'] = info_text.strip()
        else:
            # 处理未知情况
            pass

        i = i+1
    return disease_info


def write_to_file(file_name, content):
    """
    将内容写入指定的文件(追加模式)。

    Parameters:
    - file_name (str): 要写入的文件的名称。
    - content (dict): 要写入文件的内容,这里假设是一个字典。

    Returns:
    - 无
    """
    try:
        with open(file_name, 'a', encoding='utf-8') as file:
            json_content = json.dumps(content, ensure_ascii=False, indent=2)
            file.write(json_content + '\n@@@@@@\n')
            print(f"成功写入文件 {file_name}")
    except Exception as e:
        print(f"写入文件 {file_name} 时发生错误:{e}")


if __name__ == '__main__':
    # 创建字典用于映射科室数字和名称
    subject_mapping = {'6133': '心血管内科'}

    # 创建字典用于存储每个科室的疾病链接
    all_subject_links = {}

    for subject_number, subject_name in subject_mapping.items():
        links = start_crawler(subject_number)
        all_subject_links[subject_name] = links

    print(all_subject_links)

    for subject_name, subject_links in all_subject_links.items():
        # 创建一个以科室命名的文件
        file_name = f"{subject_name}.txt"

        for links in subject_links:
            for link in links:
                disease_info = get_disease_info(link)

                # 将疾病信息写入文件
                write_to_file(file_name, disease_info)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值