简单说，xpath就是选择XML文件中节点的方法。所谓节点（node），就是XML文件的最小构成单位，一共分成7种。- element（元素节点） - attribute（属性节点） - text （文本节点） - namespace （名称空间节点） - processing-instruction （处理命令节点） - comment （注释节点） - root （根节点）xpath可以用来选择这7种节点。不过，下面的笔记只涉及最常用的第一种element（元素节点），因此可以将下文中的节点和元素视为同义词。一、xpath表达式的基本格式xpath通过"路径表达式"（Path Expression）来选择节点。在形式上，"路径表达式"与传统的文件系统非常类似。# 斜杠（/）作为路径内部的分割符。 # 同一个节点有绝对路径和相对路径两种写法。 # 绝对路径（absolute path）必须用"/"起首，后面紧跟根节点，比如/step/step/...。 # 相对路径（relative path）则是除了绝对路径以外的其他写法，比如 step/step，也就是不使用"/"起首。 # "."表示当前节点。 # ".."表示当前节点的父节点二、选择节点的基本规则- nodename（节点名称）：表示选择该节点的所有子节点 - "/"：表示选择根节点 - "//"：表示选择任意位置的某个节点 - "@"： 表示选择某个属性三、选择节点的实例先看一个XML实例文档。<?xml version="1.0" encoding="ISO-8859-1"?> Harry Potter 29.99 Learning XML 39.95 [例1]bookstore ：选取 bookstore 元素的所有子节点。[例2]/bookstore ：选取根节点bookstore，这是绝对路径写法。[例3]bookstore/book ：选取所有属于 bookstore 的子元素的 book元素，这是相对路径写法。[例4]//book ：选择所有 book 子元素，而不管它们在文档中的位置。[例5]bookstore//book ：选择所有属于 bookstore 元素的后代的 book 元素，而不管它们位于 bookstore 之下的什么位置。[例6]//@lang ：选取所有名为 lang 的属性。四、xpath的谓语条件（Predicate）所谓"谓语条件"，就是对路径表达式的附加条件。所有的条件，都写在方括号"[]"中，表示对节点进行进一步的筛选。[例7]/bookstore/book[1] ：表示选择bookstore的第一个book子元素。[例8]/bookstore/book[last()] ：表示选择bookstore的最后一个book子元素。[例9]/bookstore/book[last()-1] ：表示选择bookstore的倒数第二个book子元素。[例10]/bookstore/book[position()<3] ：表示选择bookstore的前两个book子元素。[例11]//title[@lang] ：表示选择所有具有lang属性的title节点。[例12]//title[@lang='eng'] ：表示选择所有lang属性的值等于"eng"的title节点。[例13]/bookstore/book[price] ：表示选择bookstore的book子元素，且被选中的book元素必须带有price子元素。[例14]/bookstore/book[price>35.00] ：表示选择bookstore的book子元素，且被选中的book元素的price子元素值必须大于35。[例15]/bookstore/book[price>35.00]/title ：表示在例14结果集中，选择title子元素。[例16]/bookstore/book/price[.>35.00] ：表示选择值大于35的"/bookstore/book"的price子元素。五、通配符# "*"表示匹配任何元素节点。 # "@*"表示匹配任何属性值。 # node()表示匹配任何类型的节点。[例17]//* ：选择文档中的所有元素节点。[例18]// ：表示选择所有第二层的元素节点。[例19]/bookstore/* ：表示选择bookstore的所有元素子节点。[例20]//title[@*] ：表示选择所有带有属性的title元素。六、选择多个路径用"|"选择多个并列的路径。[例21]//book/title | //book/price ：表示同时选择book元素的title子元素和price子元素。相关文章： CSS选择器
这篇文章主要为大家详细介绍了python3.6根据m3u8下载mp4视频，具有一定的参考价值，感兴趣的小伙伴们可以参考一下需要下载某网站的视频，chrome浏览器按F12打开开发者模式，发现视频链接是以"blob:http"开头的链接，打开这个链接后找不到网页，网上查了下，找到了下载方法，在这里做个记录，如果有错误，欢迎指出。程序在Windows 10下运行，不过Linux应该也没问题。使用到的有re模块，requests模块和Crypto模块，其中requests模块和Crypto模块如果没安装可以使用pip命令安装。（Crypto模块安装感觉比较坑，我是从anaconda里拷贝了一份）下面开始正题：注：以下使用的m3u8文件所在的网站是自己搭建用来测试的，链接可能会失效。首先在chrome的network里找到一个m3u8文件的请求，可以通过它下载视频。通过Preview可以看到m3u8文件的内容。这里m3u8文件比较重要的内容有两个。一个是URI后面的链接，这个是加密和解密的秘钥，如果m3u8文件里有这个URI，那么这个视频就是经过了加密的，加密的方法可以看URI前面，这里是AES-128加密算法。另一个是以ts结尾链接，这个是视频片段，如果是没进行加密的，可以直接打开这个链接下载视频片段，下载下来的是后缀为ts的文件，一般可以直接播放，不过时间很短。如果是加密过的，下载后播放会提示视频文件已损坏。知道这些后，就可以进行下载了，先获取m3u8文件的内容，然后解析出秘钥（key）和ts的链接，然后下载key对每一个ts进行解密，保存到一个mp4文件里。下载用的是requests模块，解析key和ts的链接是用的re模块，解密用的是Crypto模块。使用Crypto需要注意三个地方，一个是安装和导入，第二个是Crypto里AES.new的参数，第三个是decrypt方法的参数。下面先看代码。导入模块：import re import requests from Crypto.Cipher import AES初始设置：# 保存的mp4文件名 name = "dream_it_possible.mp4" # m3u8文件的url url = "http://www.jiuyi.info/static/video/secret/dream_it_possible.m3u8" # 请求头，不一定需要，看网站更改 headers = { "Referer": "http://www.jiuyi.info/video/dream_it_possible", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36", }获取m3u8文件内容，并解析出key和ts文件的url。# 获取m3u8文件内容 r = requests.get(url) # 通过正值表达式获取key和ts的url k = re.compile(r"http://.*?\.key") # key的正则匹配 t = re.compile(r"http://.*?\.ts") # ts的正则匹配 key_url = k.findall(r.text)[0] # key的url ts_urls = t.findall(r.text) # ts的url列表下载并解密ts文件，保存为mp4文件。# 下载key key = requests.get(key_url).content # 解密并保存ts for ts_url in ts_urls: ts_name = ts_url.split("/")[-1] # ts文件名 # 解密，new有三个参数， # 第一个是秘钥（key）的二进制数据， # 第二个使用下面这个就好 # 第三个IV在m3u8文件里URI后面会给出，如果没有，可以尝试把秘钥（key）赋值给IV sprytor = AES.new(key, AES.MODE_CBC, IV=key) # 获取ts文件二进制数据 ts = requests.get(ts_url).content # 密文长度不为16的倍数，则添加二进制"0"直到长度为16的倍数 while len(ts) % 16 != 0: ts += b"0" # 写入mp4文件 with open(name, "ab") as file: # decrypt方法的参数需要为16的倍数，如果不是，需要在后面补二进制"0" file.write(sprytor.decrypt(ts)) print(name, "下载完成")到这里就下载完成了。完整代码：import re import requests from Crypto.Cipher import AES # 保存的mp4文件名 name = "dream_it_possible.mp4" # m3u8文件的url url = "http://www.jiuyi.info/static/video/secret/dream_it_possible.m3u8" # 请求头，不一定需要，看网站更改 headers = { "Referer": "http://www.jiuyi.info/video/dream_it_possible", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36", } print("正在解析：" + url.split("/")[-1]) # 获取m3u8文件内容 r = requests.get(url) # 通过正值表达式获取key和ts的链接 k = re.compile(r"http://.*?\.key") # key的正则匹配 t = re.compile(r"http://.*?\.ts") # ts的正则匹配 key_url = k.findall(r.text)[0] # key的url ts_urls = t.findall(r.text) # ts的url列表 # 下载key的二进制数据 print("正在下载key") key = requests.get(key_url).content # 解密并保存ts for ts_url in ts_urls: ts_name = ts_url.split("/")[-1] # ts文件名 # 解密，new有三个参数， # 第一个是秘钥（key）的二进制数据， # 第二个使用下面这个就好 # 第三个IV在m3u8文件里URI后面会给出，如果没有，可以尝试把秘钥（key）赋值给IV sprytor = AES.new(key, AES.MODE_CBC, IV=key) # 获取ts文件二进制数据 print("正在下载：" + ts_name) ts = requests.get(ts_url).content # 密文长度不为16的倍数，则添加b"0"直到长度为16的倍数 while len(ts) % 16 != 0: ts += b"0" print("正在解密：" + ts_name) # 写入mp4文件 with open(name, "ab") as file: # # decrypt方法的参数需要为16的倍数，如果不是，需要在后面补二进制"0" file.write(sprytor.decrypt(ts)) print("保存成功：" + ts_name) print(name, "下载完成")
python爬虫爬取视频时，实现实时进度条显示
一、全部代码展示from contextlib import closing from requests import get url = 'https://v26-web.douyinvod.com/57cdd29ee3a718825bf7b1b14d63955b/615d475f/video/tos/cn/tos-cn-ve-15/72c47fb481464cfda3d415b9759aade7/?a=6383&br=2192&bt=2192&cd=0%7C0%7C0&ch=26&cr=0&cs=0&cv=1&dr=0&ds=4&er=&ft=jal9wj--bz7ThWG4S1ct&l=021633499366600fdbddc0200fff0030a92169a000000490f5507&lr=all&mime_type=video_mp4&net=0&pl=0&qs=0&rc=ank7OzU6ZnRkNjMzNGkzM0ApNmY4aGU8MzwzNzo3ZjNpZWdiYXBtcjQwLXNgLS1kLTBzczYtNS0tMmE1Xi82Yy9gLTE6Yw%3D%3D&vl=&vr=' with closing(get(url, stream=True)) as response: chunk_size = 1024 # 单次请求最大值 # response.headers['content-length']得到的数据类型是str而不是int content_size = int(response.headers['content-length']) # 文件总大小 data_count = 0 # 当前已传输的大小 with open('文件名.mp4', "wb") as file: for data in response.iter_content(chunk_size=chunk_size): file.write(data) done_block = int((data_count / content_size) * 50) # 已经下载的文件大小 data_count = data_count + len(data) # 实时进度条进度 now_jd = (data_count / content_size) * 100 # %% 表示% print("\r [%s%s] %d%% " % (done_block * '█', ' ' * (50 - 1 - done_block), now_jd), end=" ")注：上面的url已过期，需要各位自己去找网页上的视频url二、解释1.with closing我们在日常读取文件资源时，经常会用到with open() as f:的句子。但是使用with语句的时候是需要条件的，任何对象，只要正确实现了上下文管理，就可以使用with语句，实现上下文管理是通过__enter__和__exit__这两个方法实现的。with用法（没有实现上下文管理）class Door(): def open(self): print('Door is opened') def close(self): print('Door is closed') with Door() as d: d.open() d.close()结果报错了：with用法（实现上下文管理）用__enter__和__exit__实现了上下文管理class Door(): def open(self): print('Door is opened') def close(self): print('Door is closed') with Door() as d: d.open() d.close() 结果没报错： closing用法（完美解决上述问题）一个对象没有实现上下文，我们就不能把它用于with语句。这个时候，可以用 contextlib 中的closing()来把该对象变为上下文对象。class Door(): def __enter__(self): print('Begin') return self def __exit__(self, exc_type, exc_value, traceback): if exc_type: print('Error') else: print('End') def open(self): print('Door is opened') def close(self): print('Door is closed') with Door() as d: d.open() d.close()例如：用with语句使用requests中的get(url) 也就是本文中的案例，使用with closing（）下载视频(在网页中)2.文件流stream想象一下，如果把文件读取比作向池子里抽水，同步会阻塞程序，异步会等待结果，如果池子非常大呢？因此有了文件流，它就好比你一边抽一边取，不用等池子满了再用，所以对于一些大型文件（几个G的视频）一般会用到这个参数。 （对小型文件也可以使用）3.response.headers['content-length']这表示获取文件的总大小，但是它得到的结果的数据类型是str而不是int，因此需要进行数据类型转换。4.response.iter_content()该方法一般用于从网上下载文件和网页（需要用到requests.get(url)）其中chunk_size表示单次请求最大值。5.\r和%\r表示回车（回到行首）%是一种占位符而对于%%，第一个%起到了转义的作用，使结果输出为百分号%
导入模块import requests请求方式： requests.get("https://www.baidu.com") requests.post("http://httpbin.org/post") requests.put("http://httpbin.org/put") requests.delete("http://httpbin.org/delete") requests.head("http://httpbin.org/get") requests.options("http://httpbin.org/get")获取数据： r = requests.get('http://www.baidu.com') #像目标url地址发送get请求，返回一个response对象 response.text返回的是Unicode格式，通常需要转换为utf-8格式。 response.content是二进制模式，可以下载视频之类的，如果想看的话需要decode成utf-8格式。 # response.content.decode("utf-8) 或 response.encoding="utf-8" 转码 print(r.text) # 返回响应的内容 print(r.content) # 这样获取的数据是二进制数据 print(r.url) # 打印请求网址的地址 print(r.status_code) # 打印请求页面的状态（状态码）# r.ok的布尔值便可以知道是否登陆成功 print(r.cookies) # 打印请求网址的cookies信息 print(r.headers) # 打印请求网址的headers所有信息 print(r.encoding) # 获取/修改网页编码 print(r.json()) # 返回json数据 print(r.history) # 打印请求的历史记录（以列表的形式显示）下载图片 response = requests.get("https://github.com/favicon.ico") with open('favicon.ico', 'wb') as f: f.write(response.content)例子：往请求链接中添加一些数据(data、headers、cookies、proxies...)： import requests data = {'name': 'germey', 'age': '22'} cookie = {'key':'value'} headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'} proxies = {"http": "http://127.0.0.1:9743} requests.get(url='https://www.baidu.com/baidu', params={'wd': 'python',}) # 为url传递参数 https://www.baidu.com/baidu?wd=python response = requests.post( "https://www.zhihu.com/explore", data=data, # 如果传递的是string而不是dict，需要先调用dumps方法格式化一下 cookies=cookie, headers=headers, proxies=proxies) print (response.headers.get('content-type')) #访问响应头部分内容 补： requests.get(url, allow_redirects=False) # 不允许重定向，默认True # verify=False # 关闭证书验证其他操作会话维持cookie的一个作用就是可以用于模拟登陆，做会话维持 import requests session = requests.session() session.get('http://httpbin.org/cookies/set/number/12456') response = session.get('http://httpbin.org/cookies') print(response.text)代理设置 import requests proxies = { "http": "http://127.0.0.1:9743", # 设置普通代理 "https": "https://127.0.0.1:9743", # 设置普通代理 "http": "http://user:password@127.0.0.1:9743/", # 设置用户名和密码代理 } response = requests.get("https://www.taobao.com", proxies=proxies) print(response.status_code)设置socks代理安装socks模块 pip3 install 'requests[socks]' import requests proxies = { 'http': 'socks5://127.0.0.1:9742', 'https': 'socks5://127.0.0.1:9742' } response = requests.get("https://www.taobao.com", proxies=proxies) print(response.status_code)超时设置 import requests from requests.exceptions import ReadTimeout try: response = requests.get("http://httpbin.org/get", timeout = 0.5)#设置秒数超时，仅对于连接有效 print(response.status_code) except ReadTimeout: print('Timeout')获取cookie import requests response = requests.get("https://www.baidu.com") print(response.cookies) for key, value in response.cookies.items(): print(key + '=' + value)文件上传 import requests files = {'file': open('favicon.ico', 'rb')} response = requests.post("http://httpbin.org/post", files=files) print(response.text)认证设置 import requests from requests.auth import HTTPBasicAuth r = requests.get('http://120.27.34.24:9001', auth=HTTPBasicAuth('user', '123')) # r = requests.get('http://120.27.34.24:9001', auth=('user', '123')) print(r.status_code)关闭证书验证 import requests response = requests.get('https://www.12306.cn',verify=False) print(response.status_code)消除验证证书的警报 from requests.packages import urllib3 import requests urllib3.disable_warnings() response = requests.get('https://www.12306.cn',verify=False) print(response.status_code)手动设置证书 import requests response = requests.get('https://www.12306.cn', cert=('/path/server.crt', '/path/key')) print(response.status_code)异常处理 # RequestException继承IOError, # HTTPError，ConnectionError,Timeout继承RequestionException # ProxyError，SSLError继承ConnectionError # ReadTimeout继承Timeout异常 import requests from requests.exceptions import ReadTimeout, ConnectionError, RequestException try: response = requests.get("http://httpbin.org/get", timeout = 0.5) print(response.status_code) except ReadTimeout: print('Timeout') except ConnectionError: print('Connection error') except RequestException: print('Error') # RequestException # 发送一个模糊的异常 # ConnectionError # 发生连接错误时的异常 # HTTPError # 发生HTTP错误时的异常 # URLRequired # URL错误时的异常 # ConnectTimeout # 连接服务器是请求超时 # ReadTimeout # 服务器没有在指定的时间内发送数据 # Timeout # 请求超时原文出处： requests 模块笔记