【爬虫】【python】爬取中华康网上清热药的信息

用BeautifulSoup库解析和提取数据

解析特定标签的网页元素

我最初喜欢用正则表达式来进行爬取,后来发现利用标签的形式解析源代码十分方便,也就是直接用开发者工具中的内容进行分析。

from bs4 import BeautifulSoup
# 固定写法导入BeautifulSoup库
soup = BeautifulSoup(res, 'html.parser')
# ‘html.parser’表示设置解析器为HTML解析器
# res是之前创建的需要解析的网页源码
# 并将解析器命名为soup
title = soup.select('h1')
# 选取所有<h1>标签,传入的参数为标签名
print(title)
# 输出传出的标签名,也就是两个h1标签中间的内容

因为传出的标签名可能有其他一些多余的成分,所以如果想进一步提取标签中的文本内容,需要遍历各个标签,然后通过text属性提取文本内容,代码如下:

for i in title:
    print(i.text)

解析特定属性的网页元素

由于有些标签具有特定的属性,例如class和id。

例如所有标签的clss属性都是“title”,那如下方法解析:

title = soup.select('.title')
print(title)

同前一个部分不同,这里在select函数中输入的参数不是标签名,而是class属性的值,并且在属性值前面都要加上“.”,用于声明寻找的是class属性。会把class属性为“title”的标签全部选取出来。

注:如果寻找的是id属性,那需要在属性值前面加上“#”号来声明寻找的是id属性。

select()函数的多层次筛选

由于网页源代码中的标签通常是层层嵌套的关系,所以可以使用多层次筛选功能:

from bs4 import BeautifulSoup

res = '''
<div class = "result1">
    <h1 class = "title">南京中医药大学</h1>
</div>
<div class = "result2">
    <h1 class = "title">中药213</h1>
</div>'''
# 临时代码,<div>标签中还有<h1>标签
soup = BeautifulSoup(res, 'html.parser')
title = soup.select('.result1 h1')
# 多层次筛选class属性为result1中标签为h1的信息
# 也可写成title = soup.select('.result1 .title')
print(title)

利用XPath定位网页元素的具体方法

查找与定位XPath表达式

我是遇到一个问题,例如这段代码中文字在两个便签之外,利用strong标签定位定位不到(在它们之外),利用p标签的话定位到的内容太多了,不方便后续数据清洗。所以学习一下Xpath方法来定位标签。
(注:如果输出XPath的话,输出的是一个类似于编码的信息)
在这里插入图片描述
Xpath可以理解成网页元素的名字或者id,可以利用一下语句格式根据XPath表达式来定位网页元素

find_element_xpath('XPath表达式')

利用开发者工具可以获取网页元素的XPath表达式,如下图所示,按【F12】键打开开发者工具,1.单击左上角的元素选择工具按钮;2.在网页中随机选中希望知道其XPath表达式的内容,例如我在这边选中了右上角的搜索框;3.右键选中内容对应的源码,依次选择Copy-Copy XPath命令,即可把搜索框的XPath表达式复制到剪贴板,在编写代码时就可以粘贴到函数中作为参数。
在这里插入图片描述

在这个例子中我们最终得到如下代码:

from selenium import webdriver
browser = webdriver.Chrome()
browser.get('http://www.cnkang.com/tags/15232.html')
browser.find_element_by_xpath('//*[@id="bdcsMain"]').send_keys('薄荷')
browser.find_element_by_xpath('/html/body/div[1]/div/div/form/ul/input[2]').click()

其中第四行意指定位到搜索框上,.send_key(‘薄荷’),意识是在搜索框中输入薄荷;第五行是搜索键的XPath,.click()意指点击该键。

所以整段代码的意识是先定位到搜索框,然后定位到搜索键上并点击,最后得到的结果如下:
在这里插入图片描述

XPath表达式的高级用法

我看的是这位李富贵要上岸985同学的文章,受益匪浅,现在在他的基础上,加上我自己的理解分享给大家

https://blog.csdn.net/weixin_45755332/article/details/107193013

