以后再也不愁看什么电影了

作者:猴哥Yuri

博客:https://www.jianshu.com/u/f46becd1ed83

文章目录

  • 爬取目标

  • 设计爬虫程序

    • 确定爬取入口

    • 爬取思路

    • 设计爬虫架构

    • 代码实现

  • 爬取结果


前面两篇文章介绍 requests 和 xpath 的用法。我们推崇学以致用,所以本文讲解利用这两个工具进行实战。

0

爬取目标

本次爬取的站点选择电影天堂,网址是: www.dytt8.net。爬取内容是整个站点的所有电影信息,包括电影名称,导演、主演、下载地址等。具体抓取信息如下图所示:


1

设计爬虫程序

2.1 确定爬取入口

电影天堂里面的电影数目成千上万,电影类型也是让人眼花缭乱。我们为了保证爬取的电影信息不重复, 所以要确定一个爬取方向。目前这情况真让人无从下手。但是,我们点击主页中的【最新电影】选项,跳进一个新的页面。蓦然有种柳暗花明又一村的感觉。


由图可知道,电影天堂有 5 个电影栏目,分别为最新电影、日韩电影、欧美电影、国内电影、综合电影。每个栏目又有一定数量的分页,每个分页有 25 条电影信息。那么程序的入口可以有 5 个 url 地址。这 5 个地址分别对应每个栏目的首页链接。

2.2 爬取思路

知道爬取入口,后面的工作就容易多了。我通过测试发现这几个栏目除了页面的 url 地址不一样之外,其他例如提取信息的 xpath 路径是一样的。因此,我把 5 个栏目当做 1  个类,再该类进行遍历爬取。

我这里“最新电影”为例说明爬取思路。
1)请求栏目的首页来获取到分页的总数,以及推测出每个分页的 url 地址;
2)将获取到的分页 url 存放到名为 floorQueue 队列中;
3)从 floorQueue 中依次取出分页 url,然后利用多线程发起请求;
4)将获取到的电影页面 url 存入到名为 middleQueue 的队列;
5)从 middleQueue 中依次取出电影页面 url,再利用多线程发起请求;
6)将请求结果使用 Xpath 解析并提取所需的电影信息;
7)将爬取到的电影信息存到名为 contentQueue 队列中;
8)从 contentQueue 队列中依次取出电影信息,然后存到数据库中。

2.3 设计爬虫架构

根据爬取思路,我设计出爬虫架构。如下图所示:


2.4 代码实现

主要阐述几个重要的类的代码

  • main 类

主要工作两个:第一,实例化出一个dytt8Moive对象,然后开始爬取信息。第二,等爬取结束,将数据插入到数据库中。

处理爬虫的逻辑代码如下:

# 截止到2017-08-08, 最新电影一共才有 164 个页面LASTEST_MOIVE_TOTAL_SUM = 6 #164

# 请求网络线程总数, 线程不要调太好, 不然会返回很多 400THREAD_SUM = 5

def startSpider():   # 实例化对象

   # 获取【最新电影】有多少个页面   LASTEST_MOIVE_TOTAL_SUM = dytt_Lastest.getMaxsize()   print('【最新电影】一共  ' + str(LASTEST_MOIVE_TOTAL_SUM) + '  有个页面')   dyttlastest = dytt_Lastest(LASTEST_MOIVE_TOTAL_SUM)   floorlist = dyttlastest.getPageUrlList()

   floorQueue = TaskQueue.getFloorQueue()   for item in floorlist:       floorQueue.put(item, 3)

   # print(floorQueue.qsize())

   for i in range(THREAD_SUM):       workthread = FloorWorkThread(floorQueue, i)       workthread.start()

   while True:       if TaskQueue.isFloorQueueEmpty():           break       else:           pass

   for i in range(THREAD_SUM):       workthread = TopWorkThread(TaskQueue.getMiddleQueue(), i)       workthread.start()

   while True:       if TaskQueue.isMiddleQueueEmpty():           break       else:           pass

   insertData()

if __name__ == '__main__':   startSpider()

创建数据库以及表,接着再把电影信息插入到数据库的代码如下:

