在上面一章中,我们实现了一个最基本的爬虫,但是提取页面信息时使用的是正则表达式,这样的话,万一哪个地方写错了,就会导致整个匹配失败,所以很不方便。
对于页面的节点来说,它可以定义id、class或其他属性。节点之间还有层次关系,在网页中可以通过XPath或CSS选择器来定位一个或多个节点,然后通过调用响应方法获取它的正文内容或属性。
python中,解析库已经很多,比较强大的解析库有lxml、Beautiful Soup、pyquery等,接下来将讲解前两个解析库的用法。
XPath
XPath全称为XML Path Language,即XML路径语言,在XML文档中查找信息的语言。
XPath常用规则
表达式 | 描述 |
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
如://title[@lang=’eng’]选择素有名称为title同时属性lang的值为’eng’的节点。
实例:
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>
"""
html =etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))
运行结果:
这里先导入lxml库的etree模块,调用HTML类进行初始化,就构造了一个XPath解析对象。
还可以直接读取文本文件进行解析:
html = etree.parse('./test.html',etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
所有节点:
用//开头的XPath规则来选取所有符合要求的节点
result = html.xpath('//*')
print(result)
运行结果:
*代表匹配所有节点,这里返回的每个元素都是Element类型,后面跟了节点的名称,如html、body、div、ul、li、a等。
也可以为特殊名名称节点:
result = html.xpath('//li')
子节点:
可以通过/或//即可查找元素的子节点或子孙节点,如现在想选择li节点的所有直接a子节点
result = html.xpath('//li/a')
或
result = html.xpath('//ul//a')
父节点:
可以通过..(两点)查找父节点。
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)
结果为:['item-1'],打印li节点的class属性。
也可以通过parent::来获取父节点:
result = html.xpath('//a[@href="link4.html"]/parent::*/@class')
属性匹配:
可以通过@选取属性,从而达到过滤效果:
result = html.xpath('//li[@class="item-0"]')
文本获取:
可以用XPath中的text()方法获取节点中的文本,
result = html.xpath('//li[@class="item-0"]/text()')
print(result)
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)
运行结果:
分析:/选取直接子节点,li节点没有text。
属性获取:
result = html.xpath('//li/a/@href')
print(result)
多属性匹配:
如果一个节点有多个属性,
text = """
<li class="li li-first" name="item"><a href="link.html">first item</a><li>
"""
html = etree.HTML(text)
result = html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
print(result)
按序选择:
result = html.xpath('//li[1]/a/text()')
print(result)
result = html.xpath('//li[last()]/a/text()')
print(result)
result = html.xpath('//li[position()<3]/a/text()')
print(result)
result = html.xpath('//li[last()-2]/a/text()')
print(result)
运行结果如下:
补充----获取指定节点的html源码:
result = html.xpath('//ul')
print(result)
print(etree.tostring(result[0],encoding='utf-8'))
以上就是XPath的学习,接下来可以通过一个作业来实践以下:
利用requests和XPath爬取某个静态网页的内容,因为我们的目标是爬取新闻内容,所以这里以一个静态新闻网页为例子。(全部代码见news.py)
网页地址为:https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8
第一步:打开网址,可以看到下图
现在我们要爬取的是文章内容,包含标题,时间,作者,主内容,而不需要其他非相关东西。
第二步:打开开发者模式,查看源码
我们需要的内容在节点div class=”container”下的div class=”content-a”下,文章段落为p节点,标题为h1节点,信息为span节点,接下来就可以写代码了。见.py文件。
import requests
from lxml import etree
def get_html(url):
headers={"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0"}
response = requests.get(url,headers=headers)
if response.status_code==200:
return response.text
return None
def main():
url = 'https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8'
html = get_html(url)
html = etree.HTML(html)
# 标题
print('标题:')
print(html.xpath('//div[@class="content-a" and @id="article"]//h1[@class="a-title"]/strong/text()'))
#信息
print('时间:')
print(html.xpath('//div[@class="content-a" and @id="article"]//div[@class="a-info"]/span[1]/text()'))
#责编
print('责编:')
print(html.xpath('//div[@class="content-a" and @id="article"]//div[@class="a-edit"]/span[1]/text()'))
# 新闻内容
print('新闻内容:')
print(html.xpath('//div[@class="content-a" and @id="article"]//div[@class="a-con"]//p/text()'))
# 新闻内容html代码,这样的话,图片链接还在
html1 = etree.tostring(html.xpath('//div[@class="a-con"]')[0],encoding='utf-8')
#print(html1.decode('utf-8'))
html1 = etree.HTML(html1.decode('utf-8'))
#print(html1.xpath('//p/text()'))
if __name__=='__main__':
main()
如果对你有用,点个赞 手动笑脸(*_*)