这位同学的文章中列出了几种表达式与对应用法,我在这边用具体案例来进行实操表达,以如下文档为例(来源于该同学):

在这里插入图片描述

定位子、子孙节点(/,//)

观察网页源码可知,其结构关系为html - body - div(共有三个分区,分别为“百里守约”,“song”,“tang”)。

import requests
from lxml import etree
tree = html.etree.parse('test.html') # 此处代码不能运行,原因未知
r1=tree.xpath('/html/body/div')	#直接从上往下挨着找节点
r2=tree.xpath('/html//div')#跳跃了一个节点来找到这个div节点的对象
r3=tree.xpath('//div')##跳跃上面所有节点来寻找div节点的对象

这两个的用法是按照逻辑结构次序来进行遍历后输出。

定位分区中的标签(.path)

和我上面所述查找与定位XPath表达式的内容差不多,我在这边发现一个有趣的东西,例如:

r4=tree.xpath('//div[@class="song"]')
r4

>>
>[<Element div at 0x19d447658c8>]

它这边@class后面的内容其实就是它的区名。

对分区的标签索引(/p, /p[])

续上,我们在获得class为“song”的类以后,可以在最后加上/p来返回所有的标签

tree.xpath('//div[@class="song"]/p')

如果想要具体返回某一个标签,那就观察其对应的标签序号,并且注意,这里面从1开始,而非代码中常认为的0。

tree.xpath('//div[@class="song"]/p[3]')

取标签中分文本()

同样以取“杜牧”这个标签为例
在这里插入图片描述
首先观察“杜牧”所在的位置,在第三个div(class = tang),的第五个li标签中。
所以有如下写法:

tree.xpath('//div[@class="tang"]//li[5]/a/text()')

>>
['杜牧']

@class = “tang”定位了“杜牧”这个标签所在的分区,“//li[5]/a”表示这是第五个li标签,中的a标签,最后的text()函数用于将element格式的内容转换成文本。

在我的理解里面,这里面跳过了一个ul标签,所以采用“//”,所以也可以写成如下形式:

tree.xpath('//div[@class="tang"]/ui/li[5]/a/text()')

>>
['杜牧']

爬取中华康网清热药信息的操作步骤

1. 爬取中华康网中清热药网页源码

这个在我之前的的爬虫基础知识分享中有提过,所以直接给出源码。

注:我在看其他人的代码时发现,很多人会用多个函数的形式,将爬虫代码分成爬取源码、解析等步骤,这样看上去更清楚一些,所以在此学习一下。

# 1. 获取网页源码并转换成文本格式
def getcon():
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                             'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'}
    url = "http://www.cnkang.com/cm/zcy/jry/"  # 中华康网上清热药的网址
    res = requests.get(url, headers=headers)  # 使用requests.get()函数访问该网址
    res.encoding = 'utf-8'  # 使用'utf-8'的编码格式来重新编码及解码
    res = res.text
    analyse(res)

2. 解析网页源代码并提取网址数据

观察网页源码可知,每种植物与它对应的网址在class属性值为catalog04的标签下,具体是dd中的a标签。

然后输出网址前加上爬取到的网址缺失的前缀,这边我用了replace()函数,把空格部分替代成了不空格。

# 2. 解析网页源码
def analyse(res):
    soup = BeautifulSoup(res, 'html.parser')
    med = soup.select('.catalog04  dd a')
    # 观察源码中带有每种植物与对应网址的在class为catalog04下,<dd>中的<a>标签
    for i in range(len(med)):
        print(str(i + 1) + '.' + med[i].text)
        temp = ('http://www.cnkang.com/' + str(med[i]['href']))
        print(temp.replace(" ", ""))
        # 分别输出一种植物加上它对应的网址

此时输出结果为一种植物的序号与它的名称,另起一行为它的网址
在这里插入图片描述
我为了后续还要继续访问网址,对其进行操作,所以尝试将网址与名称分别保存在两个列表里。

