您现在的位置是:首页 >技术教程 >Python爬虫实战:获取中药网站药物编号对应表网站首页技术教程

Python爬虫实战:获取中药网站药物编号对应表

敖然722 2025-07-21 12:01:04
简介Python爬虫实战:获取中药网站药物编号对应表

引言

最近在做与中药材有关的项目,有获取中药实时价格的需要,因此就需要通过程序从网络上获取到中药的价格。

分析

在网上随便搜了搜,发现这个网站的药材种类要多一点,可能会符合采集需求,因此来到这个网站进行分析。

我们随便使用一下这个网站的各功能,观察网页页面的变化。

随便搜索药品阿胶,可以发现,网页页面多了?Mid=1部分,把页面改成?Mid=2,变成了药物阿魏的界面,那么很明显了,每种药品都有一个对应编号的页面,我们在需要爬取药材价格的时候,只需要向药材对应编号的链接请求再进行处理就行了,接下来我们进入网站的开发者模式,分析如何提取需要数据。

查看页面的源代码,可以发现药材名及其编号都在 <a href="?Mid=1">阿胶</a>这一标签中,那么我们在分析爬取到的内容时,首先提取所有的<a>标签,再提取所有<a>标签中带href和?Mid=的文本内容就行了,最后再将编号和对应的药材写入同一个文本文件即可。

程序编写

引入库

import requests
from bs4 import BeautifulSoup
import time

引入这几个库,没什么好说的.

requests库用来发请求,获取网页内容。

BeautifulSoup库用来解析提取获取到的html内容。

time库用来设置时间间隔,避免对网站造成压力。

构造请求链接

def construct_url(base_url, number):
    """
    构造请求的 URL
    :param base_url: 基础 URL
    :param number: 参数值
    :return: 完整的 URL
    """
    order = f"Mid={number}"
    return f"{base_url}?{order}"

我们不难发现我们访问的链接是由网站域名加?Mid=number组成的,每次爬取内容时对number进行自增操作,转换成字符型后再与域名拼接在一起再进行请求就行。

发送请求并解析html文件

def fetch_and_parse(url):
    """
    发送请求并解析 HTML 内容
    :param url: 请求的 URL
    :return: BeautifulSoup 对象
    """
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        response.encoding = response.apparent_encoding
        return BeautifulSoup(response.text, 'html.parser')
    except requests.RequestException as e:
        print(f"请求出错: {e}")
    except Exception as e:
        print(f"发生其他错误: {e}")
    return None

在get请求中加入头文件,模拟成浏览器(试过了,不加会被拦截)

这一段代码很简单,没什么好说的。

提取标签

def extract_links(soup, number, printed_content, writer):
    """
    提取符合条件的 <a> 标签文本内容,相同内容只输出一次,并以药品,编号数字的形式写入 CSV 文件,同时保留 print 输出
    :param soup: BeautifulSoup 对象
    :param number: 当前请求的 number
    :param printed_content: 已输出内容的集合
    :param writer: CSV 文件写入器对象
    """
    if soup:
        source_tag = soup.find_all('a')
        for a_tag in source_tag:
            href = a_tag.get('href')
            if href and href.startswith('?Mid='):
                text = a_tag.get_text()
                if text not in printed_content:
                    output = f"{text},{number}"
                    print(output)
                    writer.writerow([text, number])
                    printed_content.add(text)

思路在上边分析的时候已经说过了,很简单,先提取所有<a>标签。再找所有<a>标签以?Mid=开头的href,然后后续输出内容并以csv格式文件保存(方便提取的内容写入文件后续用csv库读取)

输出内容并写入文件

if __name__ == "__main__":
    source_url = 'http://price.rz55.com/default.aspx'
    # 用于记录已经输出过的内容
    printed_content = set()
    # 使用原始字符串表示路径
    with open(r'E:Project FilesRepositoryPythonCrawleroutput1.csv', 'w', newline='', encoding='utf-8-sig') as csvfile:
        writer = csv.writer(csvfile)
        # 修正拼写错误
        writer.writerow(['herb', 'number'])
        for number in range(1, 1200):
            url = construct_url(source_url, number)
            soup = fetch_and_parse(url)
            extract_links(soup, number, printed_content, writer)
            time.sleep(5)

我这里写入的文件绝对路径是我的工作区的路径,自己用的时候改成自己的就行了。

并且为了防止网站压力过大,所以每5秒向网站请求一次,可以根据个人实际情况更改。

运行结果

数据采集十分顺利,去重也做得很好,没有重复药材的情况,生成的.csv文件也一切正常,接下来就可以通过这个csv文件作为索引表再单独爬取各药材对应编号的链接就行了。

同样的思路可以应用到很多相仿的网站上,比如期货,首先获取所有种类对应的编号,后续在爬取实时价格时直接使用索引中对应的编号就行了。

完整代码

import requests
from bs4 import BeautifulSoup
import csv
import os


def construct_url(base_url, number):
    """
    构造请求的 URL
    :param base_url: 基础 URL
    :param number: 参数值
    :return: 完整的 URL
    """
    order = f"Mid={number}"
    return f"{base_url}?{order}"


def fetch_and_parse(url):
    """
    发送请求并解析 HTML 内容
    :param url: 请求的 URL
    :return: BeautifulSoup 对象
    """
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        response.encoding = response.apparent_encoding
        return BeautifulSoup(response.text, 'html.parser')
    except requests.RequestException as e:
        print(f"请求出错: {e}")
    except Exception as e:
        print(f"发生其他错误: {e}")
    return None


def extract_links(soup, number, printed_content, writer):
    """
    提取符合条件的 <a> 标签文本内容,相同内容只输出一次,并以药品,编号数字的形式写入 CSV 文件,同时保留 print 输出
    :param soup: BeautifulSoup 对象
    :param number: 当前请求的 number
    :param printed_content: 已输出内容的集合
    :param writer: CSV 文件写入器对象
    """
    if soup:
        source_tag = soup.find_all('a')
        for a_tag in source_tag:
            href = a_tag.get('href')
            if href and href.startswith('?Mid='):
                text = a_tag.get_text()
                if text not in printed_content:
                    output = f"{text},{number}"
                    print(output)
                    writer.writerow([text, number])
                    printed_content.add(text)


if __name__ == "__main__":
    source_url = 'http://price.rz55.com/default.aspx'
    # 用于记录已经输出过的内容
    printed_content = set()
with open('E:Project FilesRepositoryPythonCrawleroutput1.csv', 'w', newline='', encoding='utf-8-sig') as csvfile:
    # 后续代码保持不变
        writer = csv.writer(csvfile)
        # 写入 CSV 文件的表头
        writer.writerow(['herb', 'number'])
        for number in range(1, 1200):
            url = construct_url(source_url, number)
            soup = fetch_and_parse(url)
            extract_links(soup, number, printed_content, writer)

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。