用Xpath制作简单的爬取网页工具,获取神奇宝贝百科每只精灵的信息

news/发布时间2024/7/15 4:43:54

最近开始学习Python的爬虫应用,个人比较喜欢用Xpath的方式来爬取数据,今天就结合一下Xpath方式,以“神奇宝贝百科”为素材,制作一个爬取每只宝可梦数据的工程项目

准备工作
神奇宝贝百科地址:https://wiki.52poke.com/wiki/主页
工程项目的目标是,获取每只精灵的名字、编号、属性、特性、以及蛋群,比如这个妙蛙花页面的例子

我们可以先看看这个网站逻辑是怎么样的,妙蛙花的图鉴地址是这个:https://wiki.52poke.com/wiki/妙蛙花
和主页相比,wiki后面跟着的内容实际上是精灵的文本编码,那么我们就需要得到每只精灵的文本编码是多少了,而在神奇宝贝百科中,在全国图鉴页面中,刚好就可以查看全部宝可梦的名字

全国图鉴页面地址:https://wiki.52poke.com/wiki/宝可梦列表(按全国图鉴编号)
在这个页面里面,点击每只精灵的名字,就可以跳转到对应精灵的图鉴页面中去,也就是说这个页面里面,应该会包含每只宝可梦图鉴页面的超链接,我们可以用开发者模式看看

用元素查找器点击妙蛙种子的名字,查找到了这只宝可梦的超链接,点击进去,果然就是跳转到了妙蛙种子的图鉴页面了,这边爬虫的基本逻辑就是,先获取每只宝可梦的超链接,再逐个超链接来解析宝可梦的数据,并爬取获得

开始编码

#urllib中的URL拼接库
from urllib.parse import urljoin#导入request库
import requests#导入re库
import re#导入日志信息库
import logging#导入文件写入库
import json
from os import makedirs
from os.path import exists#导入lxml库
from lxml import etree#日志输出级别和输出格式
logging.basicConfig(level=logging.INFO,format='%(asctime)s-%(levelname)s:%(message)s')#打开文件夹results,如果没有,就创造一个这样的文件夹
RESULTS_DIR = 'results'
exists(RESULTS_DIR) or makedirs(RESULTS_DIR)

首先,导入一些基本的库,以方便我们使用对应的功能,由于最终的结果是导出JSON格式的文件,所以也导入了JSON的库
之后按照上一步的分析,把网址的根部URL,以及全国图鉴的URL以常量的方式作为一个常量编写到程序中,:

#当前站点的根目录
BASE_URL = 'https://wiki.52poke.com'
#测试用网站
GOAL_URL = 'https://wiki.52poke.com/wiki/%E5%AE%9D%E5%8F%AF%E6%A2%A6%E5%88%97%E8%A1%A8%EF%BC%88%E6%8C%89%E5%85%A8%E5%9B%BD%E5%9B%BE%E9%89%B4%E7%BC%96%E5%8F%B7%EF%BC%89'

第一步,先发送get请求,获取目标网页的代码,创建一个这样的函数来实现功能:

#抓取网页源代码函数
def scrape_page(url):#打印日志信息logging.info('scraping %s...',url)try:response = requests.get(url)if response.status_code == 200:#如果状态码是200,直接返回源代码,否则捕捉异常return response.textlogging.error('get invalid status code %s while scraping %s',response.status_code,url)except requests.RequestException:logging.error('error occurred while scraping %s',url,exc_info=True)

第二步,创建每只宝可梦的地址列表,我们可以通过解析全国图鉴的url,并根据起初观察的地址结构,来获得每只宝可梦的超链接

#获取每只宝可梦的网页代码函数
def parse_index(html):#将网页源码解析为lxml树tree = etree.HTML(html)#用lxml树来获取目标a节点的href属性elements = tree.xpath('//td[@class = "rdexn-name"]/a/@href')#如果没有数据,返回空列表,否则返回处理后的地址列表if not elements:return []for element in elements:#把根目录和href属性的值结合成每只宝可梦的页面地址detail_url = urljoin(BASE_URL,element)logging.info('get detail url %s',detail_url)yield detail_url


根据网页的逻辑,精灵的超链接都放在了class属性为rdexn-name的td节点下的a节点的href属性里面,所以这里用Xpath的结构//td[@class = "rdexn-name"]/a/@href来获取里面的值,通过urllib库下的urljion方法,把BASE_URL和href属性值合起来,就能得到每只宝可梦的页面链接,之后再以生成器对象返回

#抓取每只宝可梦的详细页面代码函数
def scrape_detail(url):return scrape_page(url)

