当前位置: 首页 > news >正文

Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery

PyQuery:一个类似jquery的python库

学习一时爽,一直学习一直爽

  Hello,大家好,我是 Connor,一个个从无到有的技术小白。上一次我们说到了 BeautifulSoup 美味的汤,BeautifulSoup 很适合刚刚接触爬虫的新手使用。虽然 BeautifulSoup 好用,但是也有它的局限性。今天我们来讲一讲 PyQuery,让我们以 JQuery的方式来快速提取我们想要的内容。废话不多说,让我们开始吧。

1. PyQuery 的简介

  pyquery 允许您在 xml 文档上进行 jquery 查询。API 尽可能与 jquery 类似。pyquery 使用lxml 进行快速 xml 和 html 操作。当然这并不能用于生成 JavaScript 代码或者与 JavaScript 进行代码交互。所以,如果你想在 Python 上运行 JavaScript 代码,那么 pyquery 不是一个好的选择。


2. PyQuery 的简单使用

  pyquery还是非常容易上手的。现在,我们以下面的这段 xml 代码为例,进行演示:

html = """
<html>
    <head>
        <title>我的示例</title>
    </head>	
    <body>
        <div class="book">
       		<a class="book_title" id="book" href="abc.html">百炼成钢</a>
        	<p class="book_author" id="author" >王者之势</p>
         	<p class="time" id="time">2019/01/13</p>
         </div>
    </body>
</html>
"""
复制代码

  首先,我们先将这段文档变成 PyQuery 对象,pyquery的操作基本都是通过PyQuery 对象来进行的。你可以这样做:

In [2]: from pyquery import PyQuery

In [3]: doc = PyQuery(html)

In [4]: type(doc)
Out[4]: pyquery.pyquery.PyQuery
复制代码

  使用 CSS 选择器可以快速地对节点进行选择,CSS 选择器是一种快速高效的选择方式,它可以在页面中实现 一对一,一对多,多对一的多种控制,使用 CSS 选择器无疑是一种快速便捷的提取方式,你只需要像JQuery一样进行选择可以了:

In [10]: print(doc(".book"))
<div class="book">
               <a class="book_title" id="book" href="abc.html">百炼成钢</a>
            <p class="book_author" id="author">王者之势</p>
             <p class="time" id="time">2019/01/13</p>
         </div>
复制代码

  可以看到,只要像JQuery一样进行选择就可以轻而易举的将我们想要的东西就选择出来了,而且不需要像Xpath一样去找它的路径


3. 使用 PyQuery 选择单一节点

3.1 通过节点名称进行选择

  在 pyquery 中,你可以通过节点名来对节点进行简单的选择,当某个节点在文档中只出现一次的时候,这种方式是最简单的,你无需考虑其他因素的影响,就像这样:

In [11]: c = doc("a")

In [12]: print(c)
<a class="book_title" id="book" href="abc.html">百炼成钢</a>
复制代码

  可以看到,很轻松的就将我们想要的节点选取出来了,但是很多时候某个节点有很多个,很少有单独出现一次的情况,这个时候我们就需要通过别的方法来进行选择了


3.2 通过属性进行选择

  你也可以通过节点的属性来选择一个节点,当某个节点的某个属性在文档中是唯一的时候,这是最方便的做法。在上面的例子文档中,有很多的节点都拥有唯一的属性,所以,您可以这样做:

In [13]: c = doc("#author")

In [14]: print(c)
<p class="book_author" id="author">王者之势</p>
复制代码

  我们可以看到,通过单独的属性是很容易选取出我们想要的的内容的,这只是 id 属性, 我们也可以使用 class 属性来进行快速选择,其写法也和 JQuery 相同,这里不再演示。 但是当我们遇到其他的属性的时候该怎么办?我们可以通过指明属性的名称和值来进行选择, 就像这样:

In [15]: c = doc('[href="abc.html"]')

In [16]: print(c)
<a class="book_title" id="book" href="abc.html">百炼成钢</a>
复制代码

  就像这样,我们只是通过一个href属性就选择出了我们想要的节点。当然,有些时候某个属性也是众多节点一起使用的,这个时候你可以结合我们讲的第一种方法,通过节点名称来进行选择,将节点与属性进行结合,来进行选择:

