爬虫系列03 ---- lxml模块通过XPath解析网页内容

Xpath简介

XPath是一门可以在XML文档中查找指定信息的语言。可以参考w3cschool的XPath教程,内容简单易懂。

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集,非常像电脑文件系统中的文件路径。

选取节点时特别注意 //表示递归查找 、@表示选取属性
在这里插入图片描述
在选取节点的时候可以添加谓语(Predicates)来选取某个特定的节点或者包含某个特定的值的节点,这个是非常重要的,基本上爬虫都会用谓语选取指定节点。

在这里插入图片描述
XPath的轴可以定义相对于当前节点的节点集,通俗来说就是获取类似祖先孙子之类的节点。(亦可参考深入浅出XPath轴

轴名称结果
parent父节点, 俗称爸爸, 只有一个
child子节点, 俗称孩子
ancestor祖先节点, 爸爸*n, 特别注意 爸爸的兄弟不算
descendant子孙节点, 孩子*n, 特别注意 弟弟的孩子不算
following自你以下页面中所有节点, 即同胞弟弟节点即其后代, 和descendant的区别就是包括弟弟的后代
following-sibling同层下节点, 即同胞弟弟节点, 所有的弟弟
preceding自你以上页面中所有节点, 即同胞哥哥节点即其后代, 和ancestor的区别就是包括哥哥的后代
preceding-sibling同层上节点, 即同胞哥哥节点, 所有的哥哥

XPath轴选取可以参考下面的示例代码,通过selenium+XPath轴选取爬取京东首页指定内容。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from time import sleep



options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=Service(r"E:\chromeDriver\chromedriver.exe"), options=options)

url = "https://www.jd.com/"

driver.get(url)

# 1.获取左边的分类栏目
# 有data-index属性 且 class='cate_menu_item'
l = driver.find_elements(By.XPATH, "//li[@data-index][@class='cate_menu_item']")
print("\n".join([x.text for x in l]))

# 2. 获取第3个分类  和 获取倒数第二个分类  和 获取前五个分类  和 获取后五个分类
l = driver.find_elements(By.XPATH, "//li[@data-index][@class='cate_menu_item'][3]")
l = driver.find_elements(By.XPATH, "//li[@data-index][@class='cate_menu_item'][last()-1]")
l = driver.find_elements(By.XPATH, "//li[@data-index][@class='cate_menu_item'][position()<=5]")
l = driver.find_elements(By.XPATH, "//li[@data-index][@class='cate_menu_item'][position()>(last()-5)]")

# 2.1 Xpath 逻辑运算符 and  or

# 3. Xpath轴运算
# 参考地址 https://www.bbsmax.com/A/rV57PwWWdP/


# 装饰字画 的链接地址
driver.find_element(By.XPATH, "//a[text()='装饰字画']").get_property("href")

# parent::dd  上层父节点叫dd的亲生爸爸, 只有一个
driver.find_element(By.XPATH, "//a[text()='装饰字画']/parent::dd").get_attribute("clstag")

# child::a  下层所有子节点,所有亲儿子中叫a的;
l=driver.find_elements(By.XPATH, "//dd[@clstag='h|keycount|head|category_04d04']/child::a")
print("\n".join([x.get_property("innerText") for x in l]))

# ancestor:: dl  上面所有直系节点,是你亲生爸爸或者你亲爹或者你亲爹的爸爸中叫div的. 爸爸的兄弟是不算的, 必须是 爸爸*n
driver.find_element(By.XPATH, "//a[text()='装饰字画']/ancestor::dl").get_attribute("class")
driver.find_element(By.XPATH, "//a[text()='装饰字画']/ancestor::dd").get_attribute("clstag")

# descendant::li  下面所有节点,你的后代中叫div的,不包括你弟弟的后代, 必须是 儿子*n
l=driver.find_elements(By.XPATH, "//div[@id='J_cate']/descendant::li")
print("\n".join([x.get_property("innerText") for x in l]))

# following::li 自你以下页面中所有节点叫div的. 和descendant的区别就是包括弟弟的后代
driver.find_elements(By.XPATH, "//div[@id='J_cate']/following::li")

# following-sibling::li 同层下节点,你所有的亲弟弟中叫li的
l = driver.find_elements(By.XPATH, "//li[@data-index=2][@class='cate_menu_item']/following-sibling::li")
print("\n".join([x.get_property("innerText") for x in l]))

# preceding::a 同层上节点,你所有的亲哥哥以及他们的后代中叫a的
driver.find_elements(By.XPATH, "//li[@data-index=2][@class='cate_menu_item']/preceding::li")

# preceding-sibling::li 同层上节点,你所有的亲哥哥中叫li的;
l = driver.find_elements(By.XPATH, "//li[@data-index=5][@class='cate_menu_item']/preceding-sibling::li")
print("\n".join([x.get_property("innerText") for x in l]))




常用的XPath函数就是下面几种,所有的XPath函数可以参考XPath函数指南

# 常用函数
contains(string1,string2)
starts-with(string1,string2)
ends-with(string1,string2)
not(arg)  取反

lxml模块

python的lxml模块可以通过XPath解析网页,最常用的就是xpath方法,不建议使用find方法(因为该方法没有完全实现 xpath 的规则)

示例代码如下。

from lxml import etree
import requests

url = "https://www.jd.com/"

# 请求网址
resp = requests.get(url)