def insertData():   DBName = 'dytt.db'   db = sqlite3.connect('./' + DBName, 10)   conn = db.cursor()

   SelectSql = 'Select * from sqlite_master where type = "table" and name="lastest_moive";'   CreateTableSql = '''       Create Table lastest_moive (           'm_id' INTEGER PRIMARY KEY,           'm_type' varchar(100),           'm_trans_name' varchar(200),           'm_name' varchar(100),           'm_decade' varchar(30),           'm_conutry' varchar(30),           'm_level' varchar(100),           'm_language' varchar(30),           'm_subtitles' varchar(100),           'm_publish' varchar(30),           'm_IMDB_socre' varchar(50),           'm_douban_score' varchar(50),           'm_format' varchar(20),           'm_resolution' varchar(20),           'm_size' varchar(10),           'm_duration' varchar(10),           'm_director' varchar(50),           'm_actors' varchar(1000),           'm_placard' varchar(200),           'm_screenshot' varchar(200),           'm_ftpurl' varchar(200),           'm_dytt8_url' varchar(200)       );   '''

   InsertSql = '''       Insert into lastest_moive(m_type, m_trans_name, m_name, m_decade, m_conutry, m_level, m_language, m_subtitles, m_publish, m_IMDB_socre,        m_douban_score, m_format, m_resolution, m_size, m_duration, m_director, m_actors, m_placard, m_screenshot, m_ftpurl,       m_dytt8_url)       values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);   '''

   if not conn.execute(SelectSql).fetchone():       conn.execute(CreateTableSql)       db.commit()       print('====  创建表成功  ====')   else:       print('====  创建表失败, 表已经存在  ====')

   count = 1

   while not TaskQueue.isContentQueueEmpty():       item = TaskQueue.getContentQueue().get()       conn.execute(InsertSql, Utils.dirToList(item))       db.commit()       print('插入第 ' + str(count) + ' 条数据成功')       count = count + 1

   db.commit()   db.close()

  • TaskQueue 类

维护 floorQueue、middleQueue、contentQueue 三个队列的管理类。之所以选择队列的数据结构,是因为爬虫程序需要用到多线程,队列能够保证线程安全。

  • dytt8Moive 类

dytt8Moive 类是本程序的主心骨。程序最初的爬取目标是 5 个电影栏目,但是目前只现实了爬取最新栏目。如果你想爬取全部栏目电影,只需对 dytt8Moive 稍微改造下即可。

