目录
xpath简介
xpath(xml path language)是一门在xml和html文档中查找信息的语言,可用来在xml和html文档中对元素和属性进行遍历。
我们爬取页面之后用xpath语法获取我们需要的内容。
可以使用谷歌插件在页面中直接写xpath语法。可以从我的资源中下载。使用效果如下:
xpath语法
语法比较简单可以参考: xpath语法
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
选取节点
xpath使用路径表达式来选取xml文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
lxml
lxml 是一种使用 Python 编写的库,可以迅速、灵活地处理 XML 和 HTML。lxml库结合libxml2快速强大的特性,使用xpath语法来进行文件格式解析,与Beautiful相比,效率更高。经过lxml处理过的html文本会得到一个Element对象,可以直接通过xpath语法进行过滤。
直接解析文本
但是直接tostring会得到一个字节类型的内容,如果需要我可以进行重写编码再转码。
从文件中读取html
在项目目录下新建一个test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>你好</h1>
</body>
</html>
lxml中有一个parse函数可以传入文件进行解析,但是我们直接使用有报错:
我们看到报错是xml的语法错误,我们知道xml都是一个开始节点对应一个结束节点的。但是我们Html中并不都是这样的。这时因为parse函数默认是采用xml解析器去解析的,上面的html函数是直接html解析器解析。因此使用parse的时候我们要指定html解析器。
lxml和xpath结合提取html中的内容
准备了拉勾网的职位网页。
提取内容:
用parse函数解析出来的filehtml是个Element对象,用它调用xpath函数,参数就是xpath语法表达式,可以得到选择的标签。但是注意这里得到的结果是个Element对象数组,而且这个数组我们根据索引获取的时候是从1开始的。
我们通过遍历可以得到内容:
根据需要在xpath函数中填写表达式就可以得到想要的结果。
再举一个例子,如果要获取a标签中的href属性值,则可以按照如下方式:
注意下面获取的是div下的直接子节点a标签,因为只用了一个"/",如果a标签不是直接子元素又想获取就要用"//"
如果想获取标签中的文本信息,如职位信息:
可以使用text()函数。要注意"/" 和"//"的区别。
还有一点"."需要注意,假如我们要根据下面
<div class="position">获取职位信息的href值。
我们可以根据第一次返回的Element再次使用xpath函数进行匹配,
但是需要注意的是
i.xpath(".//a/@href")需要加那个".",表示以当前元素为根节点,不然就会把从html根路径开始查找把所有的href都查出来了
如下如果不加"."得到的结果。
爬取天堂电影案例
# coding:utf-8
from lxml import etree
import requests
BASE_DOMAIN = "https://www.dy2018.com/"
url = "https://www.dy2018.com/html/gndy/dyzz/index_2.html"
proxy = {
'http': '117.69.150.100:9000' # 设置代理
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36'
}
def get_detail_urls(url):
response = requests.get(url, headers=headers, proxies=proxy)
# requests库,默认会使用自己猜测的编码方式,将抓取下来的网页进行解码,然后存储到text属性。这里乱码我们需要自己指定解码方式
# text = response.content.decode('gbk')
text = response.text
# with open('dianying.html','w',encoding='utf-8') as fb:
# fb.write(response.text)
html = etree.HTML(text)
detail_urls = html.xpath("//table[@class='tbspan']//a/@href") # 获取a标签下的href属性
# for detail_url in detail_urls:
# print(BASE_DOMAIN+detail_url)
detail_urls = map(lambda url: BASE_DOMAIN + url, detail_urls) # 使用无名函数,将列表中的每一项都执行一下函数。等价于下面的:
# def abc(url):
# return BASE_DOMAIN+url
# index = 0
# for detail_url in detail_urls:
# detail_url = abc(detail_url)
# detail_urls(index) = detail_url
# index += 1
return detail_urls
# 获取内容页数据
def parse_detail_page(url):
response = requests.get(url, headers=headers)
movie = {}
# text = response.content.decode('utf-8')
text = response.content.decode('gbk')
html = etree.HTML(text)
title = html.xpath("//div[@class='title_all']")
# for x in title:
# print(etree.tostring(x,encoding='utf-8').decode('utf-8'))
movie['title'] = title
zoomE = html.xpath("//div[@id='Zoom']")[0]
imgs = zoomE.xpath(".//img/@src")
cover = imgs[0]
screenshot = imgs[1]
movie['cover'] = cover
movie['screenshot'] = screenshot
def parse_info(info, rule):
return info.replace(rule, "").strip()
infos = zoomE.xpath(".//text()")
for index, info in enumerate(infos):
if info.startswith("◎年 代"):
info = info.replace("◎年 代", "").strip() # 将年代字符串替换为空,strip方法去掉前后空格
movie['year'] = info
elif info.startswith("◎产 地"):
info = parse_info(info, "◎产 地")
movie['country'] = info
elif info.startswith("◎类 别"):
info = parse_info(info, "◎类 别")
movie['category'] = info
elif info.startswith("◎豆瓣评分"):
info = parse_info(info, "◎豆瓣评分")
movie['douban_rate'] = info
elif info.startswith("◎片 长"):
info = parse_info(info, "◎片 长")
movie['duration'] = info
elif info.startswith("◎导 演"):
info = parse_info(info, "◎导 演")
movie['director'] = info
elif info.startswith("◎主 演"):
info = parse_info(info, "◎主 演")
actors = [info]
for x in range(index + 1, len(infos)): # 因为演员不止一个,所以要用遍历形式打印
actor = infos[x].strip()
if actor.startswith("◎"):
break
actors.append(actor)
movie['actors'] = actors
elif info.startswith("◎简 介"):
info = parse_info(info, "◎简 介")
for x in range(index + 1, len(infos)):
profile = infos[x].strip()
movie['profile'] = profile
down_url = html.xpath("//td[@bgcolor='#fdfddf']/a/@href")[0]
movie['download_url'] = down_url
return movie
# 获取列表数据:
def spider():
base_url = "https://www.dy2018.com/html/gndy/dyzz/index_{}.html"
movies = []
for x in range(2, 9): # 第一个for循环,用来控制电影共有7页
url = base_url.format(x)
# print(url)
detail_urls = get_detail_urls(url)
# url = "https://www.dy2018.com/html/gndy/dyzz/index_2.html"
# detail_urls = get_detail_urls(url)
for detail_url in detail_urls: # 第二个for循环,用来遍历一页中所有电影的详情url
# print(detail_url)
movie = parse_detail_page(detail_url)
movies.append(movie)
print(movie)
if __name__ == '__main__':
spider()