# 2. 解析网页源码
def analyse(res):
    soup = BeautifulSoup(res, 'html.parser')
    med = soup.select('.catalog04  dd a')
    # 观察源码中带有每种植物与对应网址的在class为catalog04下,<dd>中的<a>标签
    med_name = []
    med_url = []
    # 创建两个存放名称和网址的列表
    for i in range(len(med)):
        temp = ('http://www.cnkang.com/' + str(med[i]['href']))
        med_name.append(med[i].text)
        med_url.append(temp.replace(" ", ""))
        # 将对应的名称和网址加入对应的列表中
    detailed_information(med_name, med_url)

3. 各味清热药的别名,药性等信息

最初我的想法是用正则表达式,但是繁杂无用数据过多,效率实在是很低,在此附上代码:

def crawler(res):
    h_res = '<strong>.*?<>(.*?)<p>'
    result = re.findall(h_res, res)
    print(result)

所以后来我采用XPath的提取方法,实践是检验真理的唯一标准,我发现在“李富贵要上岸985”同学的代码从文本文档到开始利用XPath方法进行定位前那一段,然后我找到了解决方法,问题原因我暂时仍然不清楚,先保留一下。

import requests
from lxml import html

# 附3. 用来在源码中爬取具体信息
def crawler(res):
    tree = html.etree.HTML(res.content, html.etree.HTMLParser(encoding = 'utf-8'))
    Introduction = tree.xpath('//div[@class = "zh05b"]//p/text()')
    print(Introduction)

if __name__=="__main__":
    url = "http://www.cnkang.com/tags/15232.html"  # 中华康网上清热药的网址
    res = requests.get(url)
    crawler(res)



并且他没有采用head的方式来解析获得网页源码,我猜测是因为传递数据用的是一种编译后的格式,不像之前需要转换成为文本格式再调整,所以不用head这个多余的步骤

# 附3. 用来在源码中爬取具体信息
def crawler(res):
    tree = html.etree.HTML(res.content, html.etree.HTMLParser(encoding = 'utf-8'))
    temp_re = tree.xpath('//div[@class = "zh05b"]//p/text()')
    print(temp_re)

完整代码

import requests
import re
from lxml import html
from bs4 import BeautifulSoup
from urllib.request import  urlretrieve

# 1. 获取网页源码并转换成文本格式
def getcon():
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                             'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'}
    url = "http://www.cnkang.com/cm/zcy/jry/"  # 中华康网上清热药的网址
    res = requests.get(url, headers=headers)  # 使用requests.get()函数访问该网址
    res.encoding = 'utf-8'  # 使用'utf-8'的编码格式来重新编码及解码
    res = res.text
    analyse(res)

# 2. 解析网页源码
def analyse(res):
    soup = BeautifulSoup(res, 'html.parser')
    med = soup.select('.catalog04  dd a')
    # 观察源码中带有每种植物与对应网址的在class为catalog04下,<dd>中的<a>标签
    med_name = []
    med_url = []
    # 创建两个存放名称和网址的列表
    for i in range(len(med)):
        temp = ('http://www.cnkang.com/' + str(med[i]['href']))
        med_name.append(med[i].text)
        med_url.append(temp.replace(" ", ""))
        # 将对应的名称和网址加入对应的列表中
    detailed_information(med_name, med_url)

# 3. 爬取各味清热药的别名,药性等信息
def detailed_information(name, url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                             'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'}
    Introduction = [] # 用来存储简介
    medicinal = [] # 用来存储药性
    efficacy = [] # 用来存储功效与作用
    indications = [] # 用来存储适应症
    for i in range(len(url)):
        temp_url = url[i]
        temp_res = requests.get(temp_url)
        crawler(temp_res)

# 附3. 用来在源码中爬取具体信息
def crawler(res):
    tree = html.etree.HTML(res.content, html.etree.HTMLParser(encoding = 'utf-8'))
    temp_re = tree.xpath('//div[@class = "zh05b"]//p/text()')
    print(temp_re)

if __name__ == "__main__":
    getcon()


不足之处

主要是在最后数据的清洗部分,我没有进行细致的清洗与排布,只是简单的输出需要的数据,等到后面我还要系统学习一下更加整齐的排布方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值