前言
本文主要讲解XPath语法和lxml模块、lxml库、BeautifulSoup4库、正则表达式和re模块、requests库和requests_html库。文末附爬虫爬取wallhaven的壁纸流程和代码(requests_html和requests方式)。
一、XPath语法和lxml模块
1.Xpath语法
1.1 什么是XPath?
Xpath
(XML路径语言)是一门在XML和HTML文档中查找信息的语言,可用于XML和HTML文档中对元素和属性进行遍历。
1.2 XPath开发工具
Chrome插件XPath帮助器。
Firefox插件请尝试XPath。
1.3 XPath语法
选取摘要:
这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
表达式 | 描述 | 示例 | 结果 |
---|---|---|---|
节点名 | 选取此后的所有子例程 | 书店 | 选取bookstore下所有的子例程 |
/ | 如果是在最前面,代表从根路由器选取。 | /书店 | 挑选根元素下所有的书店摘要 |
// | 从总体上游中选择基线,随便在其中位置 | //书 | 从总体上游中找到所有的图书摘要 |
@ | 选取某人的属性 | //本书[@price] | 选择所有拥有价格属性的书 |
。 | 当前官员 | 。/一种 | 选取当前上游下的a标签 |
谓语:
谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中。
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 | 描述 |
---|---|
/ bookstore / book [1] | 挑选bookstore下的第一个子元素 |
/ bookstore / book [last()] | 挑选bookstore下的倒数第二个书元素。 |
书店/书[position()❤️] | 挑选bookstore下前面两个子元素。 |
//本书[@price] | 挑选拥有价格属性的书元素 |
// book [@ price = 10] | 选取所有属性price等于10的book元素 |
通配符
*
表示通配符。
通配符 | 描述 | 示例 | 结果 |
---|---|---|---|
* | 匹配任意基线 | /书店/* | 挑选bookstore下的所有子元素。 |
@ * | 匹配二级中的任何属性 | //书[@*] | 挑选所有带有属性的book元素。 |
选择多个路径:
通过在路径表达式中使用“ |
”运算符,可以选择多个路径。
示例如下:
//bookstore/book | //book/title
# 选取所有book元素以及book元素下所有的title元素
二、lxml库
lxml是一个HTML / XML的解析器,主要的功能是如何解析和提取HTML / XML数据。
lxml和正则一样,也是用C实现的,是一种高性能的Python HTML / XML解析器,我们可以利用之前学习的XPath语法,来快速定位特定元素以及信息。
lxml python官方文档:http : //lxml.de/index.html
需要安装C语言库,可使用pip安装:pip install lxml
1、基本使用:
我们可以利用他来解析HTML代码,并且在解析HTML代码的时候,如果HTML代码不规范,他会自动的进行补全。示例代码如下:
# 使用 lxml 的 etree 库
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> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
#利用etree.HTML,将字符串解析为HTML文档
html = etree.HTML(text)
# 按字符串序列化HTML文档
result = etree.tostring(html)
print(result)
输入结果如下:
<html><body>
<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></li>
</ul>
</div>
</body></html>
可以看到。lxml会自动修改HTML代码。示例中完全补全了li标签,还添加了body,html标签。
从文件中读取html代码:
除了直接使用串行进行解析,lxml还支持从文件中读取内容。我们新建一个hello.html文件:
<!-- hello.html -->
<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"><span class="bold">third item</span></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></li>
</ul>
</div>
然后利用etree.parse()方法来读取文件。示例代码如下:
from lxml import etree
# 读取外部文件 hello.html
html = etree.parse('hello.html')
result = etree.tostring(html, pretty_print=True)
print(result)
输入结果和之前是相同的。
2、在lxml中使用XPath语法:
2.1 获取所有li标签:
from lxml import etree
html = etree.parse('hello.html')
print type(html) # 显示etree.parse() 返回类型
result = html.xpath('//li')
print(result) # 打印<li>标签的元素集合
2.2 获取所有li元素下的所有类属性的值:
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/@class')
print(result)
2.3 获取li标签下href为www.baidu.com的a标签:
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/a[@href="www.baidu.com"]')
print(result)
2.4 获取li标签下所有span标签:
from lxml import etree
html = etree.parse('hello.html')
#result = html.xpath('//li/span')
#注意这么写是不对的:
#因为 / 是用来获取子元素的,而 <span> 并不是 <li> 的子元素,所以,要用双斜杠
result = html.xpath('//li//span')
print(result)
2.5 获取li标签下的a标签里的所有类别:
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/a//@class')
print(result)
2.6 获取最后一个li的a的href属性对应的值:
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()]/a/@href')
# 谓语 [last()] 可以找到最后一个元素
print(result)
2.7获取倒数第二个li元素的内容:
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a')
# text 方法可以获取元素内容
print(result[0].text)
2.8 获取倒数第二个li元素的内容的第二种方式:
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a/text()')
print(result)
使用requests和xpath爬取电影天堂
示例代码如下:
import requests
from lxml import etree
BASE_DOMAIN = 'http://www.dytt8.net'
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
'Referer': 'http://www.dytt8.net/html/gndy/dyzz/list_23_2.html'
}
def spider():
url = 'http://www.dytt8.net/html/gndy/dyzz/list_23_1.html'
resp = requests.get(url,headers=HEADERS)
# resp.content:经过编码后的字符串
# resp.text:没有经过编码,也就是unicode字符串
# text:相当于是网页中的源代码了
text = resp.content.decode('gbk')
# tree:经过lxml解析后的一个对象,以后使用这个对象的xpath方法,就可以
# 提取一些想要的数据了
tree = etree.HTML(text)
# xpath/beautifulsou4
all_a = tree.xpath("//div[@class='co_content8']//a")
for a in all_a:
title = a.xpath("text()")[0]
href = a.xpath("@href")[0]
if href.startswith('/'):
detail_url = BASE_DOMAIN + href
crawl_detail(detail_url)
break
def crawl_detail(url):
resp = requests.get(url,headers=HEADERS)
text = resp.content.decode('gbk')
tree = etree.HTML(text)
create_time = tree.xpath("//div[@class='co_content8']/ul/text()")[0].strip()
imgs = tree.xpath("//div[@id='Zoom']//img/@src")
# 电影海报
cover = imgs[0]
# 电影截图
screenshoot = imgs[1]
# 获取span标签下所有的文本
infos = tree.xpath("//div[@id='Zoom']//text()")
for index,info in enumerate(infos):
if info.startswith("◎年 代"):
year = info.replace("◎年 代","").strip()
if info.startswith("◎豆瓣评分"):
douban_rating = info.replace("◎豆瓣评分",'').strip()
print(douban_rating)
i