class dytt_Lastest(object):

   # 获取爬虫程序抓取入口   breakoutUrl = 'http://www.dytt8.net/html/gndy/dyzz/index.html'

   def __init__(self, sum):       self.sum = sum

   # 获取【最新电影】有多少个页面   # 截止到2017-08-08, 最新电影一共才有 164 个页面   @classmethod   def getMaxsize(cls):       response = requests.get(cls.breakoutUrl, headers=RequestModel.getHeaders(), proxies=RequestModel.getProxies(), timeout=3)       # 需将电影天堂的页面的编码改为 GBK, 不然会出现乱码的情况       response.encoding = 'GBK'

       selector = etree.HTML(response.text)       # 提取信息       optionList = selector.xpath("//select[@name='sldd']/text()")       return len(optionList) - 1   # 因首页重复, 所以要减1

   def getPageUrlList(self):       '''       主要功能:目录页url取出,比如:http://www.dytt8.net/html/gndy/dyzz/list_23_'+ str(i) + '.html       '''       templist = []       request_url_prefix = 'http://www.dytt8.net/html/gndy/dyzz/'       templist = [request_url_prefix + 'index.html']

       for i in range(2, self.sum + 1):           templist.append(request_url_prefix + 'list_23_' + str(i) + '.html')

       for t in templist:           print('request url is ###   ' + t + '    ###')       return templist

   @classmethod   def getMoivePageUrlList(cls, html):       '''       获取电影信息的网页链接       '''       selector = etree.HTML(html)       templist = selector.xpath("//div[@class='co_content8']/ul/td/table/tr/td/b/a/@href")       # print(len(templist))       # print(templist)       return templist

   @classmethod   def getMoiveInforms(cls, url, html):       '''       解析电影信息页面的内容, 具体如下:       类型        : 疾速特攻/疾速追杀2][BD-mkv.720p.中英双字][2017年高分惊悚动作]       ◎译名      : ◎译\u3000\u3000名\u3000疾速特攻/杀神John Wick 2(港)/捍卫任务2(台)/疾速追杀2/极速追杀:第二章/约翰·威克2       ◎片名      : ◎片\u3000\u3000名\u3000John Wick: Chapter Two       ◎年代     : ◎年\u3000\u3000代\u30002017       ◎国家     : ◎产\u3000\u3000地\u3000美国       ◎类别     : ◎类\u3000\u3000别\u3000动作/犯罪/惊悚       ◎语言     : ◎语\u3000\u3000言\u3000英语       ◎字幕     : ◎字\u3000\u3000幕\u3000中英双字幕       ◎上映日期  :◎上映日期\u30002017-02-10(美国)       ◎IMDb评分  : ◎IMDb评分\xa0 8.1/10 from 86,240 users       ◎豆瓣评分  : ◎豆瓣评分\u30007.7/10 from 2,915 users       ◎文件格式   : ◎文件格式\u3000x264 + aac       ◎视频尺寸  : ◎视频尺寸\u30001280 x 720       ◎文件大小  : ◎文件大小\u30001CD       ◎片长     : ◎片\u3000\u3000长\u3000122分钟       ◎导演     : ◎导\u3000\u3000演\u3000查德·史塔赫斯基 Chad Stahelski       ◎主演     :       ◎简介      : 暂不要该字段       ◎获奖情况   : 暂不要该字段       ◎海报       影片截图       下载地址       '''       # print(html)       contentDir = {           'type': '',           'trans_name': '',           'name': '',           'decade': '',           'conutry': '',           'level': '',           'language': '',           'subtitles': '',           'publish': '',           'IMDB_socre': '',           'douban_score': '',           'format': '',           'resolution': '',           'size': '',           'duration': '',           'director': '',           'actors': '',           'placard': '',           'screenshot': '',           'ftpurl': '',           'dytt8_url': ''       }

       selector = etree.HTML(html)       content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/td/p/text()")       # 匹配出来有两张图片, 第一张是海报, 第二张是电影画面截图       imgs = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/td/p/img/@src")       # print(content)

       # 为了兼容 2012 年前的页面       if not len(content):           content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/span/text()")

       # 有些页面特殊, 需要用以下表达式来重新获取信息       # 电影天堂页面好混乱啊~       if not len(content):           content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/td/div/text()")

       if not len(content):           content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/p/font/text()")           if len(content) < 5:               content = selector.xpath("//div[@class='co_content8']/ul/tr/td/p/font/text()")

       if not len(content):           content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/p/span/text()")

       if not len(content):           content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/div/span/text()")

       if not len(content):           content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/font/text()")

       if not len(content):           content = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/p/text()")

       # print(content)

       # 不同渲染页面要采取不同的抓取方式抓取图片       if not len(imgs):           imgs = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/img/@src")

       if not len(imgs):           imgs = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/p/img/@src")

       if not len(imgs):           imgs = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/div/img/@src")

       if not len(imgs):           imgs = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/td/div/img/@src")

       # 类型       if content[0][0:1] != '◎':           contentDir['type'] = '[' + content[0]       actor = ''

       for each in content:           if each[0:5] == '◎译\u3000\u3000名':               # 译名 ◎译\u3000\u3000名\u3000  一共占居6位               contentDir['trans_name'] = each[6: len(each)]           elif each[0:5] == '◎片\u3000\u3000名':               # 片名               contentDir['name'] = each[6: len(each)]           elif each[0:5] == '◎年\u3000\u3000代':               # 年份               contentDir['decade'] = each[6: len(each)]           elif each[0:5] == '◎产\u3000\u3000地':               # 产地               contentDir['conutry'] = each[6: len(each)]           elif each[0:5] == '◎类\u3000\u3000别':               # 类别               contentDir['level'] = each[6: len(each)]           elif each[0:5] == '◎语\u3000\u3000言':               # 语言               contentDir['language'] = each[6: len(each)]           elif each[0:5] == '◎字\u3000\u3000幕':               # 字幕               contentDir['subtitles'] = each[6: len(each)]           elif each[0:5] == '◎上映日期':               # 上映日期               contentDir['publish'] = each[6: len(each)]           elif each[0:7] == '◎IMDb评分':               # IMDb评分               contentDir['IMDB_socre'] = each[9: len(each)]           elif each[0:5] == '◎豆瓣评分':               # 豆瓣评分               contentDir['douban_score'] = each[6: len(each)]           elif each[0:5] == '◎文件格式':               # 文件格式               contentDir['format'] = each[6: len(each)]           elif each[0:5] == '◎视频尺寸':               # 视频尺寸               contentDir['resolution'] = each[6: len(each)]           elif each[0:5] == '◎文件大小':               # 文件大小               contentDir['size'] = each[6: len(each)]           elif each[0:5] == '◎片\u3000\u3000长':               # 片长               contentDir['duration'] = each[6: len(each)]           elif each[0:5] == '◎导\u3000\u3000演':               # 导演               contentDir['director'] = each[6: len(each)]           elif each[0:5] == '◎主\u3000\u3000演':               # 主演               actor = each[6: len(each)]

       for item in content:           if item[0: 4] == '\u3000\u3000\u3000\u3000':               actor = actor + '\n' + item[6: len(item)]

       # 主演       contentDir['actors'] = actor       # 海报       if imgs[0] != None:           contentDir['placard'] = imgs[0]       # 影片截图       if imgs[1] != None:           contentDir['screenshot'] = imgs[1]       # 下载地址       ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/td/table/tbody/tr/td/a/text()")

       # 为了兼容 2012 年前的页面       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/table/tbody/tr/td/font/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/table/tbody/tr/td/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/div/table/tbody/tr/td/font/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/td/div/table/tbody/tr/td/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/td/table/tbody/tr/td/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/p/span/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/div/div/table/tbody/tr/td/font/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/span/table/tbody/tr/td/font/a/text()")

       if not len(ftp):           ftp = selector.xpath("//div[@class='co_content8']/ul/tr/td/div/div/td/div/span/div/table/tbody/tr/td/font/a/text()")

       contentDir['ftpurl'] = ftp[0]       # 页面链接       contentDir['dytt8_url'] = url       print(contentDir)       return contentDir

