简单抓站的N种方式(三)-lxml与xpath
2017-07-28 本文已影响220人
周且南_laygin
使用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)