ymk2004 发表于 2023-10-17 19:33

梨视频网站视频爬取-初探索

本帖最后由 ymk2004 于 2023-10-17 20:29 编辑

本人小白一枚,最近刚开始学习python爬虫相关的内容,于是稍微做了一个实例尝试下,做的很多地方都不好,还望大家多多包涵!!!

# 前言
最近学了一部分网络爬虫的基础知识,于是就拿梨视频网站上面的视频做了一次尝试,起初觉得有一点简单,但是真正到了请求下载视频链接时,又发现无法下载。于是对比了我第一次拿到的url跟真正的视频url比较,存在不同的地方,最后发现了梨视频官方对自己的视频进行了链接的伪造以防止爬取。
最终,感觉梨视频的爬取难度还是适中的,对我这样的新手在理解网络爬虫方面还是有很大帮助的。

# python环境
```python
Python版本-3.11.5
```
```python
import requests# 用于发送HTTP请求
from lxml import etree# 用于解析HTML
import random# 用于生成随机数
import os# 用于文件操作
from multiprocessing.dummy import Pool# 使用多线程池提高爬取效率
```
# 对相关页面的观察分析
## 首先,打开梨视频的官网链接:
[梨视频官网-有故事的短视频-Pear Video](https://www.pearvideo.com/)

通过一些观察,我准备从视频量比较多的地方进行爬取,于是便找到了主页的 `人物、万象` 两个页面进行爬取。




## -->人物页面所对应的URL地址:
[人物热点短视频_-梨视频官网-Pear Video](https://www.pearvideo.com/category_1)


## -->万象页面所对应的URL地址:
[万象热点资讯_万象热点新闻-梨视频官网-Pear Video](https://www.pearvideo.com/panorama)



---

## 以`返老还童?重庆百岁老人白发变青丝,最爱《封神榜》_身体-梨视频官网-Pear Video `为例
在人物界面,鼠标拖拽到一个图片上面,在浏览器左下角位置,我们可以看到其所对应URL链接,以[返老还童?重庆百岁老人白发变青丝,最爱《封神榜》_身体-梨视频官网-Pear Video](https://www.pearvideo.com/video_1686640)为例,我们可以看到例子中的`1686640`应该具有某种作用,这里猜测为视频所对应的一个ID。
[返老还童?重庆百岁老人白发变青丝,最爱《封神榜》_身体-梨视频官网-Pear Video](https://www.pearvideo.com/video_1686640)





### `inner_url` 所对应的 `Referer` 为 `host_url`
随便点击一个视频页面进入,我们打开开发者工具,找到视频对应的URL,可以发现请求表头里面包括`Referer:https://www.pearvideo.com/category_1`




### 检查页面元素代码,找到 `inner_video` 所对应的 `src 链接`
通过开发者工具,查看网页代码,我们可以看到element中的视频部分标签:
`src="https://video.pearvideo.com/mp4/adshort/20200717/cont-1686640-15272298_adpkg-ad_hd.mp4"`
(https://video.pearvideo.com/mp4/adshort/20200717/cont-1686640-15272298_adpkg-ad_hd.mp4)





---

### 分析页面源代码,发现 inner_video 是动态加载出来的,开始分析 ajax 文件
通过分析,我们可以发现视频的`inner_url`详情页源代码中是不含有视频链接的,也就是说详情页中的视频是动态加载出来的,这时候就需要我们去分析`ajax文件`了。




#### 找到`Fetch/XHR`中的网址请求
通过抓包工具,我们可以看到`Fetch/XHR`中存在这样一个请求:`https://www.pearvideo.com/videoStatus.jsp?contId=1686640&mrd=0.17203231944534259`




#### 打开请求网址,发现问题,证明视频链接进行了伪装,即设置了反爬机制
但是打开这个链接,发现并不是我们想要的视频,而是这样的页面:




#### 使用到的反爬机制--->防盗链(`Referer`)
      其实这是网站使用的一种反爬机制,即——防盗链(`Referer`)。所以,我们所得到的这个https://www.pearvideo.com/videoStatus.jsp?contId=1686640&mrd=0.17203231944534259链接是一个伪装过的链接,不是真正的视频链接。

---

### 分析网址请求后的相应内容,对`systemTime`和`srcUrl`进行分析
紧接着,通过抓包工具,点击查看请求响应内容:

```python
{
      "resultCode":"1",
      "resultMsg":"success", "reqId":"250bf9cf-31cc-492c-a71a-4f844e1b9acb",
      "systemTime": "1697377523473",
      "videoInfo":{
            "playSta":"1",
             "video_image":"https://image.pearvideo.com/cont/20200717/cont-1686640-12430737.png",
             "videos":
             {
                      "hdUrl":"",
                      "hdflvUrl":"",
                      "sdUrl":"",
                      "sdflvUrl":"",
                      "srcUrl":"https://video.pearvideo.com/mp4/adshort/20200717/1697377523473-15272298_adpkg-ad_hd.mp4"
             }
                   }
}
```



打开这个"srcUrl":"https://video.pearvideo.com/mp4/adshort/20200717/1697377523473-15272298_adpkg-ad_hd.mp4" 会发现报错,证明这个也不是真正的视频链接。




---

我们之前在检查网页的元素代码时找到了`src="https://video.pearvideo.com/mp4/adshort/20200717/cont-1686640-15272298_adpkg-ad_hd.mp4"`
我们在浏览器中打开这个视频链接,会发现这个链接可以正常打开,证明这个链接所对应的内容就是i我们的视频。



对比下我们目前拿到的两个URL,不难发现其中的参数存在差异性,这里的1697377523473其实就是我们在相应内容中所获得到的"`systemTime`": "`1697377523473`" ,而`1686640`就是视频所对应的`contId`。
#### 对比srcUrl 和video_url,分析差异性,分析参数的具体含义
```python
"srcUrl":"https://video.pearvideo.com/mp4/adshort/20200717/1697377523473-15272298_adpkg-ad_hd.mp4"
"video_url":"https://video.pearvideo.com/mp4/adshort/20200717/cont-1686640-15272298_adpkg-ad_hd.mp4"
```

那么我们要拿到真正的视频链接`video_url`,只需要提取出相应内容json文件里的`srcUrl`,然后通过字符串的替换操作,将`systemTime`替换成`cont-contId`即可拿到真正的视频链接。

---

#### 分析请求头中的防盗链Referer,同时分析请求参数中 mrd 的具体含义
通过抓包工具,我们可以看到该请求的`Referer`是:`https://www.pearvideo.com/video_1686640`,不难看出这个Referer地址就是我们的网页详情页面的URL





在`Fetch/XHR`中存在这样一个请求:`https://www.pearvideo.com/videoStatus.jsp?contId=1686640&mrd=0.17203231944534259`,我们不难猜测出视频链接前面的`https://www.pearvideo.com/videoStatus.jsp?`应该是一致的,而链接后面所对应的是相关的参数(contId和mrd)。**参数contId就是视频独有的ID,而参数mrd 是一个随机的浮点数,这个浮点数范围是0-1之间。**

# 导入相应的模块
```python
import requests# 用于发送HTTP请求
from lxml import etree# 用于解析HTML
import random# 用于生成随机数
import os# 用于文件操作
from multiprocessing.dummy import Pool# 使用多线程池提高爬取效率
```
      为了提高视频爬取的效率,我采用了多线程的方法来下载视频。
# 创建视频的存储路径
```python
# 检查视频存储目录是否存在,如果不存在则创建
if not os.path.exists('./梨视频'):
    os.mkdir('./梨视频')
save_path = './梨视频/'
```
检查当前工作目录下是否存在名为"梨视频"的目录,如果不存在则创建它,然后将目录路径存储在`save_path`变量中,以便后续操作可以使用这个路径。
# 实例--->主要操作
在页面的详情页面代码中进行下列操作:
## 1.构建用户可以选择某视频进行下载的页面:

```python
# URL列表
host_url_list = ['https://www.pearvideo.com/category_1', 'https://www.pearvideo.com/panorama']
# 设置host_headers请求头
host_headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63"
}
# 获取网站标题信息
site_titles = []
for host_url in host_url_list:
    page_text = requests.get(host_url, headers=host_headers).text
    tree = etree.HTML(page_text)
    site_title = tree.xpath('//title/text()')
    site_titles.append(site_title)

# 用户选择URL
print("欢迎使用梨视频下载工具")
print("请选择要爬取的网站:")

for i, (url, title) in enumerate(zip(host_url_list, site_titles), 1):
    print(f"{i}. {title}")

while True:
    try:
      choice = int(input("请输入数字 1 或 2 来选择: "))
      if 1 <= choice <= 2:
            break
      else:
            print("请输入有效的选择 (1 或 2) ")
    except ValueError:
      print("请输入有效的选择 (1 或 2)")

selected_url = host_url_list
# 获取用户选择的URL
host_url = selected_url

# 发送GET请求获取主页host_url的HTML内容
page_text = requests.get(url=host_url, headers=host_headers).text
tree = etree.HTML(page_text)
# 提取host主页中的inner视频链接
inner_href_list = tree.xpath('//div[@class="vervideo-bd"]/a/@href')
# 输出inner_href链接对应的标题
print("\n以下是可供下载的视频标题:")
for i, inner_href in enumerate(inner_href_list, 1):
    inner_url = 'https://www.pearvideo.com/' + inner_href
    inner_text = requests.get(url=inner_url, headers=host_headers).text
    title = etree.HTML(inner_text).xpath('//h1[@class="video-tt"]/text()')
    print(f"{i}. {title}")

# 用户选择要下载的inner_href视频
selected_indices = input("输入要下载的视频序号(用逗号分隔,如1,3,5),或者直接按回车下载所有视频:")
if selected_indices:
    selected_indices =
else:
    selected_indices = range(len(inner_href_list))
```
## 2.提取出inner_href视频对应的标题`title`---->xpath路径如下:
```html
//h1[@class="video-tt"]/text()
```

## 3.提取inner_href视频所对应的`contId`---->xpath路径如下:
```html
//div[@id="poster"]/@data-cid
```

## 4.通过python代码生成一个`mrd`随机浮点数
```python
# 生成一个随机的mrd参数
mrd = random.random()
```
通过上面的操作,我们便可以得到视频所对应的两个参数`contId`和`mrd`
构造出用于获取视频真实链接的ajax请求url
```python
# 构造用于获取视频真实链接的ajax请求url
ajax_url = 'https://www.pearvideo.com/videoStatus.jsp?'
params = {
    "contId": contId,
    "mrd": mrd
}
# 设置ajax请求的头部,包括Referer
ajax_headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63",
    "Referer": inner_url
}
```
## 5.发送`ajax`请求,获取视频信息的`JSON`数据,提取`JSON`中的`systemTime`和`srcUrl`
```python
# 发送ajax请求,获取视频信息的JSON数据
response = requests.get(url=ajax_url, headers=ajax_headers, params=params)

# 提取JSON中的systemTime和srcUrl
systemTime = response.json()['systemTime']
srcUrl = response.json()['videoInfo']['videos']['srcUrl']
```
## 6.替换伪装的视频链接中的`systemTime`参数,获取真正的视频链接
```python
srcUrl = str(srcUrl).replace(str(systemTime), f'cont-{contId}')
```
## 7.将视频内容保存到指定目录下,以视频标题命名
```python
# 发送请求获取视频内容
content = requests.get(url=srcUrl, headers=ajax_headers).content

# 将视频内容保存到指定目录下,以视频标题命名
with open(save_path + title, mode='wb') as fp:
    fp.write(content)
    fp.close()

# 打印提示信息
print(f'{title} 下载完毕!!!')
```
## 8.遍历用户选择的视频链接,将每个链接加入到并行下载任务
```python
# 遍历用户选择的视频链接,将每个链接加入到并行下载任务
pool.map(download_video, selected_indices)

# 等待所有任务完成
pool.close()
pool.join()
```
# 程序完整的代码
```python
# 1.导包 和 设置视频存储路径
import requests# 用于发送HTTP请求
from lxml import etree# 用于解析HTML
import random# 用于生成随机数
import os# 用于文件操作
from multiprocessing.dummy import Pool# 使用多线程池提高爬取效率

# 检查视频存储目录是否存在,如果不存在则创建
if not os.path.exists('./梨视频'):
    os.mkdir('./梨视频')
save_path = './梨视频/'

# 2.指定主页url,设置请求头信息
# URL列表
host_url_list = ['https://www.pearvideo.com/category_1', 'https://www.pearvideo.com/panorama']
# 设置host_headers请求头
host_headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63"
}
# 获取host网站标题信息
host_site_titles = []
for host_url in host_url_list:
    page_text = requests.get(host_url, headers=host_headers).text
    tree = etree.HTML(page_text)
    host_site_title = tree.xpath('//title/text()')
    host_site_titles.append(host_site_title)

# 用户选择URL
print("欢迎使用梨视频下载工具")
print("请选择要爬取的网站:")

for i, (url, title) in enumerate(zip(host_url_list, host_site_titles), 1):
    print(f"{i}. {title}")

while True:
    try:
      choice = int(input("请输入数字 1 或 2 来选择: "))
      if 1 <= choice <= 2:
            break
      else:
            print("请输入有效的选择 (1 或 2) ")
    except ValueError:
      print("请输入有效的选择 (1 或 2)")

selected_url = host_url_list
# 获取用户选择的URL
host_url = selected_url

# 发送GET请求获取主页的HTML内容
page_text = requests.get(url=host_url, headers=host_headers).text
tree = etree.HTML(page_text)
# 提取主页中的视频链接
inner_href_list = tree.xpath('//div[@class="vervideo-bd"]/a/@href')
# 输出链接对应的标题
print("\n以下是可供下载的视频标题:")
inner_url_list = []
for i, inner_href in enumerate(inner_href_list, 1):
    inner_url = 'https://www.pearvideo.com/' + inner_href
    inner_url_list.append(inner_url)
    inner_text = requests.get(url=inner_url, headers=host_headers).text
    title = etree.HTML(inner_text).xpath('//h1[@class="video-tt"]/text()')
    print(f"{i}. {title}")

# 用户选择要下载的视频
selected_indices = input("输入要下载的视频序号(用逗号分隔,如1,3,5),或者直接按回车下载所有视频:")
if selected_indices:
    selected_indices =
else:
    selected_indices = range(len(inner_href_list))

# 使用多线程池提高爬取效率
pool = Pool(6)


# 定义一个下载视频的函数
def download_video(index):
    inner_url = inner_url_list
    # 4.对详情页源代码进行分析,找出视频链接。
    #   但是通过分析,我们可以发现视频的详情页源代码中是不含有视频链接的,也就是说详情页中的视频 是动态加载出来的,这时候就需要我们去分析ajax文件了。
    #   通过抓包工具,我们可以看到Fetch/XHD中存在这样一个请求:https://www.pearvideo.com/videoStatus.jsp?contId=1156899&mrd=0.13727699405356097。但是打开这个链接,发现并不是我们想要的视频,而是这样的:
    # {
    # "resultCode":"5",
    # "resultMsg":"该文章已经下线!",
    # "systemTime": "1676187693726"
    # }
    #   其实这是网站使用的一种反爬机制,即——防盗链(Referer)
    #   通过抓包工具,我们可以看到该请求的Referer是:https://www.pearvideo.com/video_1156899 这个链接就是我们的inner_url

    # 5.找出ajax文件发出请求url,并找到参数 contId 和 mrd
    #   经常分析,我们可以看出参数 mrd 是一个随机的浮点数,这个浮点数范围是0-1之间

    # 获取详情页的HTML内容
    inner_text = requests.get(url=inner_url, headers=host_headers).text
    # 提取视频标题和contId
    title = etree.HTML(inner_text).xpath('//h1[@class="video-tt"]/text()') + '.mp4'
    contId = etree.HTML(inner_text).xpath('//div[@id="poster"]/@data-cid')

    # 生成一个随机的mrd参数
    mrd = random.random()

    # 构造用于获取视频真实链接的ajax请求url
    ajax_url = 'https://www.pearvideo.com/videoStatus.jsp?'
    params = {
      "contId": contId,
      "mrd": mrd
    }

    # 6.设置ajax请求的头部,包括Referer
    ajax_headers = {
      "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63",
      "Referer": inner_url
    }

    # 发送ajax请求,获取视频信息的JSON数据
    response = requests.get(url=ajax_url, headers=ajax_headers, params=params)

    # 提取JSON中的systemTime和srcUrl
    systemTime = response.json()['systemTime']
    srcUrl = response.json()['videoInfo']['videos']['srcUrl']

    # print(srcUrl)

    # 7.此时我们找到了json文件中包含的视频url,但是我们打开发现网页404报错,这就意味着这个链接其实并不是真正的视频链接;那么,我们开始对视频进行分析,
    # 发现视频的真正链接是:https://video.pearvideo.com/mp4/short/20170915/cont-1156899-10890623-hd.mp4
    # 通过对比分析,可以发现这两个链接只有一处不同的地方(systemTime),那么接下来我们就要把伪装的链接通过字符串替换,拿到真正的视频链接

    # 8.替换伪装的视频链接中的systemTime参数,获取真正的视频链接
    srcUrl = str(srcUrl).replace(str(systemTime), f'cont-{contId}')

    # 发送请求获取视频内容
    content = requests.get(url=srcUrl, headers=ajax_headers).content

    # 9.将视频内容保存到指定目录下,以视频标题命名
    with open(save_path + title, mode='wb') as fp:
      fp.write(content)
      fp.close()

    # 打印提示信息
    print(f'{title} 下载完毕!!!')


# 遍历用户选择的视频链接,将每个链接加入到并行下载任务
pool.map(download_video, selected_indices)

# 等待所有任务完成
pool.close()
pool.join()

```

马到功成 发表于 2023-10-18 09:30

俺也想学如何分析提取 下载网页视频,但不知道这方面的知识是从何开始学,需要学哪些方面的类容。求大佬指点下门路

逐雅斋 发表于 2023-10-18 07:25

感谢楼主分享,学习了!

yjn866y 发表于 2023-10-18 08:23

学习学习。。。。

baliao 发表于 2023-10-18 08:25

感谢楼主分享,学习了!

lianliangfo 发表于 2023-10-18 08:37

感谢分享,学习了!

lgblgc 发表于 2023-10-18 09:37

感谢楼主分享!!

ddvvc 发表于 2023-10-18 09:44

感谢楼主干货分享

少年郎向前望 发表于 2023-10-18 10:08

感谢楼主分享 多谢了

yaphoo 发表于 2023-10-18 11:25

谢谢分享!
页: [1] 2 3
查看完整版本: 梨视频网站视频爬取-初探索