简单抓站的N种方式-lxml与xpath

来源:互联网 发布:软件质量保证什么阶段 编辑:程序博客网 时间:2024/05/22 08:20

使用lxml可以解析html文档,其中需要用到xpath来选取节点,包括元素、属性、文本、命名空间、处理指令、注释和根节点。下面简单介绍一下xpath,也方便自己不时查阅,随后用一个例子具体演示使用xpath是如何抓取html文档内容的。

一、xpath简介

1、路径表达式

表达式 概述 nodename 选取此节点的所有子节点。 / 从根节点选取 [绝对路径]。 // 从匹配的当前节点选择文档中的节点 [相对路径]。 . 选取当前节点。 .. 选取当前节点的父节点。 @ 选取属性。

2、表达式例子

路径表达式 表意 div 选取div元素下的的所有子节点。 /div 从根节点选取div [绝对路径]。 body/div 选取属于body子元素的所有div元素 //div 选取该文档中所有div元素 [相对路径] 。 body//div 选取属于body后代的所有div元素 //@class 选取名为class的所有属性。

3、谓语

谓语放在方括号中用于选取特定的节点

路径表达式 结果 /body/div[1] 选取body元素下的第一个div。 /body/div[last()] 选取body元素下的最后一个div。 /body/div[last()-1] 选取body元素下的倒数第二个div。[以此类推] //div[@class] 选取所有属性名为class的div元素。 //div[@class=”age”] 选取所有属性名为class并且值为age的div元素

4、未知节点的选取

选取未知的节点,需要用到xpath的通配符,基本与正则类同

通配符 表意 * 任意元素节点 @* 任意属性节点 node() 任意类型节点

如:

路径表达式 结果 /body/* 选取body元素的所有子元素。 //* 选取文档中的所有元素。 //div[@*] 选取带有任意属性的div

5、多路径的选取

在网络爬虫中,经常会遇到这一页的内容放在一个div下,下一页就放到p标签下面了,或者标签属性不一样等等。这种情况就需要用到多路径的选取策略,相当于逻辑表达式里所说的

使用运算符| 可实现多路径的选取

路径表达式 结果 //body/div | //body/p 选取body元素的所有div和p元素。 //div | //p 选取文档中的所有div和p元素。

关于xpath的基础知识有这些基本就够了,等遇到比较复杂的网页时,也能从这些基本的表达中变通得来

二、使用lxml抓站

1、简介

  • 首先还是需要得到html文档,这里使用requests
  • 其次使用lxml的etree解析html
  • 最后通过xpath选取需要的节点内容

大致流程

通过抓取豆瓣读书的书单列表简单演示一下,以备后查。
目标网页长成这个样子,我需要把书名和简介抓取下来,其余内容类似,比如作者、评分啊等等都可以。

目标网页-豆瓣读书

2、源码

def douban_book():    '''拿豆瓣读书来试一试'''    from lxml import etree    import re    url = r'https://book.douban.com/latest?icn=index-latestbook-all'    header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1'}    html = requests.get(url,headers = header).text    lhtml = etree.HTML(html)    #找到虚构类和非虚构类的所有图书    all_book = lhtml.xpath('//div[@class="article" or @class="aside"]//li/div')    for book in all_book:        #找到图书标题        book_title = re.sub(r'[\n\t\s]+','',''.join(book.xpath('h2//text()')))  #使用正则去掉换行空格等字符        #图书简介,其余字段类似        detail = re.sub(r'[\n\t\s]+','',''.join(book.xpath('p[@class="detail" or not(@class)]/text()')))  #因为还有一部分p标签是没有属性的,注意这里的or与not        yield {book_title:detail}   #返回字典,也可以保存到文本或数据库

调用测试代码:

    for book in douban_book():        print(book)

3、结果

结果