# 将网址的文本内容变成一个 HTML 对象
# 类型是 lxml.etree._Element, 只需掌握该对象的 xpath 方法即可
html = etree.HTML(resp.text)

# xpath 获取分类对象
l = html.xpath("//li[@data-index][@class='cate_menu_item']")
for tmp in l:
    child = tmp.findall("a")
    print(" / ".join([x.text for x in child]))

# 直接获取文本内容
# 但是这个就没有层级关系
l = html.xpath("//li[@data-index][@class='cate_menu_item']/a/text()")
print(l)

# lxml.etree._Element 类常用方法
# xpath 方法, 以当前对象作为 context node 来计算xpath对应的节点. 用它即可.
# findall 方法, 可以传入 tag 或 xpath  不建议使用
# getchildren 方法, 获取所有孩子节点

特别注意通过xpath方法获取的是list列表对象。

Xpath玩转豆瓣电影TOP10

# -*- coding:UTF-8 -*-

"""
获取 豆瓣电影 Top-250
"""

import re
import requests
from lxml import etree

import importlib,sys
# sys.setdefaultencoding("utf-8")
importlib.reload(sys)

from time import sleep
from random import randint

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",
    # 设置不保持连接防止服务器屏蔽  或者 每个请求完成后都关闭  keep-alive 或者 close
    "Connection": "close",
    "Host": "movie.douban.com"
}

url_f = "https://movie.douban.com/top250?start=%s&filter="


def spider(start, end):
    result_list = list()
    for i in range(start, end+1):
        url = url_f % (25*(i-1),)
        resp = requests.get(url, headers=headers)

        html = etree.HTML(resp.text)

        # 获取当前页面所有的 电影
        movies = html.xpath("//div[@id='content']//li")

        # 遍历 movies 获取想要的数据
        for movie in movies:
            info = list()
            # 获取名字和别名
            movie_name_tag_a = movie.xpath(".//div[@class='hd']/a")[0]
            # 添加名字
            movie_name_text = movie_name_tag_a.xpath("span/text()")
            info.append(movie_name_text[0])
            alias = " | ".join([x.strip("/\xa0") for i, x in enumerate(movie_name_text) if i > 0])
            # 添加别名
            info.append(alias)

            # 添加图片地址
            info.append(movie.xpath(".//div[@class='pic']/a/img/@src")[0])

            # 添加导演和主演
            director_actor = movie.xpath(".//div[@class='bd']/p[1]")[0].text
            director = re.search("导演: (.*?)\xa0", director_actor).group(1) if "\xa0" in director_actor else re.search("导演: (.*)", director_actor).group(1)
            if re.search("主演: (.*)", director_actor):
                actor = re.search("主演: (.*)", director_actor).group(1)
            else:
                actor = ""
            info.append(director)
            info.append(actor)

            # 添加评分
            info.append(movie.xpath(".//span[@class='rating_num']/text()")[0])

            # 添加点评人数
            dianping = movie.xpath(".//span[not(@*)]/text()")[0]
            info.append(re.search("(\d+)人评价", dianping).group(1))

            # 添加简介
            if movie.xpath(".//p[@class='quote']/span/text()"):
                info.append(movie.xpath(".//p[@class='quote']/span/text()")[0])
            else:
                info.append("")

            print("\t".join(info))

        sleep(randint(1,3))

    titles = ["名字", "别名", "图片", "导演", "主演", "评分", "评价人数", "简介"]

Xpath获取猪八戒网站信息

# -*- coding:UTF-8 -*-

"""
获取 猪八戒网 信息
"""

import requests
from lxml import etree
from lxml.etree import _Element


url = "https://foshan.zbj.com/search/service/?l=0&kw=java%E5%90%8E%E5%8F%B0&r=2"
# 请求网页内容
resp = requests.get(url)
# 看看网页内容是否正常
# print(resp.text)
# 用lxml将网页解析成 lxml.etree._Element 对象
html = etree.HTML(resp.text)

print("\t".join(["店名", "简介", "价格", "评分", "销量", "好评"]))

# 找到所有的服务商
shop_list = html.xpath('//div[@class="search-result-list-service"]/div[@data-styleonly]')  # type: list[_Element]
for shop in shop_list:
    # 店名  由于xpath返回的都是列表, 所以这里取第一个
    shop_name = shop.xpath('.//div[contains(@class,"shop-info")]/text()')[0]
    # 其中 find 方法没有完全实现 xpath 的规则, 所以这里就报错了, 不建议使用 find 方法
    # shop_name = shop.find(".//div[contains(@class,'shop-info')]/text()")

    # 简介
    shop_brief = shop.xpath('.//div[contains(@class,"name-pic-box")]/a/text()')[0]
    # 价格
    shop_price = shop.xpath('.//div[contains(@class,"price")]/span/text()')[0]
    # 评分
    shop_scores = shop.xpath('.//div[contains(@class,"fraction")]//span/text()')
    # 为了适配没有评分的情况
    shop_score = shop_scores[0] if shop_scores else "0"
    # 销量
    shop_sales = shop.xpath('.//div[contains(@class,"sales")]//span[2]/text()')[0]
    # 好评
    shop_evaluate = shop.xpath('.//div[contains(@class,"evaluate")]//span[2]/text()')[0]

    print("\t".join([shop_name, shop_brief, shop_price, shop_score, shop_sales, shop_evaluate]))

在这里插入图片描述

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值