In [17]: c = doc('p[id="author"]')

In [18]: print(c)
<p class="book_author" id="author">王者之势</p>
复制代码

  通过上面的方法,还是基本上很容易满足我们的需求了,但是有些时候,当我们有更高的需求的时候,我们需要用到css的伪类选择器,下面,我们将介绍伪类选择器:


3.3 通过伪类来进行选择

  当某些节点拥有多个,或者与其它节点有太多重复属性的时候,我们就需要用到伪类选择器,伪类选择器出了可以使用css自带的伪类选择器之外,pyquery 还提供了类似 JQuery 一样的非标准伪类选择器,这些伪类选择器可以帮助我们快速的进行选择:

In [19]: c = doc("p:first")

In [20]: print(c)
<p class="book_author" id="author">王者之势</p>
复制代码

  伪类选择器支持几乎所有的 CSS 标准伪类以及 JQuery 的非标准伪类:

CSS 标准伪类

选择器示例示例说明
:checkedinput:checked选择所有选中的表单元素
:disabledinput:disabled选择所有禁用的表单元素
:emptyp:empty选择所有没有子元素的p元素
:enabledinput:enabled选择所有启用的表单元素
:first-of-typep:first-of-type选择每个父元素是p元素的第一个p子元素
:last-childp:last-child选择所有p元素的最后一个子元素
:last-of-typep:last-of-type选择每个p元素是其母元素的最后一个p元素
:not(selector):not(p)选择所有p以外的元素
:nth-child(n)p:nth-child(2)选择所有 p 元素的父元素的第二个子元素
:nth-last-child(n)p:nth-last-child(2)选择所有p元素倒数的第二个子元素
:nth-last-of-type(n)p:nth-last-of-type(2)选择所有p元素倒数的第二个为p的子元素
:nth-of-type(n)p:nth-of-type(2)选择所有p元素第二个为p的子元素
:only-of-typep:only-of-type选择所有仅有一个子元素为p的元素
:only-childp:only-child选择所有仅有一个子元素的p元素
:rootroot选择文档的根元素
:target#news:target选择当前活动#news元素(点击URL包含锚的名字)
:linka:link选择所有未访问链接
:visiteda:visited选择所有访问过的链接
:activea:active选择正在活动链接
:hovera:hover把鼠标放在链接上的状态
:focusinput:focus选择元素输入后具有焦点
:first-childp:first-child选择器匹配属于任意元素的第一个子元素的

元素

:lang(language)p:lang(it)

元素的lang属性选择一个开始值

上表部分取自--菜鸟教程

JQuery 非标准伪类

选择器示例示例说明
:firstp:first选择第一个

元素

:lastp:last选择最后一个

元素

:oddp:odd选择所有

元素中索引为奇数的元素

:evenp:even选择所有

元素中索引为偶数的元素

:eq(index)p:eq(0)选择所有

元素中索引为0的元素

:lt(index)p:lt(3)选择所有

元素中索引小于3的元素

:gt(index)p:gt(2)选择所有

元素中索引大于2的元素

:header:header选择所有的H1-H6的元素
:hiddenp:hidden选取所有

元素中包含有隐藏属性的元素

:contains(text)p:contains(王者)选择所有

元素中内容包含有‘王者’的元素

:has(selector)div:has(p)选择所有带有

元素的

元素
:input:input选择所有的<input>元素
:(type)type指input的类型:submit选择所有type为submit的<input>元素
input的类型包括
button submit reset text checkbox image hidden file

  通过上列的伪类你可以更快更方便的对节点进行选择,当然 pyquery 并不只有这些功能,它还有更多的功能:


4. 使用 PyQuery 选择多个节点

  当我们使用 PyQuery 进行选择的时候,难免会遇到某个节点都是使用同一种方式来进行封装的,他们的描述方式都是相同的,亦或者我们想要批量的选取某个东西的时候,他们的封装形式是相同的,这样很难进行单一的选取。或者说单一选取太麻烦了。此时我们就需要考虑选择后的遍历问题了。因为一个东西如果不能遍历,对我们取值有很大的困难,PyQuery 自然也是可以进行遍历的,可能你会这样做:

