梨视频高级爬取

April 3, 2020

2363 10 minutes and 44 seconds
梨视频高级爬取

[DeadPool爬虫]梨视频高级爬取

通过之前的梨视频初级爬取小实战,我们已经简单体会了Python写爬虫的威力,但是有没有感受到局限性?通过查看梨视频的网站,我们可以看到每个页面只加载12个短视频,因此每次爬取的视频内容量是非常不够的,所以我们要更进一步,是不是可以通过代码来实现控制页面加载更多呢?

PS: 如果您已经有爬虫相关知识了,可以直接看我的框架地址Deadpool项目

还是一样,话不多说,直接上最终代码效果~

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time    : 2020/4/3 14:16
@Author  : WangHuan
@Contact : hi_chengzi@126.com
@File    : 2020-04-03.py
@Software: PyCharm
@description: webdriver实现页面加载新数据后抓取信息并保存
"""

import os
import re
import time
import urllib.request
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By

def down_top10_techvideo():
    storage = 'videos'
    if storage not in os.listdir():
        os.mkdir(storage)

    seed_url = 'https://www.pearvideo.com/popular_8'
    prefix_url = 'https://www.pearvideo.com/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
    }

    # 获取前十名视频的作者
    content = requests.get(seed_url, headers=headers).text
    author_regex = r'<a href="(.*?)" class="column">'
    author_ids = re.findall(author_regex, content)
    author_url = []
    for _ in author_ids:
        _url = prefix_url + _
        author_url.append(_url)

    videos = []
    driver = webdriver.Chrome(executable_path="E:\\Github\\Lesson\\chromedriver.exe")

    # 进入作者页面,获取作者页面下“最新”中的所有视频
    for _ in author_url:
        print('-' * 60)
        print(f'作者地址:{_}')

        # 访问作者页面
        driver.get(_)
        driver.implicitly_wait(10)
        temp_pagesource = driver.page_source

        # 匹配视频链接
        vids_regex = r'<a href="(.*?)" class="vervideo-lilink actplay" target="_blank">'
        vids = re.findall(vids_regex, temp_pagesource)
        # 剔除前六个热门视频
        # vids = vids[6:]
        for i in range(0, 6):
            vids.pop(0)
        for _ in vids:
            _url = prefix_url + _
            videos.append(_url)

        i = 1
        # 实现一直点击“加载更多”直到没有加载出新的内容
        while videos:

            print('当前队列里面有视频数量为:{0}'.format(len(videos)))

            # 访问videos中的视频地址,保存对应视频文件和相关描述信息,并将已保存过的视频链接从videos中去除
            videos.reverse()
            while videos:
                video = videos.pop()
                print(video)
                video_content = requests.get(video, headers=headers).text
                mp4_regex = r'ldUrl="",srcUrl="(.*?)",vdoUrl='
                video_url = re.findall(mp4_regex, video_content)
                title_regex = r'<h1 class="video-tt">(.*?)</h1>'
                video_title = re.findall(title_regex, video_content)

                md5 = hashlib.md5()
                md5.update(video_title[0].encode(encoding='utf-8'))
                md5_title = md5.hexdigest()

                print(f'正在下载视频:{video_title[0]}')
                urllib.request.urlretrieve(video_url[0], os.path.join(storage, f"{md5_title}.mp4"))

                sum_regex = r'<div class="summary">(.*?)</div>'
                video_sum = re.findall(sum_regex, video_content)
                with open(f"videos/{md5_title}.txt", 'w', encoding='utf-8') as f:
                    f.write(video_title[0])
                    f.write(video_sum[0])

            # 在作者页面点击“加载更多”,获取更多视频信息
            driver.find_element(By.ID, "listLoadMore").click()
            print(f'点击“加载更多” {i} 次')
            i += 1
            time.sleep(3)
            # 重新获取页面内容
            new_pagesource = driver.page_source
            # 获取新的vids
            new_vids = re.findall(vids_regex, new_pagesource)
            old_vids = re.findall(vids_regex, temp_pagesource)
            vids = (vid for vid in new_vids if vid not in old_vids)

            # 当没有新vids时,跳出循环
            if vids:
                temp_pagesource = new_pagesource
                for v in vids:
                    _url = prefix_url + v
                    videos.append(_url)
            else:
                break


if __name__ == '__main__':
    down_top10_techvideo()

虽然这次的代码里面混杂了一些还没有涉及的内容,但是先为了咱们的目标"不择手段",以实现目标为最终目标,后续会介绍selenium的相关知识~

接下来还是一样的,代码解读时间,不过这次我们只讲解一些有差别的地方

===

这段代码是获取视频排行榜页面中的排名前十的作者地址

# 获取前十名视频的作者
content = requests.get(seed_url, headers=headers).text
author_regex = r'<a href="(.*?)" class="column">'
author_ids = re.findall(author_regex, content)
author_url = []
for _ in author_ids:
    _url = prefix_url + _
    author_url.append(_url)

===

这段代码是启动一个Chromedriver来打开浏览器进行页面访问

driver = webdriver.Chrome(executable_path="E:\\Github\\Lesson\\chromedriver.exe")

===

这段代码同上一个课程里面的最后的实战练习一样,将视频以MD5.mp4的形式进行保存,同时用一个MD5.txt的文件记录视频的相关信息

md5 = hashlib.md5()
md5.update(video_title[0].encode(encoding='utf-8'))
md5_title = md5.hexdigest()

print(f'正在下载视频:{video_title[0]}')
urllib.request.urlretrieve(video_url[0], os.path.join(storage, f"{md5_title}.mp4"))

sum_regex = r'<div class="summary">(.*?)</div>'
video_sum = re.findall(sum_regex, video_content)
with open(f"videos/{md5_title}.txt", 'w', encoding='utf-8') as f:
    f.write(video_title[0])
    f.write(video_sum[0])

===

因为我们是通过chrome浏览器打开的页面,所以我们可以通过chrome来触发点击动作

# 在作者页面点击“加载更多”,获取更多视频信息
driver.find_element(By.ID, "listLoadMore").click()
print(f'点击“加载更多” {i} 次')
i += 1
time.sleep(3)

===

这段代码是我们进行加载前后的两次内容对比,通过找到的链接列表来进行比对,找到新增的视频链接

# 重新获取页面内容
new_pagesource = driver.page_source
# 获取新的vids
new_vids = re.findall(vids_regex, new_pagesource)
old_vids = re.findall(vids_regex, temp_pagesource)
vids = (vid for vid in new_vids if vid not in old_vids)

===

最后当我们判断没有新增链接之后,就可以得知,该视频作者的所有短视频都爬取完毕了

# 当没有新vids时,跳出循环
if vids:
    temp_pagesource = new_pagesource
    for v in vids:
        _url = prefix_url + v
        videos.append(_url)
else:
    break

代码链接来自我女朋友的Github-2020-04-07

实战练习

通过上面的更高级一点的代码爬取的实战例子,我们再拔高一点?正好通过拔高例子的过程,再次巩固我们的代码实践能力~

目标是: 通过一定的构建逻辑,将上面的实践代码例子,重构成一个可以复用的类对象

这里的重构代码由我亲自操刀

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time    : 2020/4/7 10:26
@Author  : WangHuan
@Contact : hi_chengzi@126.com
@File    : 2020-04-07.py
@Software: PyCharm
@description: 在梨视频科技排行榜中获取前十名视频的对应作者的全部视频内容
"""

