python爬取琳琅社区整站视频(一晚6000部)[通俗易懂]

python爬取琳琅社区整站视频(一晚6000部)[通俗易懂]琳琅社区(传闻中最受男人喜爱的网站),哼哼,我倒要看看是不是真的该项目用于爬取琳琅社区整站视频(仅供学习)主要使用:python3.7+scrapy2.19+Mysql8.0+win10首先确定需要爬取的内容,定义item:classLinglangItem(scrapy.Item): #视频属于哪个模块video_belong_module=scrap…

大家好,又见面了,我是你们的朋友全栈君。

琳琅社区(传闻中最受男人喜爱的网站),哼哼,我倒要看看是不是真的

该项目用于爬取琳琅社区整站视频(仅供学习)

主要使用:python3.7 + scrapy2.19 + Mysql 8.0 + win10

  • 首先确定需要爬取的内容,定义item
class LinglangItem(scrapy.Item):
	#视频属于哪个模块
    video_belong_module = scrapy.Field()
    #视频播放页面url
    video_url = scrapy.Field()
    #视频标题
    video_title = scrapy.Field()
    #视频真实m3u8地址
    video_m3u8_url =  scrapy.Field()
  • 然后编写爬虫文件
    构造初始url的解析函数,得到琳琅网站的视频分类请求,并在本地生成存储的主目录
def parse(self, response):
    # 创建主目录
    if not os.path.exists(self.base_dir):
        os.mkdir(self.base_dir)
    all_module_url = response.xpath('//div[@id="head_nav"]/div/div[@class="left_nav"]/a/@href').extract()[1:]
    #得到所有模块(最新,动漫。。。)的绝对url
    all_module_url = [self.start_urls[0] + url for url in all_module_url]
    # 构造所有模块页的请求
    for page_url in all_module_url:
        # 引擎判断该数据为一个请求,给调度器,
        # 如果是其他格式比如列表,引擎不能识别,只能通过我们的命令-o处理
        yield scrapy.Request(page_url, callback=self.page_parse)

定义具体模块页面的解析函数,支持分页爬取:

def page_parse(self,response):

    # 得到该页面所有视频的url,title,视频m3u8地址 (20个)
    video_urls = response.xpath('//ul[contains(@class,"piclist")]/li/a/@href').extract()
    video_titles =  response.xpath('//ul[contains(@class,"piclist")]/li/a/@title').extract()
   
    video_m3u8_url_ls = response.xpath('//ul[contains(@class,"piclist")]/li/a/@style').extract()
    # 该视频所在模块
    video_belong_module =  response.xpath('//a[contains(@class,"on")]/text()').extract_first()
    for index,video_m3u8_url in enumerate(video_m3u8_url_ls):
        # 最好yield一个item就重新创建一个,否则可能导致一些问题,比如名字重复
        item = dict()
        ls = video_m3u8_url.split('/')
        #https://bbb.188370aa.com/20191014/WLDsLTZK/index.m3u8
        #0 1 2 3 4 5
        # 得到绝对m3u8_url
        try:
            m3u8_url = self.m3u8_domain + ls[3] + '/' +ls[4] + '/index.m3u8'
        except:
            continue
        item['video_belong_module'] = video_belong_module
        item['video_url'] = self.start_urls[0] + video_urls[index]

        #教训:有些名字后面带空格,删的时候找不到文件
        # item['video_title'] = video_titles[index].strip()
        item['video_title'] = video_titles[index].strip().replace('.','')
        # item['video_m3u8_url'] = m3u8_url
        self.num += 1
        print('当前是第 %s 个视频: %s' % (self.num, item['video_title']))
        #创建每个视频目录
        module_name = video_belong_module
        file_name = item['video_title']
        # module_path = os.path.join(self.base_dir, module_name)
        # video_path = os.path.join(module_path, file_name)
        module_path = self.base_dir + module_name + '/'
        video_path = module_path + file_name +'/'
        if not os.path.exists(video_path):
            try:
                os.makedirs(video_path)
            except:
                video_path = module_path + str(random()) + '/'
                os.makedirs(video_path)

        yield scrapy.Request(m3u8_url, callback=self.m3u8_parse, meta={ 
   'video_path':video_path,'item':item})
    try:
        # 得到下一页的a标签selector对象
        next_page_selector = response.xpath('//div[@class="pages"]/a')[-2]
        # 如果有下一页则向下一页发起请求,尾页的下一页a标签没有href属性
        next_page = next_page_selector.xpath('./@href').extract_first()
        if  next_page:
            next_page_url = self.start_urls[0] + next_page
            yield scrapy.Request(next_page_url, callback=self.page_parse)
    except:
        pass

返回item给管道文件:

ef m3u8_parse(self,response):
        item = LinglangItem()
        for k,v in response.meta['item'].items():
            item[k] = v
        # response.text得到m3u8文件内容字符串
        # 得到最新的m3u8文件url
        real_url = re.findall(r'https:.*?m3u8', response.text)[-1]
        item['video_m3u8_url'] = real_url
        # yield返回给引擎的时候会判断 item 的数据类型是不是item类型如果是则返回给piplines
        yield item

实现一个去重管道:

#实现去重Item Pipeline 过滤重复数据
class DuplicatesPipline(object):
    #只在第一个item来时执行一次,可选实现,做参数初始化等
    def __init__(self):
        self.video_title_set = set()
    def process_item(self,item,spider):
        video_title = item['video_title']
        if video_title in self.video_title_set:
            item['video_title'] = item['video_title'] + str(random())
        self.video_title_set.add(video_title)
        #表示告诉引擎,我这个item处理完了,可以给我下一个item
        return item
    #然后去settings中启用DuplicatesPipline

