第四课:Xpath 语法和 lxml 模块

184 阅读4分钟

1.什么是Xpath

xpath(XML Path Language)是一门在XML和HTML文档中查找信息的语言,可用来在XML和HTML文档中对元素和属性进行遍历。

2.XPath开发工具

Chrome插件XPath Helper。

使用的时候需要在浏览器搜索栏输入你要查找的网页地址,这样才能启动XPath,就是先点击XPath图标,这时候网址搜索栏会提醒你输入网址,输入网址后即可开启XPath Helper。

3.XPath节点

在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。

4.XPath 语法

  • 使用方法: 使用//获取整个页面当中的元素,然后写标签名,然后在写谓语进行提取,比如:
//title[@lang = 'en']
  • 需要注意的知识点:
  1. /和//的区别:/代表只获取子节点,//获取子孙节点,一般//用的比较多,当然也要视情况而定
  2. contains:有时候某个属性中包含了多个值,那么可以使用contains函数,示例如下:
//title[contains(@lang, "en")]
这个contains()函数也相当于一个模糊查询,凡是属性里面有en即可查询到。
  1. 谓词中下标是从1开始的,不是从0开始的

5.lxml库

lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。

  • 安装:
pip install lxml
  • 基本使用:
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''
# 1.将字符串解析为html文档
html = etree.HTML(text)
# 2.将字符串序列化html
result = etree.tostring(html).decode('utf-8')
print(result)
  • 从文件中读取html代码
from lxml import etree

# 1.读取
html = etree.parse('hello.html')
# 2.将字符串序列化
result = etree.tostring(html).decode('utf-8')
print(result)

6.在lxml中使用xpath语法

  • hello.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>XPath Exercise</title>
</head>
<body>
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a></li>
     </ul>
 </div>
</body>
</html>
  • 语法练习:
from lxml import etree

# 1.读取
html = etree.parse('hello.html')

# 获取所有li标签:
result = html.xpath('//li')
for i in result:
    print(etree.tostring(i).decode('utf-8'))
    
# 获取所有li元素下的所有class属性的值:
result = html.xpath('//li/@class')
for i in result:
    print(i)

# 获取li标签下hrefwww.baidu.coma标签:
result = html.xpath('//li/a[@href="www.baidu.com"]')
print(result)

# 获取li标签下所有span标签:
result = html.xpath('//li//span')
for i in result:
    print(etree.tostring(i).decode('utf-8'))

# 获取li标签下的a标签里的所有classresult = html.xpath('//li/a/@class')
for i in result:
    print(i)

# 获取最后一个liahref属性对应的值:
result = html.xpath('//li[last()]/a/@href')
print(result)

# 获取倒数第二个li元素的内容:
result = html.xpath('//li[last()-1]/a')
print(result[0].text)

# 获取倒数第二个li元素的内容的第二种方式:
print(html.xpath('//li[last()-1]/a/text()'))

7.爬取笑话网站数据

""""
目标:抓取该网址下的笑话:https://xiaohua.zol.com.cn/new/
分析:第一步是抓取不同页的详情页的网址
     第二步是抓取该页下的详情页的内容
所有我们在抓取第一步内容的网址下,要抓取到详情页的网址,从而得到我们想要的笑话
parse: 解析器
"""

import requests
from lxml import etree
import time
import json

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 Edg/110.0.1587.63",
    "cookie": "z_pro_city=s_provice%3Dsichuan%26s_city%3Dchengdushi; userProvinceId=17; userCityId=386; userCountyId=0; userLocationId=90403; ip_ck=5c+G5vP3j7QuMjg3MTkwLjE2ODQ4MDg0Nzg%3D; lv=1684808478; vn=1; Hm_lvt_ae5edc2bc4fc71370807f6187f0a2dd0=1684808479; _gid=GA1.3.486821387.1684808481; z_day=icnmo11564%3D1%26ixgo20%3D1; __bid_n=1884674e926f55d8914207; FPTOKEN=xP+ItlDJPdz7ywgrddT66Vcmymc4T/KAAm/nz9Qc8UC3EZU5LzL70Tq+tO+DA6sYpH4ZUeXOLw+wn7z+Yt2MgREZfXxQWh/ZZMFA87rY6tn326QAA6boQSi9K0wjlbB/kLm8XsbOjKu63ZAcEhPgpqe5xj0ZeB8XjBD+XVS/u0cN68a0olgqxlMEIhoQmnDPO9IbFFPgAkxRi8ESY/qz5IoWo9R5DLgIMHL++KImkI1wwG9YirszVXyaO4Ub/qxbBdT4tffDGPp8WGYIun0cNV3jsuA94r+ef75vWeqRIqbA6GmMVAqTgbHZx0O/OiEIb9dKlX8ccBO9HsS/Nf2wodgvoYMH56Jvelf4Ezf+HjBDly1oYxUXuMpoGqILsG9716cS0Kt5SE6967lVkvERGw==|zBcBTCiISR6AajzS8DeIVnFFyAWHcv1FL93dTUgjY+Y=|10|50792253cf2977cf6348573871a81850; _gat=1; Hm_lpvt_ae5edc2bc4fc71370807f6187f0a2dd0=1684810038; questionnaire_pv=1684800014; _ga_6XFCZ4WP8R=GS1.1.1684808482.1.1.1684810039.0.0.0; _ga=GA1.1.805832979.1684808481"
}

domain = "https://xiaohua.zol.com.cn/"

joke_list = []


# 1.首先抓到不同页的详情页网址
def parse_page(page_url):
    # 得到url网址下的对象
    resp = requests.get(page_url, headers=headers, verify=False)
    # 得到对象里面的html内容
    text = resp.text
    # 将内容解析为html
    parser = etree.HTML(text)
    detail_url_list = parser.xpath('//ul[@class="article-list"]/li[@class="article-summary"]/a[@class="all-read"]/@href')
    for detail_url in detail_url_list:
        detail_url = domain + detail_url
        parse_detail(detail_url)
        time.sleep(3)

# 2.用得到的详情页网址爬取笑话
def parse_detail(detail_url):
    resp = requests.get(detail_url, headers=headers)
    text = resp.text
    parser = etree.HTML(text)
    joke_title = parser.xpath('//h1[@class="article-title"/text()]')[0]
    # "/".join()使用/连接答应的内容   .strip()去掉前后的空格
    joke_content = "/".join(parser.xpath('//div[@class="article-text"//text()]')).strip()
    joke_list.append({
        "title":joke_title,
        "content": joke_content
    })
    print(f"{joke_title}笑话下载完毕")

# 3.整合函数
def main():
    for page in range(1,11):
        page_url = f"https://xiaohua.zol.com.cn/new/{page}.html"
        parse_page(page_url)
        time.sleep(2)

    with open("joke.json","w",encoding='utf-8') as fp:
        json.dump(joke_list,fp, ensure_ascii=False)



if __name__ == '__main__':
    main()