- XPath是在HTML/XML中查找信息的语言
- lxml模块
XPath
xpath节点关系
每个xml标签都叫做节点,最顶层的节点为根节点
节点选择工具
Chrome插件 XPath Helper 密码:337b
xpath语法
路径表达式
表达式 | 描述 |
---|---|
节点名称 | 选中该元素 |
/ | 从根节点选取、或者是元素和元素间的过渡 |
// | 获取任意字节 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@属性值 | 选取属性 |
text() | 选取文本 |
查找特定节点
路径表达式 | 结果 |
---|---|
节点名称[@属性="值"] | 选取该节点属性值的所有节点元素 |
/父节点/子节点[1] | 选取父节点的第一个子节点 |
/父节点/子节点[last()] | 选取父节点的倒数第一个子节点 |
/父节点/子节点[last()-1] | 选取父节点的倒数第二个子节点 |
/父节点/子节点[position()>1] | 选择父节点下面的子节点,从第二个开始选择 |
//节点名称/title[text()='Harry Potter'] | 选择所有节点下的title元素,仅仅选择文本为Harry Potter的 |
通配符
路径表达式 | 结果 |
---|---|
/节点名称/* | 选取节点下的所有子元素 |
//* | 选取文档中的所有元素 |
//title[@*] | 选取所有带有属性的 title 元素 |
选取若干路径
使用“|”运算符连接路径表达式
lxml模块
安装
pip install lxml
使用
- 导入lxml模块的etree库
from lxml import etree
- 利用etree.HTML,实例化Element对象
使用xpath(路径表达式),返回列表
html = etree.HTML(text)
ret_list = html.xpath("xpath字符串")
作业:
用XPath来做一个简单的爬虫,爬取某个贴吧里的所有帖子,获取每个帖子的标题,连接和帖子中图片
#coding:utf-8
import os
import requests
from lxml import etree
"""
用XPath来做一个简单的爬虫,爬取某个贴吧里的所有帖子,获取每个帖子的标题,连接和帖子中图片
"""
class TieBa():
def __init__(self):
self.url = "http://tieba.baidu.com/f?ie=utf-8&kw=python&fr=search"
self.headers = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) "
}
def get_data(self, url):
response = requests.get(url, headers=self.headers)
return response.content
def parse_data(self, data):
# 解析:获取每个帖子的标题,连接和帖子中图片
html = etree.HTML(data)
dt_list = html.xpath('//ul[@id="thread_list"]/li/div/div/div/div/a')
data_list = []
for dt in dt_list:
temp = {}
# xpath()返回的是个列表
temp['title'] = dt.xpath('./text()')[0]
temp['link'] = 'https://tieba.baidu.com' + dt.xpath('./@href')[0]
data_list.append(temp)
# 匹配下一页的链接
try:
next_url = 'http:' + html.xpath('//a[text()="下一页>"]/@href')[0]
except:
next_url = None
return data_list, next_url
def image_data(self, data):
html = etree.HTML(data)
image_list = html.xpath('//*[contains(@id,"post_content_")]/img/@src')
return image_list
def download(self, image_list):
if not os.path.exists('images'):
os.makedirs('images')
for image in image_list:
# os.sep根据所属平台生成分隔符
filename='images'+os.sep+image.split("/")[-1]
if "sign" in image:
data=self.get_data(image)
with open(filename, 'wb') as f:
f.write(data)
def run(self):
# url
# header
next_url = self.url
# 循环
while True:
# 发送请求获取响应
data = self.get_data(next_url)
# 解析:获取每个帖子的标题,连接
data_list, next_url = self.parse_data(data)
print(data_list)
# 打开每个帖子详情页,解析,获取图片链接,下载
for data in data_list:
tz_url=data['link']
tz_data = self.get_data(tz_url)
image_list = self.image_data(tz_data)
print(image_list)
# 保存
self.download(image_list)
# 根据是否有下一页判断是否退出循环
if not next_url:
break
if __name__ == "__main__":
baidu = TieBa()
baidu.run()