再实现将数据存入mysql的存储管道,此处也可选择其他种类数据库进行存储:

#将item数据存入数据库
class MySqlPipeline(object):
    def __init__(self,database):
        self.database = database

    # 该方法可以在settings里面拿到一些配置信息
    @classmethod
    def from_crawler(cls, crawler):
        # 相当于返回一个MySqlPipeline对象
        return cls(
            # 得到settings里面的对应配置信息并返回,当作init的参数
            database=crawler.settings.get('DATABASE')
        )

    #当spider被开启时,这个方法被调用, 连接数据库
    def open_spider(self, spider):
        self.db = pymysql.connect(host='localhost',port=3306,user='root',password='123456',database=self.database, charset='utf8')
        self.cursor = self.db.cursor()
        print('数据库:',type(self.db),type(self.cursor))


    def process_item(self,item,spider):

        sql = "insert into video_info values(%s,%s,%s,%s);"
        values = tuple(dict(item).values())
        #执行成功返回1
        self.cursor.execute(sql,values)
        # 前面只是把数据写到缓存里,执行commit命令写到数据库中
        self.db.commit()
        return item
        # 然后去settings中启用MySqlPipeline,这里暂时不启用


    # 当spider被关闭时,这个方法被调用,关闭数据库
    def close_spider(self, spider):
        self.cursor.close()
        self.db.close()

其实呢,到这已经能够进行爬取了。但是我们利用scrapy对该网站频繁发起这么多次请求,对方服务器判定我们为爬虫时,会强行关闭与我们之间的连接。

虽然scrapy会将这些没有爬取成功的请求重新放回调度器,等待之后连接成功再发送请求,但是这样会浪费我们一些时间。

为了提高效率,当本地请求失败后,我们可以在下载中间件中使用动态代理重新发起请求:

  def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        # 如果返回的response状态不是200,重新生成当前request对象
        if response.status != 200:
            print('使用代理-------------------------')
            headers = { 
   
                'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'
            }
            # 对当前request加上代理
            request.headers = headers
            request.meta['proxy'] = 'http://' + self.random_ip()
            return request
        return response

最后启动爬虫,等待爬虫结束,查看数据库,满满的收获~
可以看出该网站共有5997条视频,感觉没有想象的那么多啊可以看出该网站共有5997条视频,感觉没有想象的那么多啊,网站url不敢贴出来,小怕怕,哈哈。

实践出真知,这种做着有精神的网站更是练手的好目标,就是身体一天不如一天,可能是熬夜吧。。。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/149048.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • PyCharm配置_pycharm安装配置

    PyCharm配置_pycharm安装配置pycharmpycharm是一个比较好的pythonIDE,可以在MACOS和windows上使用,补全功能强大,而且界面十分友好,特别适合python编程人员使用。pycharmPycharm安装Pycharm配置修改成灰底主题显示行号修改字体大小编程字体我推荐运行调试Pycharm安装pycharm的安装地址:http://www.jetbrains.com/

  • 数字证书原理,公钥私钥加密原理 – 因为这个太重要了[通俗易懂]

    文中首先解释了加密解密的一些基础知识和概念,然后通过一个加密通信过程的例子说明了加密算法的作用,以及数字证书的出现所起的作用。接着对数字证书做一个详细的解释,并讨论一下windows中数字证书的管理,最后演示使用makecert生成数字证书。如果发现文中有错误的地方,或者有什么地方说得不够清楚,欢迎指出!1、基础知识这部分内容主要解释一些概念和术语,最好是先理解这…

  • 软件著作权说明书模板_软件设计方案怎么写

    软件著作权说明书模板_软件设计方案怎么写1.引言1.1编写目的1.2项目背景1.2项目概要总体要求2.1系统功能概述2.2系统功能要求软件开发3.1软件需求分析3.2软件的概要设计3.2.1软件概要设计说明3.2.3基本设计概念和处理流程3.3软件的详细设计3.3.1系统结构3.3.2模块设计说明3.3.3爬虫模块3.3.4日志模块3.3.5数…

  • 究竟什么是Java虚拟机(JVM)?

    究竟什么是Java虚拟机(JVM)?我们都知道,在Windows上,软件包后缀有exe,而苹果的MacOSX系统上没有安装exe。类似地,MacOSX系统上的软件安装包是dmg后缀,不能安装在Windows系统上。为什么不能安装不同系统上的软件,因为操作系统的底层实现是不同的。对于Windows系统,exe后缀的软件代码被编译成能被Windows系统识别的机器代码。对于MacOSX系统,最后将DMG后缀的软件代码编译为M…

  • uo什么意思_2018五大学科集训队

    uo什么意思_2018五大学科集训队uoj#418. 【集训队作业2018】三角形(线段树合并)

  • docker复制文件到宿主机_下面哪几个属于docker网络模式

    docker复制文件到宿主机_下面哪几个属于docker网络模式自从Docker容器出现以来,容器的网络通信就一直是被关注的焦点,也是生产环境的迫切需求。容器的网络通信又可以分为两大方面:单主机容器上的相互通信,和跨主机的容器相互通信。 一、端口映射(局域网,外网此方式均可)。此种方式是将容器的某个端口映射到宿主机的某个端口,其它主机访问容器提供的服务需要通过宿主机的IP进行访问:dockerrun-p9000:8000–namec…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号