In [21]: c = doc('p')

In [22]: for item in c:
    ...:     print(item)
    ...:
<Element p at 0x21395a4b448>
<Element p at 0x21395a12888>
复制代码

  你会发现,通过这种方式打印出来的只是一个内存地址,并没有实际的东西。当我们需要遍历 PyQuery 选择出来的东西的时候,我们需要使用 items() 方法。通过这种方法可以将选择的多个内容区分开来:

In [23]: c = doc('p').items()

In [24]: for item in c:
    ...:     print(item)
    ...:
<p class="book_author" id="author">王者之势</p>

<p class="time" id="time">2019/01/13</p>

复制代码

  通过 items() 方法就可以打印出每一项的内容了。


5. PyQuery 的其它使用方法

5.1 text() 方法

  当我们获取到指定的节点的时候,有时候我们需要获取他的文本内容,这个时候就需要的使用 text() 方法了,这个方法可以取出当前 PyQuery 对象中的文本内容,例如:

In [25]: c = doc('#author')

In [26]: print(c.text())
王者之势
复制代码

5.2 attr() 方法

  有些时候我们不只是需要提取文本内容,有时候我们也需要提取节点中的属性内容,例如 href,这个时候我们就需要使用到 attr() 方法,通过该方法来进行属性内容的提取:

In [13]: tags = doc('a').attr('href')
    ...: print(tags)
abc.html
复制代码

5.3 html() 方法

  如果你所选择的当前节点还有子节点,那么你可以通过 html() 方法将当前节点下的所有子元素选择出来,当然,你也可以对它进行更改,改变当前节点下的内容:

In [27]: c = doc('div')

In [28]: print(c.html())

               <a class="book_title" id="book" href="abc.html">百炼成钢</a>
            <p class="book_author" id="author">王者之势</p>
             <p class="time" id="time">2019/01/13</p>
复制代码

  或者你可以通过向html()方法中给参数的方法来改变当前节点下的子节点内容:

In [29]: c = doc('div')

In [30]: print(c.html('abcdefghijklmnopqrstuvwxyz'))
<div class="book">abcdefghijklmnopqrstuvwxyz</div>
复制代码

5.4 find() 方法

  在找到某一节点的内容之后,你还可以通过 find() 方法来在当前基础上进行二次查找甚至是多次查找,find() 的使用方法和正常的查找方式是相同的:

In [31]: c = doc('div').find('#author')

In [32]: print(c)
<p class="book_author" id="author">王者之势</p>
复制代码

5.5 children() 方法

  通过 children() 方法可以快速的选取出某个节点下的所有子节点,该方法同样适用于多次查找,该方法的使用方法和 find() 方法类似:

In [33]: c = doc('div').children('p')

In [34]: print(c)
<p class="book_author" id="author">王者之势</p>
             <p class="time" id="time">2019/01/13</p>
复制代码

  当 children() 方法不给参数的时候会默认查找出所有的子节点,请按需使用。


5.6 has_class() 方法

  有时候我们寻找某一节点需要用到 class 但每次都单独去取该属性再进行判断有些太麻烦了,PyQuery 为我们提供了 has_class() 方法,该方法可以快速的对节点是否拥有指定 class 属性进行判断:

In [35]: tags = doc.find('a').has_class('book_title')
    ...: print(tags)
True
复制代码

5.7 is_() 方法

  有的时候我们并不能只验证 class 属性,还有其他的属性需要我们进行验证,或者使用其它属性来进行验证会更加的方便,这个时候我们就需要使用 is_() 方法。这个方法可以匹配节点中的任意属性:

In [12]: tags = doc('p').eq(0).is_('[id="author"]')
    ...: print(tags)
True
复制代码

