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

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

时间:2024-06-17 21:21:06浏览次数:27  
标签:Xpath xpath 可梦 网页 百科 tree text td class

最近开始学习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.text
        logging.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文件了,里面包括每只精灵的属性、编号、名字、特性、蛋群信息


标签:Xpath,xpath,可梦,网页,百科,tree,text,td,class
From: https://www.cnblogs.com/GodWolley/p/18253228

相关文章

  • 基于html,css,js,jQuery,ajax实时天气的网页
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><linkrel="stylesheet"hr......
  • 领导让前端实习生在网页上添加一个长时间不操作锁定电脑的功能
    接上文:屠龙少年终成恶龙,前端转产品的我给前端挖了个坑前情提要大约一个月前,公司的医疗管理系统终于完工上线。后面一个周一,领导叫大家开会,说后面没有项目进来了,用不了这么多开发人员,原地宣布裁员。再后一周后,花2000招了个实习生,工作内容为系统维护。工作内容领导:由于我们工......
  • Hbuilder创建项目,制作html网页第一天
    1、引入js文件<scriptsrc="js/pixi.min.js"></script>2、创建应用varapp=newPIXI.Application(500,600);//宽500,高6003、将应用放进页面document.body.appendChild(app.view);4、创建背景元素varbg=newPIXI.Sprite.fromImage("img/bg.jpg");5、将背景元素放进界......
  • 遇到网页不让复制粘贴该怎么办?
    当我们在网上看到一些有用的信息时,通常会想要复制粘贴以便稍后查看或与他人分享。但是,有些网页使用了JavaScript或其他技术来防止用户复制其内容。这可能会导致一些不便,但有几种方法可以尝试解决这个问题。下面我们将讨论几种方法来应对这种情况。第一招:查看源代码我们在网......
  • Apache网页优化
    一、网页压缩1.网页压缩网站访问速度影响因素:应用程序响应速度、网络带宽、服务器性能、与客户端之间网络传输速度等。其中最重要的是=一个因素是Apache本身,因此提升Apache执行速度(使用网页压缩)是性价比最高的选择。(1)gzip介绍一种流行的文件压缩算法,大约可以减少70%以......
  • Nginx设置缓存后,访问网页404 问题原因及解决方案(随手记)
    原文链接:Nginx设置缓存后,访问网页404问题原因及解决方案(随手记)_nginx的html文件地址修改后404-CSDN博客 目录问题描述Nginx文件解决方案查看error_log日志问题原因修改文件并测试Nginx文件测试总结问题描述在Nginx中设置缓存expires后,结果重启nginx,网站访问404了。Nginx文件s......
  • 1832javaERP管理系统之车间计划管理Myeclipse开发mysql数据库servlet结构java编程计算
    一、源码特点 javaerp管理系统之车间计划管理是一套完善的web设计系统,对理解JSPjava编程开发语言有帮助采用了serlvet设计,系统具有完整的源代码和数据库,系统采用web模式,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql,使用java语言开发。......
  • HTML静态网页成品作业(HTML+CSS)—— 明星吴磊介绍网页(5个页面)
    ......
  • Apache网页优化
    一.网页压缩    网站的访问速度是由多个因素所共同决定的,这些因素包括应用程序的响应速度、网络带宽、服务器性能、与客户端之间的网络传输速度等等。其中最重要的一个因素是Apache本身的响应速度。因此当为网站性能所苦恼时,第一个需要着手进行处理的便是尽可能的提升......
  • div+css布局实现个人网页设计(HTML期末作业)
    ......