Python3.x+迅雷x 自动下载高分电影的实现方法


Posted in Python onJanuary 12, 2020

快要过年了,大家都在忙些什么呢?一到年底公司各种抢票,备年货,被这过年的气氛一烘,都归心似箭,哪还有心思上班啊。归心似箭=产出低下=一行代码十个错=无聊。于是想起了以前学过一段时间的Python,自己平时也挺爱看电影的,手动点进去看电影详情然后一部一部的去下载太烦了,何不用Python写个自动下载电影的工具呢?诶,这么一想就不无聊了。以前还没那么多XX会员的时候,想看看电影都是去XX天堂去找电影资源,大部分想看的电影还是有的,就它了,爬它!

话说以前玩Python的时候爬过挺多网站的,都是在公司干的(Python不属于公司的业务范围,纯属自己折腾着好玩),我那个负责运维的同事天天跑过来说:你又在爬啥啊,你去看看新闻,某某爬东西又被抓了!出了事你自己负责啊!哎呀我的娘亲,吓的都没继续玩下去了。这个博客是爬取某天堂的资源(具体是哪个天堂下面的代码里会有的),会不会被抓啊?单纯的作为技术讨论,个人练手,不做商业用途应该没事吧?写到这里小手不禁微微颤抖...

得嘞,死就死吧,我不入地狱谁入地狱,先看最终实现效果:

Python3.x+迅雷x 自动下载高分电影的实现方法

如上,这个下载工具是有界面的(牛皮吧),只要输入一个根地址和电影评分,就可以自动爬电影了,要完成这个工具需要具备以下知识点:

  • PyCharm的安装和使用 这个不多说,猿们都懂,不属于猿类的我也没办法科普了,就是个IDE
  • tkinter 这是个Python GUI开发的库,图中这个简陋的可怜的界面就是基于TK开发的,不想要界面也可以去掉,丝毫不影响爬电影,加上用户界面可以显得?乓坏悖?比蛔钪饕?氖俏蚁胙?耙坏阈轮??strong>静态网页的分析技巧 相对于动态网站的爬取,静态网站的爬取就显得小菜了,F12会按吧,右键查看网页源代码会吧,通过这些简单的操作就可以查看网页的排版布局规则,然后根据这些规则写爬虫,soeasy
  • 数据持久化 已经下载过的电影,下次再爬电影的时候不希望再下载一次吧,那就把下载过的链接存储起来,下载电影之前去比对是否下载过,以过滤重复下载
  • 迅雷X的下载安装 这个就更不用多说了,作为当代社会主义有为青年,谁没用过迅雷?谁的硬盘里没有几部动作类型的片子?

差不多就这些了,至于实现的技术细节的话,也不多,requests+BeautifulSoup的使用,re正则Python数据类型Python线程dbm、pickle等数据持久化库的使用,等等,这个工具也就这么些知识范畴了。当然,Python是面向对象的,编程思想是所有语言通用的,这个不是一朝一夕的事,也没办法通过语言描述清楚。各位对号入座,以上哪个知识面不足的自己去翻资料学习,我可是直接贴代码的。

说到Python的学习还是多说两句吧,以前学习Python爬虫的时候看的是 @工匠若水 https://blog.csdn.net/yanbober的博客,这哥们的Python文章写的真不错,对于有过编程经验却从没接触过Python的人很有帮助,基本上很快就能上手一个小项目。得嘞,撸代码:

import url_manager
import html_parser
import html_download
import persist_util
from tkinter import *
from threading import Thread
import os
 