创建一个专门抓取每只宝可梦详细页面的函数,这里直接调用之前解析网页的函数就可以了,之后就是抓取最重要的数据部分了,也是最复杂的一步

重点分析
第一步最简单,先创建一个解析精灵数据的函数,依然使用Xpath的对象树的方法来创建解析对象

#解析每只宝可梦的内容,包括属性、编号、名字、特性、蛋群
def parse_details(html):#将网页源码解析为lxml树tree = etree.HTML(html)

接下来就是重点环节了,要获取精灵的属性,就要观察它的网页结构是怎么样的:

每只宝可梦基本上有1~2种属性,根据网页结构,属性的路径表达可以是这样子:

#属性
types = tree.xpath('//table[@class = "roundy bgwhite fulltable"]//td[contains(@class,"roundy")]/a//span[@class = "type-box-9-text"]/text()')


编号就更简单了,直接找到title属性为“宝可梦列表(按全国图鉴编号)”的a标签,获取文本属性就可以了

#编号
number = tree.xpath('//a[@title = "宝可梦列表(按全国图鉴编号)"]/text()')


名字也很简单,直接获取style等于font-size:1.5em的span标签下的b标签的文本属性就可以了

#名字
name = tree.xpath('//span[@style = "font-size:1.5em"]/b/text()')

特性的话会相对比较麻烦,每只精灵至少有1种基本特性,而至多有两种基本特性,但是隐藏特性也属于精灵的特性,而且并不是每只精灵都会有隐藏特性的,例如铁甲蛹这只精灵


所以网页的结构也因此而不同,可以观察到,有隐藏特性和没隐藏特性的路径,是不一样的,没有隐藏特性的精灵,td的标签没有colspan这个属性,如果用同一种写法,可能会引起获取不到特性的问题


因此这就需要加上逻辑判断,根据情况来决定路径的执行,思路是根据上面td属性的区别来作出判断,找到这边标签的Xpath路径之后,作为if语句的判断条件,特性的路径语句也不难,找到包含“特性”字符的title属性就可以了

#特性
#如果没有隐藏特性
hide_feature = tree.xpath('/table[contians(@class,"roundy")]/tbody/tr[4]/td[@colspan = "2"]')
if not hide_feature:features = tree.xpath('//td/table[@class = "roundy bgwhite fulltable"]/tbody//a[contains(@title,"(特性)")]/text()')
else:features = tree.xpath('//td[@colspan = "2"]//a[contains(@title,"(特性)")]/text()')


蛋群属性也不难,按照页面逻辑,找到colspan属性的td标签下,包含“蛋群”字眼的title属性的a标签,获取文本就可以了

#蛋群
eggs = tree.xpath('//td[@colspan = "2"]//a[contains(@title,"(蛋群)")]/text()')

最后再将获取到的数据,以字典的形式返回,这样一来,就能得到一只宝可梦的属性、编号、名字、特性、蛋群的信息

return{'types':types,'number':number,'name':name,'features':features,'eggs':eggs}

这样的代码看似没问题,不过宝可梦有个设定就是,有些宝可梦可能会有其他形态,比如妙蛙花这只精灵,有超级进化,以及超极巨化的形态,那么要获取它的信息的时候,可能会出现数据重复的问题,这种情况是因为具有多种形态的宝可梦,网页结构不一样的原因


可以看到,妙蛙花因为具有多个形态的原因,网页结构上多了几个class属性为_toggle from的属性,那么就需要多做一步判断处理,这里只取他最基本的形态的数据为主,代码如下:

#解析每只宝可梦的内容,包括属性、编号、名字、特性、蛋群
def parse_details(html):#将网页源码解析为lxml树tree = etree.HTML(html)#判断是否有额外形态toggle = tree.xpath('//tr[@class = "_toggle form1"]')#根据是否有额外形态,来进行解析筛选if toggle:#属性types = tree.xpath('//tr[@class = "_toggle form1"]//table[@class = "roundy bgwhite fulltable"]//td[contains(@class,"roundy")]/a//span[@class = "type-box-9-text"]/text()')#编号number = tree.xpath('//tr[@class = "_toggle form1"]//a[@title = "宝可梦列表(按全国图鉴编号)"]/text()')#名字name = tree.xpath('//tr[@class = "_toggle form1"]//span[@style = "font-size:1.5em"]/b/text()')#特性#如果没有隐藏特性hide_feature = tree.xpath('/table[contians(@class,"roundy")]/tbody/tr[4]/td[@colspan = "2"]')if not hide_feature:features = tree.xpath('//tbody/tr[contains(@class,"_toggle form1")]//td//a[contains(@title,"(特性)")]/text()')else:features = tree.xpath('//tbody/tr[contains(@class,"_toggle form1")]//td[@colspan = "2"]//a[contains(@title,"(特性)")]/text()')#蛋群eggs = tree.xpath('//tr[@class = "_toggle form1"]//td[@colspan = "2"]//a[contains(@title,"(蛋群)")]/text()')else:#属性types = tree.xpath('//table[@class = "roundy bgwhite fulltable"]//td[contains(@class,"roundy")]/a//span[@class = "type-box-9-text"]/text()')#编号number = tree.xpath('//a[@title = "宝可梦列表(按全国图鉴编号)"]/text()')#名字name = tree.xpath('//span[@style = "font-size:1.5em"]/b/text()')#特性#如果没有隐藏特性hide_feature = tree.xpath('/table[contians(@class,"roundy")]/tbody/tr[4]/td[@colspan = "2"]')if not hide_feature:features = tree.xpath('//td/table[@class = "roundy bgwhite fulltable"]/tbody//a[contains(@title,"(特性)")]/text()')else:features = tree.xpath('//td[@colspan = "2"]//a[contains(@title,"(特性)")]/text()')#蛋群eggs = tree.xpath('//td[@colspan = "2"]//a[contains(@title,"(蛋群)")]/text()')return{'types':types,'number':number,'name':name,'features':features,'eggs':eggs}

