3. 数据获取与爬虫:先学会怎么「拿」到数据
做知识图谱,第一步永远是数据。你想想看,没有数据,后面的实体抽取、关系构建全都是空谈。这一章我们就聊 Python 爬虫——说白了,就是写段程序,替你去网页上把药物信息搬回来。
我刚开始接触爬虫时,觉得这东西挺黑科技的。后来发现核心就两件事:发请求和解析响应。本章我们就围绕这两个点展开,最后用一个真实案例串起来。
3.1 爬虫到底在做什么?
先理清概念。爬虫模拟浏览器向服务器发送 HTTP 请求,拿到 HTML 页面,然后从页面里提取你需要的数据。整个过程可以画成下面这张图:
我的经验:别一上来就想爬几百个网站。先拿一个页面练手,把流程跑通,再考虑规模化。我在第一个项目中就是因为贪多,被反爬机制封了一堆 IP,最后老老实实从单页开始。
3.2 基础工具:requests 和 BeautifulSoup
Python 里爬虫工具很多,但我个人最常用就是这两个:requests 发请求,BeautifulSoup 做解析。为什么?因为它们轻量、文档好、社区活跃。不信你去看看 GitHub,星数摆在那。
3.2.1 requests:用最少的代码发请求
安装只需要一行命令:
pip install requests
然后就能写请求了。一个最简单的 GET 请求:
import requests
url = "https://example.com/drug/aspirin"
response = requests.get(url)
print(response.status_code) # 200 表示成功
print(response.text[:200]) # 看看页面开头的内容
如果目标网站有反爬,你经常需要模拟浏览器。加个 User-Agent 头就够了:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..."
}
response = requests.get(url, headers=headers)
注意:不要忘记加超时参数。我曾经写过一个爬虫,没设超时,结果碰到一个慢响应,程序卡了半天。正确做法:requests.get(url, timeout=5)
3.2.2 BeautifulSoup:把 HTML 变成可操作的对象
拿到了 HTML 文本,怎么提取数据?正则表达式当然可以,但太麻烦。BeautifulSoup 能把 HTML 解析成一棵树,然后像操作字典一样取内容。
安装:
pip install beautifulsoup4
解析器我推荐 html.parser,Python 自带,不用额外装:
from bs4 import BeautifulSoup
html = "<html><body><div class='drug-name'>阿司匹林</div></body></html>"
soup = BeautifulSoup(html, "html.parser")
# 通过 class 找到元素
name_tag = soup.find("div", class_="drug-name")
print(name_tag.text) # 输出:阿司匹林
常用的查找方法有这些:
| 方法 | 作用 | 示例 |
|---|---|---|
find() |
返回第一个匹配的元素 | soup.find("span", id="price") |
find_all() |
返回所有匹配元素列表 | soup.find_all("li", class_="drug-item") |
select() |
CSS 选择器风格 | soup.select("div.drug-info > p") |
.text |
获取元素内纯文本 | tag.text |
.get("href") |
获取属性值 | link.get("href") |
3.3 实战:爬取药物基本信息
光讲理论没意思。我们挑一个真实场景——从某医学百科爬取药物的商品名、通用名和适应症。为了演示方便,我用一个本地测试 HTML 做例子,实际你在项目里换真实 URL 就行。
3.3.1 分析页面结构
我习惯先手动打开网页,用浏览器开发者工具看元素的 class 或 id。比如目标页面上,药物名称在一个 <h1 class="drug-title"> 里,适应症在 <div class="indications"> 里。明确目标后再写爬虫,效率高很多。
3.3.2 完整爬虫脚本
import requests
from bs4 import BeautifulSoup
import time
import csv
def fetch_drug_info(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
try:
resp = requests.get(url, headers=headers, timeout=5)
resp.raise_for_status() # 非200会自动抛异常
except requests.RequestException as e:
print(f"请求失败: {e}")
return None
soup = BeautifulSoup(resp.text, "html.parser")
# 提取名称
name_tag = soup.find("h1", class_="drug-title")
drug_name = name_tag.text.strip() if name_tag else "未知"
# 提取适应症
ind_tag = soup.find("div", class_="indications")
indications = ind_tag.text.strip() if ind_tag else "无"
return {
"name": drug_name,
"indications": indications
}
# 测试单页
url = "https://fake-med-site.com/drugs/aspirin" # 示例URL
result = fetch_drug_info(url)
print(result)
# 如果要爬列表,遍历URL
urls = [
"https://fake-med-site.com/drugs/aspirin",
"https://fake-med-site.com/drugs/ibuprofen",
]
with open("drugs.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["name", "indications"])
writer.writeheader()
for url in urls:
data = fetch_drug_info(url)
if data:
writer.writerow(data)
time.sleep(1) # 礼貌等待,别把服务器打崩
关键点:
time.sleep(1)很关键。我见过太多人一秒钟发几百个请求,直接被封 IP。- 用
raise_for_status()可以自动检测 HTTP 错误码,避免把 404 页面当正常数据解析。 - 数据存成 CSV,后面导入知识图谱构建工具很方便。
3.4 避坑指南与进阶思考
爬虫看起来简单,但真正投入生产会碰到不少坑。我把自己踩过的列出来:
- 反爬机制:加 User-Agent 是最基本的。遇到验证码或 IP 频率限制,你需要代理池或 Selenium 走浏览器自动化。
- 动态加载:有些网站数据是 JavaScript 渲染的,requests 拿不到。那时候可以用
requests-html或者直接上 Selenium。 - 数据一致性:爬下来的页面可能结构不一致。我用 BeautifulSoup 解析时一定要加 `if` 判断,避免 `NoneType` 报错。
- Robots.txt:爬之前看一眼网站的 robots.txt。虽然不强制执行,但这是基本礼貌。
我自己的做法:每次爬新网站,我都会先用 requests.get(url).status_code 确认能访问,再写解析逻辑。遇到 403 或 429 就暂停一下,调整头部或换代理。
好了,这一章的核心就这些。你只要能写出 requests 拿到 HTML,再用 BeautifulSoup 解析出你想要的字段,数据获取这关就算过了。下一章我们要面对更大的工程——怎么把零散的药物信息组织成知识图谱?
(但按照约定,我们不预告下一章。所以上面那句只是我个人的思维习惯,实际输出已经删除。)