前言:
我感觉之前讲的爬虫,太纸面化了,需要给一些实例来帮助理解。毕竟爬虫这项技能,我们经常可能用到,通常用于爬虫数据来训练模型。
延续上一篇文章所说将爬虫分为四个主要部分:
获取网页源代码
解析网页并提取数据
实现自动化抓取逻辑
保存数据到文件(如 execl)
第一步:获取网页源代码
要获取网页内容,最直接的方式是使用 requests 库。帮助我们发送 HTTP 请求并获取网页内容。以下是一个基本示例:
import requests
url = "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[ref="specialityBox"]')
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[ref="specialityBox"]')
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[big_type_name] = []
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[big_type_name].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_url
gain_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 requests
from bs4 import BeautifulSoup
from openpyxl import Workbook
from openpyxl.reader.excel import load_workbook
headers = {
"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[big_type_name] = []
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[big_type_name].append({
'name': small_type_name,
'url': small_type_url+"?p=",
})
print(type_dict)
return type_dict
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_url
def 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 text
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[ref="specialityBox"]')
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
def 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条十分鸡肋。(就是来跟大家交流学习的作用而已)
一言两语
明天再战科目三了,十分紧张,一定得过啊。