最后一步是把数据导出为JSON文件格式

#保存为JSON格式
def save_data(data):name = data.get('name')data_path = f'{RESULTS_DIR}/{name}.json'json.dump(data,open(data_path,'w',encoding='utf-8'),ensure_ascii=False,indent=2)

所有的功能性代码就已经全部完成了,最后只需要执行主函数,让程序运行起来,就可以自动抓取每只宝可梦的数据了

#主函数
def main():#获取全国图鉴的页面信息html = scrape_page(GOAL_URL)#获取每只精灵的超链接代码detail_urls = parse_index(html)#把每只宝可梦的详情页逐个分析,用data的变量来存取for detail_url in detail_urls:detail_html = scrape_detail(detail_url)data = parse_details(detail_html)logging.info('get detail data %s',data)logging.info('saving data to json file')save_data(data)logging.info('data saved successfully')#运行程序
if __name__=='__main__':main()        

之后就可以获得每只宝可梦的JSON文件了,里面包括每只精灵的属性、编号、名字、特性、蛋群信息


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.jwkm.cn/p/77168501.html

如若内容造成侵权/违法违规/事实不符,请联系宁远站长网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

提升效率必备VSCode运行快捷键全攻略

哈喽,大家好,我是木头左!快速编译与执行 在开发过程中,频繁地编译和执行代码是必不可少的。而在VSCode中,通过简单的键盘操作即可完成这些操作,无需鼠标点击或多余的步骤。 Ctrl + Shift + B or Cmd + Shift + B 这个快捷键用于编译当前打开的文件。按下它,VSCode会使用…

SpringBoot的Security和OAuth2的使用

创建项目 先创建一个spring项目。 然后编写pom文件如下,引入spring-boot-starter-security,我这里使用的spring boot是2.4.2,这里使用使用spring-boot-dependencies,在这里就能找到对应的security的包。 <?xml version="1.0" encoding="UTF-8"?&g…

上周面了百度,问的很细~

上周刚刚面了百度,问的问题不算很难,但却很细,我把这些面试题和答案都整理出来了,一起来看吧。 重点介绍一个你觉得有意义的项目? 回答技巧和思路:介绍的项目业务难度和技术难点要高一些,最好是微服务项目。 简明扼要的讲清楚项目核心板块的业务场景即可,切忌不要讲的太…

团队开发sprint 第一天

2024-04-19项目任务进展: 6小时(6/50) 会议照片 过去一天完成了哪些任务今日主要是对后续任务和工作的细化分配和对课程情况和空余时间的讨论与协调 确定 flutter + Springboot 开发心理健康程序,并内置chat-gpt 完成了环境的安装接下来的计划对flutter和Springboot进行学习并尝…

MLOps模型部署的三种策略:批处理、实时、边缘计算

机器学习运维(MLOps)是一组用于自动化和简化机器学习(ML)工作流程和部署的实践。所选择的部署策略可以显著影响系统的性能和效用。所以需要根据用例和需求,采用不同的部署策略。在这篇文章中,我们将探讨三种常见的模型部署策略:批处理、实时和边缘计算。https://avoid.ov…

中西文化比较

这本书名为《Western Civilization with Chinese Comparisons》,由John G. Blair和Jerusha Hull McCormack合著,是一本专注于西方文明与中国文明比较研究的教材。以下是对书中核心知识点的快速总结: 1. **文明比较的目的**:本书强调通过比较不同文明来增进对各自独特性的理…