class SpiderMain(object):
  def __init__(self):
    self.mUrlManager = url_manager.UrlManager()
    self.mHtmlParser = html_parser.HtmlParser()
    self.mHtmlDownload = html_download.HtmlDownload()
    self.mPersist = persist_util.PersistUtil()
 
  # 加载历史下载链接
  def load_history(self):
    history_download_links = self.mPersist.load_history_links()
    if history_download_links is not None and len(history_download_links) > 0:
      for download_link in history_download_links:
        self.mUrlManager.add_download_url(download_link)
        d_log("加载历史下载链接: " + download_link)
 
  # 保存历史下载链接
  def save_history(self):
    history_download_links = self.mUrlManager.get_download_url()
    if history_download_links is not None and len(history_download_links) > 0:
      self.mPersist.save_history_links(history_download_links)
 
  def craw_movie_links(self, root_url, score=8):
    count = 0;
    self.mUrlManager.add_url(root_url)
    while self.mUrlManager.has_continue():
      try:
        count = count + 1
        url = self.mUrlManager.get_url()
        d_log("craw %d : %s" % (count, url))
        headers = {
          'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36',
          'Referer': url
        }
        content = self.mHtmlDownload.down_html(url, retry_count=3, headers=headers)
        if content is not None:
          doc = content.decode('gb2312', 'ignore')
          movie_urls, next_link = self.mHtmlParser.parser_movie_link(doc)
          if movie_urls is not None and len(movie_urls) > 0:
            for movie_url in movie_urls:
              d_log('movie info url: ' + movie_url)
              content = self.mHtmlDownload.down_html(movie_url, retry_count=3, headers=headers)
              if content is not None:
                doc = content.decode('gb2312', 'ignore')
                movie_name, movie_score, movie_xunlei_links = self.mHtmlParser.parser_movie_info(doc, score=score)
                if movie_xunlei_links is not None and len(movie_xunlei_links) > 0:
                  for xunlei_link in movie_xunlei_links:
                    # 判断该电影是否已经下载过了
                    is_download = self.mUrlManager.has_download(xunlei_link)
                    if is_download == False:
                      # 没下载过的电影添加到迅雷下载列表
                      d_log('开始下载 ' + movie_name + ', 链接地址: ' + xunlei_link)
                      self.mUrlManager.add_download_url(xunlei_link)
                      os.system(r'"D:\迅雷\Thunder\Program\Thunder.exe" {url}'.format(url=xunlei_link))
                      # 每下载一部电影都实时更新数据库,这样可以保证即使程序异常退出也不会重复下载该电影
                      self.save_history()
          if next_link is not None:
            d_log('next link: ' + next_link)
            self.mUrlManager.add_url(next_link)
      except Exception as e:
        d_log('错误信息: ' + str(e))
 
 
def runner(rootLink=None, scoreLimit=None):
  if rootLink is None:
    return
  spider = SpiderMain()
  spider.load_history()
  if scoreLimit is None:
    spider.craw_movie_links(rootLink)
  else:
    spider.craw_movie_links(rootLink, score=float(scoreLimit))
  spider.save_history()
 
# rootLink = 'https://www.dytt8.net/html/gndy/dyzz/index.html'
# rootLink = 'https://www.dytt8.net/html/gndy/dyzz/list_23_207.html'
def start(rootLink, scoreLimit):
  loop_thread = Thread(target=runner, args=(rootLink, scoreLimit,), name='LOOP THREAD')
  #loop_thread.setDaemon(True)
  loop_thread.start()
  #loop_thread.join() # 不能让主线程等待,否则GUI界面将卡死
  btn_start.configure(state='disable')
 
# 刷新GUI界面,文字滚动效果
def d_log(log):
  s = log + '\n'
  txt.insert(END, s)
  txt.see(END)
 
if __name__ == "__main__":
  rootGUI = Tk()
  rootGUI.title('XX电影自动下载工具')
  # 设置窗体背景颜色
  black_background = '#000000'
  rootGUI.configure(background=black_background)
  # 获取屏幕宽度和高度
  screen_w, screen_h = rootGUI.maxsize()
  # 居中显示窗体
  window_x = (screen_w - 640) / 2
  window_y = (screen_h - 480) / 2
  window_xy = '640x480+%d+%d' % (window_x, window_y)
  rootGUI.geometry(window_xy)
 
  lable_link = Label(rootGUI, text='解析根地址: ',\
            bg='black',\
            fg='red', \
            font=('宋体', 12), \
            relief=FLAT)
  lable_link.place(x=20, y=20)
 
  lable_link_width = lable_link.winfo_reqwidth()
  lable_link_height = lable_link.winfo_reqheight()
 
  input_link = Entry(rootGUI)
  input_link.place(x=20+lable_link_width, y=20, relwidth=0.5)
 
  lable_score = Label(rootGUI, text='电影评分限制: ', \
            bg='black', \
            fg='red', \
            font=('宋体', 12), \
            relief=FLAT)
  lable_score.place(x=20, y=20+lable_link_height+10)
 
  input_score = Entry(rootGUI)
  input_score.place(x=20+lable_link_width, y=20+lable_link_height+10, relwidth=0.3)
 
  btn_start = Button(rootGUI, text='开始下载', command=lambda: start(input_link.get(), input_score.get()))
  btn_start.place(relx=0.4, rely=0.2, relwidth=0.1, relheight=0.1)
 
  txt = Text(rootGUI)
  txt.place(rely=0.4, relwidth=1, relheight=0.5)
 
  rootGUI.mainloop()