getMoiveInforms 方法是主要负责解析电影信息节点并将其封装成字典。在代码中,你看到 Xpath 的路径表达式不止一条。因为电影天堂的电影详情页面的排版参差不齐,所以单单一条内容提取表达式、海报和影片截图表达式、下载地址表达式远远无法满足。

选择字典类型作为存储电影信息的数据结构,也是自己爬坑之后才决定的。这算是该站点另一个坑人的地方。电影详情页中有些内容节点是没有,例如类型、豆瓣评分,所以无法使用列表按顺序保存。

2

爬取结果

我这里展示自己爬取最新栏目中 4000 多条数据中前面部分数据。


附:源代码地址(https://link.jianshu.com/?t=https://github.com/monkey-soft/MoivesSpider)

分享朋友圈 也是另一种赞赏

The more we share, The more we have

 

欢迎加入数据君高效数据分析社区


加我私人微信进入大数据干货群:tongyuannow 

目前100000+人已关注加入我们

       

       

爬取电影天堂的最新电影相关推荐

  1. Python 爬取电影天堂top最新电影

    Python爬虫有他无可比拟的优势:语法简单,经常几十行代码就能轻松解决问题,相比于JAVA,C,PHP;第三方库丰富,Python强大而又丰富的第三方库使他几乎可以无所不能.今天我们就来用用Pyth ...

  2. java多线程-爬电影天堂上的电影下载地址

    获取网页html 刚开始做的时候,在网上搜了一下资料.然后找到了一个获取网页最简单的dome,如下. public static String getHtml(String urlstring) th ...

  3. python找电影资源_Python爬虫爬取BT之家找电影资源

    head.jpg 一.写在前面 最近看新闻说圣城家园(SCG)倒了,之前BT天堂倒了,暴风影音也不行了,可以说看个电影越来越费力,国内大厂如企鹅和爱奇艺最近也出现一些幺蛾子,虽然目前版权意识虽然越来越 ...

  4. beautifulsoup爬取网页中的表格_Python爬虫爬取BT之家找电影资源

    一.写在前面 最近看新闻说圣城家园(SCG)倒了,之前BT天堂倒了,暴风影音也不行了,可以说看个电影越来越费力,国内大厂如企鹅和爱奇艺最近也出现一些幺蛾子,虽然目前版权意识虽然越来越强,但是很多资源在 ...

  5. Python爬虫爬取BT之家找电影资源

    一.写在前面 圣城家园(SCG)倒了BT天堂倒了,暴风影音也不行了,可以说看个电影越来越费力,国内大厂如企鹅和爱奇艺最近也出现一些幺蛾子,虽然目前版权意识虽然越来越强,但是很多资源在这些主流视频网站上 ...

  6. python爬电影资源_Python爬虫爬取BT之家找电影资源

    一.写在前面 最近看新闻说圣城家园(SCG)倒了,之前BT天堂倒了,暴风影音也不行了,可以说看个电影越来越费力,国内大厂如企鹅和爱奇艺最近也出现一些幺蛾子,虽然目前版权意识虽然越来越强,但是很多资源在 ...

  7. Node.js 抓取电影天堂新上电影节目单及ftp链接

    代码地址如下: http://www.demodashi.com/demo/12368.html 1 概述 本实例主要使用Node.js去抓取电影的节目单,方便大家使用下载. 2 node packa ...

  8. python实战(一)Python爬取猫眼评分排行前100电影及简单数据分析可视化python实战(一)Python爬取猫眼排行前一百电影及简单数据分析可视化

    python实战(一)Python爬取猫眼排行前一百电影及简单数据分析可视化 一.抓取数据 需要的库 request库 响应http请求 json库 将文本保存成json形式 pyquery 类似JQ ...

  9. python爬猫眼电影影评,Python系列爬虫之爬取并简单分析猫眼电影影评

    前言 今天给大家介绍利用Python爬取并简单分析猫眼电影影评.让我们愉快地开始吧~ 开发工具 Python版本:3.6.4 相关模块: requests模块: pyecharts模块: jieba模 ...

  10. 爬取某猫即将上映电影数据,写入excel保存

    今天给大家带来如何爬取某猫即将上映电影的详细数据 这是我们今天爬取的页面 今天需要用到3个模块 import parsel import requests as r import xlwt parse ...

最新文章

  1. Xmind笔记之HTML5+CSS
  2. World Final 2012
  3. codeforces gym100959 I - Robots(稠密图建图优化)
  4. MySQL支持IPv6
  5. HBase Filter 过滤器概述
  6. 『004』索引-Python
  7. python递归(一分钟读懂)
  8. shell脚本批量导出MYSQL数据库日志/按照最近N天的形式导出二进制日志[连载之构建百万访问量电子商务网站]...
  9. Swift on Linux —— 从源码开始安装
  10. js最新手机号码、身份证正则表达式
  11. 历经万难,终于搭好深度学习环境[吐血总结篇,造福后人]
  12. android webview最新版下载,AndroidWebView
  13. 抓住暴涨点,通达信洗盘回调介入指标公式图解
  14. T(n)=2T(n/2)+n的解为T(n)=O(nlogn)的求解
  15. 浏览器在b站等网站看视频无法用鼠标滑轮控制声音
  16. 美国通胀“爆表”要加息100点?
  17. postman模拟并发请求
  18. 股票:开盘价 收盘价 涨停价 跌停价
  19. Idea分享项目到全球最大同x交友网站gayhub居然失败了!我居然没有权限!来看看解决方法吧
  20. ppt精选111个毕业答辩模板

热门文章

  1. 支付宝签名php,支付宝快捷支付 PHP服务端签名
  2. 凭什么国内造不出高端示波器?一起扒一扒示波器的发展史!
  3. MATLAB自定义拟合函数
  4. JS获取当前时间的日周月年的开始结束时间
  5. 如何查看win10系统的激活情况
  6. c语言大一,C语言复习 大一.doc
  7. Riverbed为用户优化网络效能并保护数据安全
  8. 治愈系英语笔记-1-特殊疑问句
  9. Android LocalServices解耦妙用: system_server进程中各个服务之间的连通器LocalServices
  10. HDU 4609 3-idiots (思维+FFT卷积)