import re
import os
import time
import requests
import hashlib
import urllib.request
from selenium import webdriver
from selenium.webdriver.common.by import By



class PearVideoSpider(object):
    """ 爬取梨视频分类中排行榜前十名视频作者的所有视频内容 """

    def __init__(self, seed):
        self.seed = seed
        self.prefix = "https://www.pearvideo.com/"
        self.temp_pagesource = ""
        self.finish_video_urls = set()
        self.storage = 'videos'
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
        }
        self.author_urls = set()

        self._setup_dirver()

    def _setup_dirver(self):
        """ 加载谷歌浏览器内核并进行配置 """
        self.driver = webdriver.Chrome(executable_path="E:\\Github\\Lesson\\chromedriver.exe")

    def _click_more(self, elementid):
        """ 加载更多 """
        self.driver.find_element(By.ID, elementid).click()
        time.sleep(3)

    def setup_storage(self):
        """ 创建视频存储路径 """
        if self.storage not in os.listdir():
            os.mkdir(self.storage)

    def get_top10_creator(self, regex):
        """ 获取前十名视频的作者 """
        content = requests.get(self.seed, headers=self.headers).text
        author_ids = re.findall(regex, content)
        for _ in author_ids:
            _url = self.prefix + _
            self.author_urls.add(_url)

    def find_video_urls(self, pagesource):
        """ 查找页面里面视频的链接地址 """
        # 匹配视频链接
        videos = []
        vids_regex = r'<a href="(.*?)" class="vervideo-lilink actplay" target="_blank">'
        vids = re.findall(vids_regex, pagesource)
        for _ in vids:
            _url = self.prefix + _
            videos.append(_url)
        videos.reverse()
        return videos

    def download_video(self, item):
        """ 下载每一个视频并进行存储 """
        video_content = requests.get(item, headers=self.headers).text
        mp4_regex = r'ldUrl="",srcUrl="(.*?)",vdoUrl='
        video_url = re.findall(mp4_regex, video_content)
        title_regex = r'<h1 class="video-tt">(.*?)</h1>'
        video_title = re.findall(title_regex, video_content)

        md5 = hashlib.md5()
        md5.update(video_title[0].encode(encoding='utf-8'))
        md5_title = md5.hexdigest()

        print(f'正在下载视频:{video_title[0]}')
        urllib.request.urlretrieve(video_url[0], os.path.join(self.storage, f"{md5_title}.mp4"))

        sum_regex = r'<div class="summary">(.*?)</div>'
        video_sum = re.findall(sum_regex, video_content)
        with open(f"{self.storage}/{md5_title}.txt", 'w', encoding='utf-8') as f:
            f.write(video_title[0])
            f.write(video_sum[0])

    def run(self):
        self.setup_storage()
        self.get_top10_creator(regex=r'<a href="(.*?)" class="column">')

        # 进入作者页面,获取作者页面下“最新”中的所有视频
        print(f'当前类别视频排名前十的视频作者有{len(self.author_urls)}个')
        for _ in self.author_urls:
            print('-' * 60)
            print(f'作者地址:{_}')
            # 访问作者页面
            self.driver.get(_)
            self.driver.implicitly_wait(10)
            self.temp_pagesource = self.driver.page_source

            # 匹配视频链接
            videos = self.find_video_urls(pagesource=self.temp_pagesource)
            videos = videos[:-6]

            i = 1
            # 实现一直点击“加载更多”直到没有加载出新的内容
            while videos:
                print(f'当前队列里面有视频数量为: {len(videos)}')
                # 访问videos中的视频地址,保存对应视频文件和相关描述信息,并将已保存过的视频链接从videos中去除
                while videos:
                    video = videos.pop()
                    print(f'当前爬取的视频地址: {video}')
                    self.download_video(video)

                # 在作者页面点击“加载更多”,获取更多视频信息
                print(f'点击“加载更多” {i} 次')
                self._click_more("listLoadMore")
                i += 1

                # 重新获取页面内容
                new_pagesource = self.driver.page_source
                # 获取新的vids
                new_vids = self.find_video_urls(new_pagesource)
                old_vids = self.find_video_urls(self.temp_pagesource)
                vids = (vid for vid in new_vids if vid not in old_vids)

                # 当没有新vids时,跳出循环
                if vids:
                    temp_pagesource = new_pagesource
                    videos = vids
                else:
                    break