1、xpath学习笔记
1)xpath描述
xpath(XML Path Language)是一门在XML和HTML文档中查找信息的语言,可用来在XML和HTML文档中对元素和属性进行遍历。
2)xpaht开发工具
A)Chrome插件XPath Helper
B)Firefox插件Try XPath
3)xpath语法
选取节点:xpath使用路径表达式来选取XML文档中的节点或节点集。
A)当/
在最前面时,选取根节点下面的元素,否则选取某个节点下面的元素,然后写标签名,再写谓词进行选取,示例代码如下:
/div[@class = 'abc']
/div/tr[@class = 'xyz']
B)使用//
选取整个页面当中的元素,不管在任何位置都是,然后写标签名,再写谓词进行选取,示例代码如下:
//div[@class = 'abc']
//div//tr[@class = 'xyz']
C)使用@
选取某个节点的属性,使用中括号[]
括起来,示例代码如下:
//div[@class = 'abc']
D)使用.
指定当前节点,示例代码如下:
./div[@class = 'abc']
谓词用法:谓词用来查找某个特定的节点或包含某个指定的值的节点,被嵌在中括号
[]
中。
A)选取某个节点下的第一个节点元素,示例代码如下:
//div//tr[1]
B)选取某个节点下的倒数第二个节点元素,示例代码如下:
//div//tr[last()]
C)选取某个节点下的前面两个节点元素,示例代码如下:
//div//tr[position() < 3]
D)选取某个节点下拥有某个属性的节点元素,示例代码如下:
//div//tr[@class]
E)选取某个节点下等于某个值的所有属性的节点元素,示例代码如下:
//div//tr[@class = 'abc]
通配符:使用
*
代表通配符。
A)使用*
匹配任意节点,示例代码如下:
//div/*
B)使用@*
匹配节点中的任意属性,示例代码如下:
//div//tr[@*]
选取多个路径:通过在路径表达式中使用
|
运算符,可以选取若干个路径,示例代码如下:
//div//tr[last()] | //div//td[@class = 'abc] | //div//p
需要注意的知识点:
1、/
和//
的区别:/
代表只获取直接子节点,//
代表获取子孙节点。一般//
用的比较多,当然也要视情况而定。
2、contains:有时候某个属性中包含了多个值,那么可以使用contains()
函数,示例代码如下:
//div[contains(@class, 'job_detail')]
3、谓词中的下标是从1开始的,不是从0开始的。
2、lxml学习笔记
1)lxml描述
lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,可以使用之前学习的XPath语法,来快速的定位特定元素以及节点信息。
lxml Python 官方文档:http://lxml.de/index.html。
2)解析HTML字符串
使用
lxml.etree.HTML
进行解析,示例代码如下:
htmlElement = etree.HTML(text)
print(etree.tostring(htmlElement, encoding = 'utf-8').decode('utf-8'))
3)解析HTML文件
使用
lxml.etree.parse
进行解析,示例代码如下:
htmlElement = etree.HTML('tencent.html')
print(etree.tostring(htmlElement, encoding = 'utf-8').decode('utf-8'))
这个函数默认使用的是
XML
解析器,所以如果碰到一些不规范的HTML
代码的时候就会解析错误,这时候就要自己创建HTML
解析器,示例代码如下:
parser = etree.HTMLParser(encoding = 'utf-8')
htmlElement = etree.parse('lagou.html', parser = parser)
print(etree.tostring(htmlElement, encoding = 'utf-8').decode('utf-8'))
3、lxml与xpath结合
1)获取所有li
标签
from lxml import etree
html = etree.parse('hello.html')
print type(html) # 显示etree.parse() 返回类型
result = html.xpath('//li')
print(result) # 打印<li>标签的元素集合
2)获取所有li元素下的所有class属性的值
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/@class')
print(result)
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)
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)
5)获取li标签下的a标签里的所有class
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/a//@class')
print(result)
6)获取最后一个li的a的href属性对应的值
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()]/a/@href')
# 谓语 [last()] 可以找到最后一个元素
print(result)
7)获取倒数第二个li元素的内容
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a')
# text 方法可以获取元素内容
print(result[0].text)
8)获取倒数第二个li元素的内容的第二种方式
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a/text()')
print(result)
9)注意事项
A)使用
xpath
语法,应该使用Element.xpath
方法来执行xpath
的选择,xpath
函数返回的永远都是一个列表;
B)获取文本,是通过xpath
中的text()
函数;
C)在某个标签下,再执行xpath
函数,获取这个标签下的子孙元素时,应该在//
之前加一个点.
,代表是在在当前元素下获取。
4、实战示例
使用
requests
和xpath
爬取“电影天堂”中“最新电影”的所有数据,示例代码如下:
import requests
from lxml import etree
def url_status_code(target_url, headers):
'''
获取target_url的status_code(即url访问状态码),常用状态码如下:
200:返回消息为“OK”,描述-请求被确认
201:返回消息为“Created”,描述-请求是完整的,新的资源被创建
202:返回消息为“Accepted”,描述-请求被接受,但未处理完
301:返回消息为“Moved Permanently”,描述-被请求的页面已经移动到了新的URL下
302:返回消息为“Found”,描述-被请求的页面暂时性地移动到了新的URL下
303:返回消息为“See Other”,描述-被请求的页面可以在一个不同的URL下找到
400:返回消息为“Bad Request”,描述-服务器无法识别请求
403:返回消息为“Forbidden”,描述-禁止访问所请求的页面
500:返回消息为“Internal Server Error”,描述-请求不完整,服务器遇见了出乎意料的状况
502:返回消息为“Bad Gateway”,描述-请求不完整,服务器从上游服务器接受了一个无效的响应
503:返回消息为“Service Unavailable”,描述-请求不完整,服务器暂时重启或关闭
504:返回消息为“Gateway Timeout”,描述-网关超时
:param target_url: (类型:字符串)指定的url地址
:param headers: (类型:字典)设置的请求头信息
:return: (类型:整型数值)返回指定url的请求状态码
'''
response = requests.get(target_url, headers=headers) # 使用requests的get()方法请求target_url
status_code = response.status_code # 获取url访问的请求状态码,并赋值给status_code
return status_code # 返回指定url的请求状态码
def get_page_url(base_domain, page_num_max, headers):
'''
获取指定页码数中每页的url地址,该函数为生成器。
:param base_domain: (类型:字符串)访问站点的域名
:param page_num_max: (类型:整型数值)最大获取页码数
:param headers: (类型:字典)设置的请求头信息
:return: (类型:字符串)返回每页的url地址
'''
page_num = 1
while page_num <= page_num_max:
page_url = base_domain + 'html/gndy/dyzz/list_23_{}.html'.format(page_num) # 拼接每页的url地址
status_code = url_status_code(page_url, headers) # 获取指定url页面的请求状态码
'''
判断每个url页面的请求状态码是否为200,如果是,则返回,否则结束。
'''
if status_code == 200:
page_num += 1
yield page_url
else:
break
def crawl_html(target_url, headers):
'''
获取target_url的内容,并返回
:param target_url: (类型:字符串)指定的url地址
:param headers: (类型:字典)设置的请求头信息
:return: 返回target_url页面内容
'''
response = requests.get(target_url, headers = headers)
text = response.content.decode('gbk', errors = 'ignore')
return text
def get_detail_url(text, base_domain):
'''
获取指定text中每个对象的详情页url,该函数为生成器。
:param text: 指定的text内容
:param base_domain: (类型:字符串)访问站点的域名
:return: (类型:字符串)返回详情页url
'''
html = etree.HTML(text)
tables = html.xpath("//table[@class='tbspan']")
for table in tables:
detail_url = base_domain + table.xpath(".//a/@href")[0]
yield detail_url
def parse_detail_url(detail_url, headers):
'''
解析详情页中的内容
:param detail_url: (类型:字符串)指定的详情页url
:param headers: (类型:字典)设置的请求头信息
:return: (类型:字典)返回详情页中解析出来的内容
'''
movie = {}
text = crawl_html(detail_url, headers)
html = etree.HTML(text)
title = html.xpath("//div[@class='title_all']//font[@color='#07519a']//text()")[0]
movie['title'] = title
zoom = html.xpath("//div[@id='Zoom']")[0]
cover = zoom.xpath(".//img/@src")
if cover != []:
cover = cover[0]
movie['cover'] = cover
infos = zoom.xpath(".//text()")
def parse_info(info, rule):
return info.replace(rule, "").strip()
for index, info in enumerate(infos):
if info.startswith("◎译 名"):
info = parse_info(info, "◎译 名")
movie['translation'] = info
elif info.startswith("◎片 名"):
info = parse_info(info, "◎片 名")
movie['name'] = info
elif info.startswith("◎年 代"):
info = parse_info(info, "◎年 代")
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['language'] = info
elif info.startswith("字 幕"):
info = parse_info(info, "字 幕")
movie['subtitles'] = info
elif info.startswith("◎上映日期"):
info = parse_info(info, "◎上映日期")
movie['release_date'] = info
elif info.startswith("◎IMDb评分"):
info = parse_info(info, "◎IMDb评分")
movie['IMDb'] = info
elif info.startswith("◎豆瓣评分"):
info = parse_info(info, "◎豆瓣评分")
movie['douban_rating'] = 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, "◎编 剧")
writers = [info]
for x in range(index+1, len(infos)):
writer = infos[x].strip()
if writer.startswith("◎"):
break
writers.append(writer)
movie['writers'] = writers
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, "◎标 签")
movie['label'] = info
elif info.startswith("◎简 介"):
info = parse_info(info, "◎简 介")
for x in range(index + 1, len(infos)):
profile = infos[x].strip()
if profile.startswith("【下载地址】"):
break
movie['profile'] = profile
download_url = html.xpath("//td[@bgcolor='#fdfddf']/a/text()")
movie['download_url'] = download_url
return movie
if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.9 Safari/537.36',
'Cookie': '37cs_pidx=1; 37cs_user=37cs20498016905; 37cs_show=253; UM_distinctid=171521ba5e1a5-0d67214e920904-721f3a40-13c680-171521ba5e27a; bz_finger=6133b30a7ebc8a1a448492bb4dbea710; CNZZDATA1260535040=1919690611-1586220307-https%253A%252F%252Fwww.dytt8.net%252F%7C1586225707; cscpvrich5041_fidx=4',
}
base_domain = 'https://www.dytt8.net/'
page_num_max = 1 # 可以通过修改page_num_max的值来确定需要爬取总页数
movies = []
page_url_generator = get_page_url(base_domain, page_num_max, headers)
for page_url in page_url_generator:
text = crawl_html(page_url, headers)
detail_url_generator = get_detail_url(text, base_domain)
for detail_url in detail_url_generator:
movie = parse_detail_url(detail_url, headers)
movies.append(movies)
print(movie)
print(movies)