欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解Python爬取并下载《电影天堂》3千多部电影

程序员文章站 2024-01-21 09:15:34
不知不觉,玩爬虫玩了一个多月了。 我愈发觉得,爬虫其实并不是什么特别高深的技术,它的价值不在于你使用了什么特别牛的框架,用了多么了不起的技术,它不需要。它只是以一种自动化...

不知不觉,玩爬虫玩了一个多月了。

我愈发觉得,爬虫其实并不是什么特别高深的技术,它的价值不在于你使用了什么特别牛的框架,用了多么了不起的技术,它不需要。它只是以一种自动化搜集数据的小工具,能够获取到想要的数据,就是它最大的价值。

我的爬虫课老师也常跟我们强调,学习爬虫最重要的,不是学习里面的技术,因为前端技术在不断的发展,爬虫的技术便会随着改变。学习爬虫最重要的是,学习它的原理,万变不离其宗。

爬虫说白了是为了解决需要,方便生活的。如果能够在日常生活中,想到并应用爬虫去解决实际的问题,那么爬虫的真正意义也久发挥出来了。

这是些闲话啦,有感而发而已。

最近有点片荒,不知道该看什么电影,而且有些电影在网上找好久也找不到资源。后来我了解到这个网站,发现最近好多不错的电影上面都有资源(这里我就先不管它的来源正不正规啦,#掩面)。

所以这次我们要爬取的网站是:《电影天堂》,屯一些电影,等无聊的时候拿出来看看,消遣消遣也是不错。

详解Python爬取并下载《电影天堂》3千多部电影

这次的网站,从爬虫的技术角度上来讲,难度不大,而且可以说是非常简单了。但是,它实用啊!你想嘛,早上上班前跑一下爬虫,晚上回家以后已经有几十部最新大片在你硬盘里等着你啦,累了一天躺床上看看电影,这种感觉是不是很爽啊。

而且正因为这个爬虫比较简单,所以我会写的稍微细一点,争取让 python 小白们也能尽可能看懂,并且能够在这个爬虫的基础上修改,得到爬取这个网站其他板块或者其他电影网站的爬虫。

写在前面的话

在编写爬虫程序之前,我先捋一捋我们的思路。

  1. 爬虫的原理,是通过给定的一个 url(就是类似于 这样的,俗称网址的东东) 请求,去访问一个网页,获取那个网页上的源代码(不知道源代码的,随便打开一个网页,右键,查看网页源代码,出来的一大堆像乱码一样的东西就是网页源代码,我们需要的数据就藏在这些源代码里面)并返回来。
  2. 然后,通过一些手段(比如说json库,beautifulsoup库,正则表达式等)从网页源代码中筛选出我们想要的数据(当然,前提是我们需要分析网页结构,知道自己想要什么数据,以及这些数据存放在网页的哪儿,存放的位置有什么特征等)。
  3. 最后,将我们获取到的数据按照一定的格式,存储到本地或者数据库中,这样就完成了爬虫的全部工作。

当然,也有一些 「骚操作」,如果你嫌爬虫效率低,可以开多线程(就是相当于几十只爬虫同时给你爬,效率直接翻了几十倍);如果担心爬取频率过高被网站封 ip,可以挂 ip 代理(相当于打几枪换个地方,对方网站就不知道你究竟是爬虫还是正常访问的用户了);如果对方网站有反爬机制,那么也有一些骚操作可以绕过反爬机制(有点黑客攻防的感觉,有木有!)。这些都是后话了。

爬虫部分

一、分析网站结构(以动作片电影为例)

1. 分析网页的 url 的组成结构

首先,我们需要分析网页的 url 的组成结构,主要关注两方面,一是如何切换选择的电影类型,二是网页如何翻页的。

点击网页上的电影类型的按钮,观察地址栏中的 url ,发现网址和电影类型的关系如下:

电影类型 网址
剧情片
喜剧片
动作片
爱情片
科幻片
动画片
悬疑片
惊悚片
恐怖片
记录片
...... ......
灾难片
武侠片
古装片

发现规律了吧,以后如果想爬其他类型的电影,只要改变 url 中的数字即可,甚至你可以写一个循环,把所有板块中的电影全部爬取下来。

随便打开一个分类,我们滚动到页面的最下面,发现这里有翻页的按钮,点击按钮翻页的同时,观察 url 的变化。

页码 url
第一页
第二页
第三页
第四页

除了第一页是 「index」外,其余页码均是 「index_页码」的形式。

所以我们基本掌握了网站的 url 的构成形式,这样我们就可以通过自己构造 url 来访问任意类型电影的任意一页了,是不是很酷。

2. 分析网站的页面结构

其次,我们分析一下网站的页面结构,看一看我们需要的信息都藏在网页的什么地方(在这之前我们先要明确一下我们需要哪些数据),由于我们这个目的是下载电影,所以对我有用的数据只有两个,电影名称和下载电影的磁力链接。

按 f12 召唤出开发者工具(这个工具可以帮助你快速定位网页中的元素在 html 源代码中位置)。

详解Python爬取并下载《电影天堂》3千多部电影

然后,我们可以发现,电影列表中,每一部电影的信息存放在一个 <table> 标签里,而电影的名字,就藏在里面的一个 <a> 标签中。电影下载的磁力链接在电影的详情页面,而电影详情页面的网址也在这个 <a> 标签中( href 属性的值)。

详解Python爬取并下载《电影天堂》3千多部电影

 而下载的磁力链接,存放在 <tbody> 标签下的 <a> 标签中,是不是很好找啊!

最后我们来缕一缕思路,一会儿我们准备这样操作:通过前面的网址的构造规则,访问到网站的某一页,然后获取到这个页面里的所有 table 标签(这里存放着电影的数据),然后从每一个 table 标签中找到存有电影名称的 a 标签(这里可以拿到电影名称以及详情页面的网址),然后通过这里获取的网址访问电影的详情页面,在详情页面挑选出 <tbody> 标签下的 <a> 标签(这里存放着电影的下载链接),这样我们就找到了我们所需要的全部数据了,是不是很简单啊。

二、爬虫编码阶段 

爬虫的程序,我一般习惯把它分成五个部分, 一是主函数,作为程序的入口,二是爬虫调度器,三是网络请求函数,四是网页解析函数,五是数据存储函数。

  1. get_data :其参数是目标网页 url,这个函数可以模拟浏览器访问 url,获取并将网页的内容返回。
  2. parse_data :其参数是网页的内容,这个函数主要是用来解析网页内容,筛选提取出关键的信息,并打包成列表返回。
  3. save_data :其参数是数据的列表,这个函数用来将列表中的数据写入本地的文件中。
  4. main :这个函数是爬虫程序的调度器,可以根据事先分析好的 url 的规则,不断的构造新的请求 url,并调用其他三个函数,获取数据并保存到本地,直到结束。
  5. if __name__ == '__main__' :这是主程序的入口,在这里调用 main 函数,启动爬虫调度器即可。
# 我们用到的库
import requests
import bs4
import re
import pandas as pd

1. 网络请求函数 :get_data (url)

负责访问指定的 url 网页,并将网页的内容返回,此部分功能比较简单固定,一般不需要做修改(除非你要挂代理,或者自定义请求头等,可以做一些相应的调整)。

def get_data(url):
  '''
  功能:访问 url 的网页,获取网页内容并返回
  参数:
    url :目标网页的 url
  返回:目标网页的 html 内容
  '''
  headers = {
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'user-agent': 'mozilla/5.0 (windows nt 10.0; wow64) applewebkit/537.36 (khtml, like gecko) chrome/68.0.3440.106 safari/537.36',
  }
 
  try:
    r = requests.get(url, headers=headers)
    r.raise_for_status()
    return r.text
  
  except requests.httperror as e:
    print(e)
    print("httperror")
  except requests.requestexception as e:
    print(e)
  except:
    print("unknown error !")

2. 网页解析函数:parse_data(html)

 这个函数是整个爬虫程序的核心所在,整体思路在上一部分已经讲过了。我这里使用的库是 beautifulsoup。

这部分的写法多种多样,有很多发挥的空间,也没有什么太多固定的模式,因为这部分的写法是要随着不同网站的页面结构来做调整的,比如说有的网站提供了数据的 api 接口,那么返回的数据就是 json 格式,我们只需要调用 json 库就可以完成数据解析,而大部分的网站只能通过从网页源代码中一层层筛选(筛选手段也多种多样,什么正则表达式,beautifulsoup等等)。

这里需要根据数据的形式来选择不同的筛选策略,所以,知道原理就可以了,习惯什么方法就用什么方法,反正最后能拿到数据就好了。

def parse_data(html):
  '''
  功能:提取 html 页面信息中的关键信息,并整合一个数组并返回
  参数:html 根据 url 获取到的网页内容
  返回:存储有 html 中提取出的关键信息的数组
  '''
  bsobj = bs4.beautifulsoup(html,'html.parser')
  info = []
  
  # 获取电影列表
  tblist = bsobj.find_all('table', attrs = {'class': 'tbspan'})
  
  # 对电影列表中的每一部电影单独处理
  for item in tblist:
 
    movie = []
    link = item.b.find_all('a')[1]
 
    # 获取电影的名称
    name = link["title"]
 
    # 获取详情页面的 url
    url = 'https://www.dy2018.com' + link["href"]
 
    # 将数据存放到电影信息列表里
    movie.append(name)
    movie.append(url)
    
    try:
      # 访问电影的详情页面,查找电影下载的磁力链接
      temp = bs4.beautifulsoup(get_data(url),'html.parser')
      tbody = temp.find_all('tbody')
      
      # 下载链接有多个(也可能没有),这里将所有链接都放进来
      for i in tbody:
        download = i.a.text
        movie.append(download)
        
      #print(movie)
 
      # 将此电影的信息加入到电影列表中
      info.append(movie)
      
    except exception as e:
      print(e)
  
  return info

3. 数据存储函数:save_data(data)

 这个函数目的是将数据存储到本地文件或数据库中,具体的写法要根据实际需要的存储形式来定,我这里是将数据存放在本地的 csv 文件中。

当然这个函数也并不只能做这些事儿,比如你可以在这里写一些简单的数据处理的操作,比如说:数据清洗,数据去重等操作。

def save_data(data):
  '''
  功能:将 data 中的信息输出到文件中/或数据库中。
  参数:data 将要保存的数据 
  '''
  filename = 'data/电影天堂/动作片.csv'
  
  dataframe = pd.dataframe(data)
  dataframe.to_csv(filename, mode='a', index=false, sep=',', header=false)

4. 爬虫调度器:main()

这个函数负责根据 url 生成规则,构造新的 url 请求,然后依次调用网络请求函数,网页解析函数,数据存储函数,爬取并保存该页数据。

所谓爬虫调度器,就是控制爬虫什么时候开始爬,多少只爬虫一起爬,爬哪个网页,爬多久休息一次,等等这些事儿。

def main():
  # 循环爬取多页数据
  for page in range(1, 114):
    print('正在爬取:第' + str(page) + '页......')    
    # 根据之前分析的 url 的组成结构,构造新的 url
    if page == 1:
      index = 'index'
    else:
      index = 'index_' + str(page)      
    url = 'https://www.dy2018.com/2/'+ index +'.html'
    # 依次调用网络请求函数,网页解析函数,数据存储函数,爬取并保存该页数据
    html = get_data(url)
    movies = parse_data(html)
    save_data(movies)
    
    print('第' + str(page) + '页完成!')

5. 主函数:程序入口

主函数作为程序的入口,只负责启动爬虫调度器。

这里我一般习惯在 main() 函数前后输出一条语句,以此判断爬虫程序是否正常启动和结束。

if __name__ == '__main__':
  print('爬虫启动成功!')
  main()
  print('爬虫执行完毕!')

三、程序运行结果

 运行了两个小时左右吧,终于爬完了 113 页,共 3346 部动作片电影的数据(本来不止这些的,但是有一些电影没有提供下载链接,我在 excel 中排序后直接手动剔除了)。

详解Python爬取并下载《电影天堂》3千多部电影

 详解Python爬取并下载《电影天堂》3千多部电影

然后想看什么电影的话,直接复制这些电影下载的磁力链接,到迅雷里面下载就好啦。 

四、爬虫程序的一些小优化

1. 在网站提供的下载链接中,我试了一下,发现 magnet 开头的这类链接放在迅雷中可以直接下载,而 ftp 开头的链接在迅雷中总显示资源获取失败(我不知道是不是我打开的方式不对,反正就是下载不来),于是我对程序做了一些小的调整,使其只获取 magnet 这类的链接。

修改的方式也很简单,只需要调整 网页解析函数 即可(爬虫的五个部分是相对独立的,修改时只需调整相应的模块即可,其余部分无需修改)。

def parse_data(html):
  '''
  功能:提取 html 页面信息中的关键信息,并整合一个数组并返回
  参数:html 根据 url 获取到的网页内容
  返回:存储有 html 中提取出的关键信息的数组
  '''
  bsobj = bs4.beautifulsoup(html,'html.parser')
  info = []
  
  # 获取表头信息
  tblist = bsobj.find_all('table', attrs = {'class': 'tbspan'})
  
  for item in tblist:
    movie = []
    link = item.b.find_all('a')[1]
    name = link["title"]
    url = 'https://www.dy2018.com' + link["href"]
    
    try:
      # 查找电影下载的磁力链接
      temp = bs4.beautifulsoup(get_data(url),'html.parser')
      tbody = temp.find_all('tbody')
      
      for i in tbody:
        download = i.a.text
        if 'magnet:?xt=urn:btih' in download:
          movie.append(name)
          movie.append(url)
          movie.append(download)
          #print(movie)
          info.append(movie)
          break
    except exception as e:
      print(e)
  
  return info

注意代码 26 行处,我加了一个 if 语句的判断,如果下载链接中包含 magnet:?xt=urn:btih 字符串,则视为有效链接,下载下来,否则跳过。

2. 我一直在想能不能有个办法让迅雷一键批量下载我们爬到的电影。使用 python 操纵第三方的软件,这其实挺难的。不过后来找到了一种方法,也算是解决了这个问题。

就是我们发现迅雷软件启动后,会自动检测我们的剪切板,只要我们复制了下载链接,它便会自动弹出下载的提示框。借助这个思路,我们可以使用代码,将下载的链接复制进入剪切板,等下载框自动出现后,手动确认开始下载(这是我目前想到的最好的办法了,不知道各位大佬有没有更好的思路,欢迎指导交流)。

import pyperclip
import os
import pandas as pd
 
imagedata = pd.read_csv("data/电影天堂/动作片2.csv",names=['name','link','download'],encoding = 'gbk')
# 获取电影的下载链接,并用换行符分隔
a_link = imagedata['download']
links = '\n'.join(a_link)
 
# 复制到剪切板
pyperclip.copy(links);
print('已粘贴');
 
# 打开迅雷
thunder_path = r'd:\program files (x86)\thunder network\thunder9\program\thunder.exe'
os.startfile(thunder_path)

亲测可以实现,但是。。。不建议尝试(你能想象迅雷打开的一瞬间创建几百个下载任务的场景吗?反正我的电脑是缓了好久好久才反应过来)。大家还是老老实实的,手动复制链接下载吧(csv文件可以用 excel 打开,竖着选中一列,然后复制,也能达到相同的效果) ,这种骚操作太蠢了还是不要试了。

写在后面的话

啰啰嗦嗦的写了好多,也不知道关键的问题讲清楚了没有。有哪里没讲清楚,或者哪里讲的不合适的话,欢迎骚扰。
 其实吧,写文章,写博客,写教程,都是一个知识重新熔炼内化的过程,在写这篇博客的时候,我也一直在反复审视我学习爬虫的过程,以及我爬虫代码一步步的变化,从一开始的所有代码全部揉在主函数中,到后来把一些变动较少的功能提取出来,写成单独的函数,再到后来形成基本稳定的五大部分。

以至于在我后来学习使用 scrapy 框架时候,惊人的发现 scrapy 框架的结构跟我的爬虫结构有着异曲同工之妙,我的这个相当于是一个简易版的爬虫框架了,纯靠自己摸索达到这个效果,我感觉还是挺有成就感的。

以上所述是小编给大家介绍的python爬取并下载《电影天堂》3千多部电影详解整合,希望对大家有所帮助