spider_main.py,主代码入口,主要是tkinter 实现的一个简陋的界面,可以输入根地址,电影最低评分。所谓的根地址就是某天堂网站的一类电影的入口,比如进入首页有如下的分类,最新电影、日韩电影、欧美影片、2019精品专区,等等。这里以2019精品专区为例(https://www.dytt8.net/html/gndy/dyzz/index.html),当然,用其它的分类地址入口也是可以的。评分就是个过滤电影的条件,要学会对垃圾电影说不,浪费时间浪费表情,你可以指定大于等于8分的电影才下载,也可以指定大于等于9分等,必须输入数字哈,输入些乱七八糟的东西进去程序会崩溃,这个细节我懒得处理。

'''
URL链接管理类,负责管理爬取下来的电影链接地址,包括新解析出来的链接地址,和已经下载过的链接地址,保证相同的链接地址只会下载一次
'''
class UrlManager(object):
  def __init__(self):
    self.urls = set()
    self.used_urls = set()
    self.download_urls = set()
 
  def add_url(self, url):
    if url is None:
      return
    if url not in self.urls and url not in self.used_urls:
      self.urls.add(url)
 
  def add_urls(self, urls):
    if urls is None or len(urls) == 0:
      return
    for url in urls:
      self.add_url(url)
 
  def has_continue(self):
    return len(self.urls) > 0
 
  def get_url(self):
    url = self.urls.pop()
    self.used_urls.add(url)
    return url
 
  def get_download_url(self):
    return self.download_urls
 
  def has_download(self, url):
    return url in self.download_urls
 
  def add_download_url(self, url):
    if url is None:
      return
    if url not in self.download_urls:
      self.download_urls.add(url)

url_manager.py,注释里写的很清楚了,基本上每个py文件的关键地方我都写了比较详细的注释

import requests
from requests import Timeout
 
'''
HtmlDownload,通过一个链接地址将该html页面整体down下来,然后通过html_parser.py解析其中有价值的信息
'''
class HtmlDownload(object):
  def __init__(self):
    self.request_session = requests.session()
    self.request_session.proxies
 
  def down_html(self, url, retry_count=3, headers=None, proxies=None, data=None):
    if headers:
      self.request_session.headers.update(headers)
    try:
      if data:
        content = self.request_session.post(url, data=data, proxies=proxies)
        print('result code: ' + str(content.status_code) + ', link: ' + url)
        if content.status_code == 200:
          return content.content
      else:
        content = self.request_session.get(url, proxies=proxies)
        print('result code: ' + str(content.status_code) + ', link: ' + url)
        if content.status_code == 200:
          return content.content
    except (ConnectionError, Timeout) as e:
      print('HtmlDownload ConnectionError or Timeout: ' + str(e))
      if retry_count > 0:
        self.down_html(url, retry_count-1, headers, proxies, data)
      return None
    except Exception as e:
      print('HtmlDownload Exception: ' + str(e))

html_download.py,就是用requests将静态网页的内容整体down下来

from bs4 import BeautifulSoup
from urllib.parse import urljoin
import re
import urllib.parse
import base64
 
'''
html页面解析器
'''
class HtmlParser(object):
  # 解析电影列表页面,获取电影详情页面的链接
  def parser_movie_link(self, content):
    try:
      urls = set()
      next_link = None
      doc = BeautifulSoup(content, 'lxml')
      div_content = doc.find('div', class_='co_content8')
      if div_content is not None:
        tables = div_content.find_all('table')
        if tables is not None and len(tables) > 0:
          for table in tables:
            link = table.find('a', class_='ulink')
            if link is not None:
              print('movie name: ' + link.text)
              movie_link = urljoin('https://www.dytt8.net', link.get('href'))
              print('movie link ' + movie_link)
              urls.add(movie_link)
        next = div_content.find('a', text=re.compile(r".*?下一页.*?"))
        if next is not None:
          next_link = urljoin('https://www.dytt8.net/html/gndy/dyzz/', next.get('href'))
          print('movie next link ' + next_link)
 
      return urls, next_link
    except Exception as e:
      print('解析电影链接地址发生错误: ' + str(e))
 
  # 解析电影详情页面,获取电影详细信息
  def parser_movie_info(self, content, score=8):
    try:
      movie_name = None # 电影名称
      movie_score = 0  # 电影评分
      movie_xunlei_links = set() # 电影的迅雷下载地址,可能存在多个
      doc = BeautifulSoup(content, 'lxml')
      movie_name = doc.find('title').text.replace('迅雷下载_电影天堂', '')
      #print(movie_name)
      div_zoom = doc.find('div', id='Zoom')
      if div_zoom is not None:
        # 获取电影评分
        span_txt = div_zoom.text
        txt_list = span_txt.split('◎')
        if txt_list is not None and len(txt_list) > 0:
          for tl in txt_list:
            if 'IMDB' in tl or 'IMDb' in tl or 'imdb' in tl or 'IMdb' in tl:
              txt_score = tl.split('/')[0]
              print(txt_score)
              movie_score = re.findall(r"\d+\.?\d*", txt_score)
              if movie_score is None or len(movie_score) <= 0:
                movie_score = 1
              else:
                movie_score = movie_score[0]
        print(movie_name + ' IMDB影片分数: ' + str(movie_score))
        if float(movie_score) < score:
          print('电影评分低于' + str(score) + ', 忽略')
          return movie_name, movie_score, movie_xunlei_links
        txt_a = div_zoom.find_all('a', href=re.compile(r".*?ftp:.*?"))
        if txt_a is not None:
          # 获取电影迅雷下载地址,base64转成迅雷格式
          for alink in txt_a:
            xunlei_link = alink.get('href')
            '''
            这里将电影链接转换成迅雷的专用下载链接,后来发现不转换迅雷也能识别
            xunlei_link = urllib.parse.quote(xunlei_link)
            xunlei_link = xunlei_link.replace('%3A', ':')
            xunlei_link = xunlei_link.replace('%40', '@')
            xunlei_link = xunlei_link.replace('%5B', '[')
            xunlei_link = xunlei_link.replace('%5D', ']')
            xunlei_link = 'AA' + xunlei_link + 'ZZ'
            xunlei_link = base64.b64encode(xunlei_link.encode('gbk'))
            xunlei_link = 'thunder://' + str(xunlei_link, encoding='gbk')
            '''
            print(xunlei_link)
            movie_xunlei_links.add(xunlei_link)
      return movie_name, movie_score, movie_xunlei_links
    except Exception as e:
      print('解析电影详情页面错误: ' + str(e))

html_parser.py,用bs4解析down下来的html页面内容,根据网页规则过去我们需要的东西,这是爬虫最重要的地方,写爬虫的目的就是想要取出对我们有用的东西。

import dbm
import pickle
import os
 
'''
数据持久化工具类
'''
class PersistUtil(object):
  def save_data(self, name='No Name', urls=None):
    if urls is None or len(urls) <= 0:
      return
    try:
      history_db = dbm.open('downloader_history', 'c')
      history_db[name] = str(urls)
    finally:
      history_db.close()
 
  def get_data(self):
    history_links = set()
    try:
      history_db = dbm.open('downloader_history', 'r')
      for key in history_db.keys():
        history_links.add(str(history_db[key], 'gbk'))
    except Exception as e:
      print('遍历dbm数据失败: ' + str(e))
    return history_links
 
  # 使用pickle保存历史下载记录
  def save_history_links(self, urls):
    if urls is None or len(urls) <= 0:
      return
    with open('DownloaderHistory', 'wb') as pickle_file:
      pickle.dump(urls, pickle_file)
 
  # 获取保存在pickle中的历史下载记录
  def load_history_links(self):
    if os.path.exists('DownloaderHistory'):
      with open('DownloaderHistory', 'rb') as pickle_file:
        return pickle.load(pickle_file)
    else:
      return None

persist_util.py,数据持久化工具类。

这样代码部分就完成了,说下迅雷,我安装的是最新版的迅雷X,一定要如下图一样在迅雷设置打开一键下载功能,否则每次新增一个下载任务都会弹出用户确认框的,还有就是调用迅雷下载资源的代码:os.system(r'"D:\迅雷\Thunder\Program\Thunder.exe" {url}'.format(url=xunlei_link)),一定要去到迅雷安装目录找到Thunder.exe文件,不能用快捷方式的地址(我的电脑->迅雷->右键属性->目标,迅雷X这里显示的路径是快捷方式的路径,不能用这个),否则找不到程序。

Python3.x+迅雷x 自动下载高分电影的实现方法

到这里你应该就可以电影爬起来了,妥妥的。当然,你想要优化也可以,程序有很多可以优化的地方,比如线程那一块,比如数据持久化那里..... 初学者可以通过这个练手,然后自己去分析分析静态网站的规则,把解析html那一块的代码改改就可以爬其它的网站了,比如那些有着危险动作的电影... 不过这类电影还是少看为妙,要多读书,偶尔看了也要擦擦干净,洗洗干净,要讲卫生。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python入门篇之数字
Oct 20 Python
详解Python中dict与set的使用
Aug 10 Python
python实现class对象转换成json/字典的方法
Mar 11 Python
pycharm 配置远程解释器的方法
Oct 28 Python
Python 保存矩阵为Excel的实现方法
Jan 28 Python
Django多数据库配置及逆向生成model教程
Mar 28 Python
Iconfont(矢量图标)+iconmoon(图标svg互转)配合javascript实现社交分享系统
Apr 21 Python
Keras 快速解决OOM超内存的问题
Jun 11 Python
如何教少儿学习Python编程
Jul 10 Python
Pycharm调试程序技巧小结
Aug 08 Python
Django解决frame拒绝问题的方法
Dec 18 Python
详解Open Folder as PyCharm Project怎么添加的方法
Dec 29 Python
tensorflow的计算图总结
Jan 12 #Python
python利用JMeter测试Tornado的多线程
Jan 12 #Python
Django 批量插入数据的实现方法
Jan 12 #Python
python处理RSTP视频流过程解析
Jan 11 #Python
pyftplib中文乱码问题解决方案
Jan 11 #Python
python实现实时视频流播放代码实例
Jan 11 #Python
python3下pygame如何实现显示中文
Jan 11 #Python
You might like
PHP面向对象分析设计的61条军规小结
2010/07/17 PHP
php二维数组排序与默认自然排序的方法介绍
2013/04/27 PHP
PHP中mysql_field_type()函数用法
2014/11/24 PHP
php跨服务器访问方法小结
2015/05/12 PHP
一个很简单的办法实现TD的加亮效果.
2006/06/29 Javascript
一步一步制作jquery插件Tabs实现过程
2010/07/06 Javascript
tangram框架响应式加载图片方法
2013/11/21 Javascript
通过JS来动态的修改url,实现对url的增删查改
2014/09/01 Javascript
jQuery中clone()方法用法实例
2015/01/16 Javascript
详解JavaScript的策略模式编程
2015/06/24 Javascript
jquery模拟进度条实现方法
2015/08/03 Javascript
JS三级可折叠菜单实现方法
2016/02/29 Javascript
Js 获取当前函数参数对象的实现代码
2016/06/20 Javascript
Angular ng-repeat遍历渲染完页面后执行其他操作详细介绍
2016/12/13 Javascript
解决微信小程序中转换时间格式IOS不兼容的问题
2019/02/15 Javascript
JQuery通过键盘控制键盘按下与松开触发事件
2020/08/07 jQuery
python的常见命令注入威胁
2013/02/18 Python
Python爬取读者并制作成PDF
2015/03/10 Python
python实现可将字符转换成大写的tcp服务器实例
2015/04/29 Python
教你用Python创建微信聊天机器人
2020/03/31 Python
Python3.5.3下配置opencv3.2.0的操作方法
2018/04/02 Python
python3+PyQt5自定义视图详解
2018/04/24 Python
python实现大转盘抽奖效果
2019/01/22 Python
pymongo中聚合查询的使用方法
2019/03/22 Python
Python中类似于jquery的pyquery库用法分析
2019/12/02 Python
Python文件操作及内置函数flush原理解析
2020/10/13 Python
Omio西班牙:全欧洲低价大巴、火车和航班搜索和比价
2017/02/11 全球购物
Office DEPOT法国官网:欧迪办公用品采购
2018/01/03 全球购物
JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?
2015/10/22 面试题
2014年综治宣传月活动总结
2014/04/28 职场文书
赔偿协议书怎么写
2015/01/28 职场文书
社区五一劳动节活动总结
2015/02/09 职场文书
个人求职自荐信范文
2015/03/06 职场文书
2015秋季幼儿园开学通知
2015/07/16 职场文书
如何创建一个创建MySQL数据库中的datetime类型
2022/03/21 MySQL
python多次执行绘制条形图
2022/04/20 Python