用pyquery5行代码爬取百度热点新闻
导读:学习python爬虫很久了,一直习惯于requests抓取+xpath解析的两步走套路,直到我今天发现了pyquery这个爬虫利器后,才意识到python的世界没有最简,只有更简……
2020-03-06 21:22:12
01 pyquery简介
pyquery是Python的一个第三方爬虫库,仿照 jQuery实现,语法与js十分相像。如果有js基础,那么应用pyquery简直是平步青云。pyquery自带网页解析和信息提取功能,所以应用起来会非常简洁。
pyquery安装(要求cssselect和lxml库支持):
pip install pyquery
安装好pyquery包后,默认可在..\Python\Python37\Lib\site-packages目录下找到pyquery文件,主要包括5个.py文件,分别是pyquery.py、 opener.py、 cssselectpath.py、 text.py以及一个初始化文件__init__.py,其中pyquery是主应用模块,内部调用其他3个模块。在pyquery模块中,实现了多个类,其中PyQuery是主类,继承列表类(所以列表的所有接口PyQuery都可以调用)。如下命令获取其所有方法接口:
from pyquery import PyQuery as pq
dir(pq)
02 pq初始化
pyquery的初始化可接收4种格式的参数,分别是字符串类型、html、html文件地址以及需要请求的ur相关信息,尤其是支持请求url的初始化方式,大大简化了的爬虫代码实现。
- 接收字符串
doc = pq("<html>hello, world</html>")
-
接收html
doc = pq(etree.fromstring("<html>hello, world</html>")
-
接收html文件地址
doc = pq(filename=path_to_html_file)
-
接受URL及相关信息
这里具体讲解pyquery接收url相关信息的实现方式。查看pyquery初始化方法,其接收不定参数和字典参数如下:
def __init__(self, *args, **kwargs):
html = None
elements = []
self._base_url = None
self.parser = kwargs.pop('parser', None)
if (len(args) >= 1 and
isinstance(args[0], string_types) and
args[0].split('://', 1)[0] in ('http', 'https')):
kwargs['url'] = args[0]
if len(args) >= 2:
kwargs['data'] = args[1]
args = []
pass
当接受参数满足:①参数个数大于等于1个、②第一个参数为字符串类型、③第一个参数可以解析为http或https格式,则提取url及其相关信息(如headers等)并赋值给字典参数kwargs,进行网页解析:
if kwargs:
# specific case to get the dom
if 'filename' in kwargs:
html = open(kwargs['filename'])
elif 'url' in kwargs:
url = kwargs.pop('url')
if 'opener' in kwargs:
opener = kwargs.pop('opener')
html = opener(url, **kwargs)
else:
html = url_opener(url, kwargs)
if not self.parser:
self.parser = 'html'
self._base_url = url
else:
raise ValueError('Invalid keyword arguments %s' % kwargs)
当前一步提取的字典参数不为空,且包含"url"键值时则解析为需要进行网页请求。网页请求中当初始化参数中指定了解析器opener时,则调用指定解析器,否则使用pyquery自定义的url_opener方法进行解析。 进一步查看pyquery自定义的url_opener方法:
try:
import requests
HAS_REQUEST = True
except ImportError:
HAS_REQUEST = False
--------------
def url_opener(url, kwargs):
if HAS_REQUEST:
return _requests(url, kwargs)
return _urllib(url, kwargs)
--------------
def _requests(url, kwargs):
encoding = kwargs.get('encoding')
method = kwargs.get('method', 'get').lower()
session = kwargs.get('session')
if session:
meth = getattr(session, str(method))
else:
meth = getattr(requests, str(method))
pass
--------------
def _urllib(url, kwargs):
method = kwargs.get('method')
url, data = _query(url, method, kwargs)
return urlopen(url, data, timeout=kwargs.get('timeout', DEFAULT_TIMEOUT))
至此,我们知道pyquery的自定义解析器采用如下规则:
-
如果系统当前安装了requests库(第三方库,使用前需手动安装),则调用_requests方法,其中还根据初始化参数中是否指定了session来进一步区分
-
否则,则调用urllib库(python自带的网页解析库)urlopen方法进行解析
可见,pyquery相当于会自动调用requests库或默认的urllib库进行网页解析,且以前者优先,并支持携带requests的session进行解析。
03 选择器
pyquery支持与CSS一致的选择器实现方法:
from pyquery import PyQuery as pq
html = '''
<div id="test">
<ul class="c1">
hello
<link href="http://123.com">this</link>
<link href="http://456.com">is</link>
<link href="http://789.com">pyquery</link>
</ul>
<ul class="c2">
world
<link href="http://321.com">test</link>
<link href="http://654.com">for</link>
<link href="http://987.com">spider</link>
</ul>
</div>
'''
doc = pq(html)
print(type(doc))#<class 'pyquery.pyquery.PyQuery'>
print(doc("div#test>ul.c1 link"))
"""
<link href="http://123.com">this</link>
<link href="http://456.com">is</link>
<link href="http://789.com">pyquery</link>
"""
其中:
-
pyquery初始化后为PyQuery对象类型
-
pyquery使用字符串识别标签和CSS标识
-
父标签和子标签间可以用>,也可以用空格
-
可以通过属性选择特定标签,其中id用"#",class用"."
04 查找节点
pyquery可以很容易实现查找父类、子类、兄弟节点,主要方法如下:
- find()选择所有满足条件的子节点
item=doc(".c1")
link = item.find('link')
print(link)
"""
<link href="http://123.com">this</link>
<link href="http://456.com">is</link>
<link href="http://789.com">pyquery</link>
"""
- parents()选择父节点
item=doc(".c1")
p = item.parents()
print(p)
"""
<div id="test">
<ul class="c1">
hello
<link href="http://123.com">this</link>
<link href="http://456.com">is</link>
<link href="http://789.com">pyquery</link>
</ul>
<ul class="c2">
world
<link href="http://321.com">test</link>
<link href="http://654.com">for</link>
<link href="http://987.com">spider</link>
</ul>
</div>
"""
- children()选择子节点
item=doc(".c1")
c = item.children()
print(c)
"""
<link href="http://123.com">this</link>
<link href="http://456.com">is</link>
<link href="http://789.com">pyquery</link>
"""
- siblings()选择兄弟节点
item=doc(".c1")
s = item.siblings()
print(s)
"""
<ul class="c2">
world
<link href="http://321.com">test</link>
<link href="http://654.com">for</link>
<link href="http://987.com">spider</link>
</ul>
"""
05 获取信息
在完成选择节点的基础上,pyquery获取信息实现就非常简单。常用的方法如下:
- items()获取所有匹配结果(返回一个生成器)
items=doc(".c1 link").items()
print(items)#<generator object PyQuery.items at 0x000001F124C23390>
- attr()获取属性信息
for item in items:
print(item.attr('href'))
"""
http://123.com
http://456.com
http://789.com
"""
- text()获取文本信息
for item in items:
print(item.text())
"""
this
is
pyquery
"""
- html()获取相应html文本
h = doc('ul').html()
print(h)
"""
hello
<link href="http://123.com">this</link>
<link href="http://456.com">is</link>
<link href="http://789.com">pyquery</link>
"""
06 pyquery实战
了解了基本操作,我们就可以应用pyquery进行快速的网页爬取了。以百度热点新闻为例,进行简单的接口分析获得url后只需要5行代码就可以轻松获取40条搜索热点标题,简直不能比这更方便了!
from pyquery import PyQuery as pq
doc = pq(url='http://top.baidu.com/buzz?b=1&fr=20811', encoding = 'gb18030')
items = doc("td.keyword>a.list-title").items()
hots = [item.text() for item in items]
print(hots)
# ['春雨医生获投资', '肖战ins头像', '洛杉矶马拉松', '韦特斯加盟湖人', '甘薇弃优先分配权', '硕士研究生扩招', '毛剑卿退役', '皮亚琴察市长确诊', '67台ECMO发往湖北', '伊朗国家动员计划', '雷神山机器人上岗', '上海马拉松升级', '湖北人民免费看剧', '草地贪夜蛾入侵', '快女喻佳丽结婚', '惠普拒绝施乐收购', '汪曾祺百年诞辰', '美国对塔利班空袭', '鼓浪屿将恢复开放', 'Faker2000杀', '小罗因假护照被捕', '杨蓉经纪公司声明', '库里复出23分', '英国确诊人数翻倍', '英国再现集装箱藏人案', 'Facebook员工确诊', '普利兹克建筑奖', '卢浮宫重新开放', '史酷比狗预告', '黄子佼孟耿如结婚', '法国政府征用口罩', '76人vs湖人', '龙卷风袭击美国', '邓肯执教首胜', '英国首例死亡病例', '布隆伯格退出大选', '库里复出', '罗永浩宣布开直播', '湖人战胜76人', '热刺大将怒揍球迷']
当然,为了实现反爬能力更强和更加高效的爬虫,可以在pyquery初始化时传入更多参数,例如headers、session和timeout等。
07 总结
-
pyquery是一个高度集成的python第三方爬虫库,方法众多、实现简洁
-
pyquery内置了网页解析,会首先尝试调用requests库,若当前未安装则调用python内置urllib库解析
-
pyquery解析提取采用类似CSS选择器的方式进行
-
pyquery支持简洁的节点选择、属性和文本提取操作
-
pyquery还有很多其他方法,包括节点操作、伪类选择器等,但爬虫一般不需要用到