5.9 对上述方法的总结

  总的来说,我觉得 pyquery 其实并没有像想像中的那么好用,使用起来手感和 Xpath 差不了多少,甚至我个人认为 Xpaht 更加好用一些,其实如果你仔细地看 pyquery 的源码你就会发现,其实它也使用了 Xpaht ,比较讽刺吧,但是 pyquery 确实是在 Xpath 的基础上又做出了很大的改进,比如 伪类选择器 这在一定情况下确实是要比 Xpath 要好用的多,所以其实解析并没有什么难易,看你是否找对了适合的工具,就好比是否找到了适合脚的鞋子。找对了工具,事半功倍,找错了工具,事倍功半。希望你能够灵活运用。


下期预告

  Xpath 也讲了,BeautifulSoup也讲了,甚至PyQuery你也讲了,怎么就是不讲 re 正则呢??别着急,重头戏总是在最后出场,下一次我们就来讲述,如何用 re 正则来获取我们想要的内容。敬请期待下期-- Python爬虫十六式 - 第七式:正则的艺术

  好了,这就是今天的内容了,不知道你今天又学会了多少内容。我是 Connor,一个从无到有的技术小白,愿你能在前进的道路上与我一同前行!

学习一时爽,一直学习一直爽!


系列文章连接:

Python 爬虫十六式 - 第一式:HTTP协议 >>>
Python 爬虫十六式 - 第二式:urllib 与 urllib3 >>>
Python 爬虫十六式 - 第三式:Requests的用法 >>>
Python 爬虫十六式 - 第四式: 使用Xpath提取网页内容 >>>
Python 爬虫十六式 - 第五式:BeautifulSoup,美味的汤 >>>
Python 爬虫十六式 - 第七式:正则的艺术 >>>
Python 爬虫十六式 - 第八式:实例解析-全书网 >>>

转载于:https://juejin.im/post/5c4c8b6cf265da6175741af9

相关文章:

  • 本科理工男如何学习Linux
  • [洛谷P2511][HAOI2008]木棍分割
  • C语言之路-2-判断
  • JavaScript面向对象名词详解
  • java对象拷贝最完全解说
  • JVM,DVM,ART
  • 微软工程师认为 Mozilla 也应该拥抱 Chromium
  • 司法部:做好春节期间在押罪犯的离监探亲工作
  • 斯内德将出任2020欧洲杯荷兰地区形象大使
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • 使用Jmeter输出错误响应结果到日志
  • Hunt framework 2.0.0 发布,简单且高性能的 Web 服务框架
  • 补贴退坡幅度进一步加大 新能源汽车会涨价吗
  • Linux基础_软件包管理
  • Nginx配置文件的高亮显示设置
  • avalon2.2的VM生成过程
  • C++类的相互关联
  • Codepen 每日精选(2018-3-25)
  • flutter的key在widget list的作用以及必要性
  • Java反射-动态类加载和重新加载
  • select2 取值 遍历 设置默认值
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 彻底搞懂浏览器Event-loop
  • 复习Javascript专题(四):js中的深浅拷贝
  • 猴子数据域名防封接口降低小说被封的风险
  • 区块链分支循环
  • 实战|智能家居行业移动应用性能分析
  • ​Spring Boot 分片上传文件
  • # 数据结构
  • #《AI中文版》V3 第 1 章 概述
  • #Java第九次作业--输入输出流和文件操作
  • $.ajax中的eval及dataType
  • (1)(1.9) MSP (version 4.2)
  • (java)关于Thread的挂起和恢复
  • (LeetCode 49)Anagrams
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转载)深入super,看Python如何解决钻石继承难题
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .net2005怎么读string形的xml,不是xml文件。
  • .net分布式压力测试工具(Beetle.DT)
  • .net网站发布-允许更新此预编译站点
  • .net中我喜欢的两种验证码
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?
  • // an array of int
  • /dev/VolGroup00/LogVol00:unexpected inconsistency;run fsck manually
  • ::before和::after 常见的用法
  • [ SNOI 2013 ] Quare
  • [ 蓝桥杯Web真题 ]-Markdown 文档解析
  • [100天算法】-实现 strStr()(day 52)
  • [Django 0-1] Core.Checks 模块
  • [HOW TO]怎么在iPhone程序中实现可多选可搜索按字母排序的联系人选择器
  • [IE编程] IE 是如何决定Accept-Language 属性的