Python 网络爬虫实战:爬取并下载《电影天堂》3千多部动作片影片

优采云 发布时间: 2020-07-03 08:00

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

  我更加认为,爬虫似乎并不是哪些非常深奥的技术,它的价值不在于你使用了哪些非常牛的框架,用了多么了不起的技术,它不需要。它只是以一种自动化收集数据的小工具,能够获取到想要的数据,就是它最大的价值。

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

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

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

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

  所以此次我们要爬取的网站是:《电影天堂》,屯一些影片,等无趣的时侯拿出来瞧瞧,消遣闲暇也是不错。

  

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

  而且正由于这个爬虫比较简单,所以我会写的稍为细一点,争取使 python 小白们也能尽可能读懂,并且还能在这个爬虫的基础上更改,得到爬取这个网站其他蓝筹股或则其他影片网站的爬虫。

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

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

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

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

  首先,我们须要剖析网页的 URL 的组成结构,主要关注两方面,一是怎样切换选择的影片类型,二是网页怎么翻页的。

  电影类型

  网址

  剧情片

  喜剧片

  动作片

  爱情片

  科幻片

  *敏*感*词*片

  悬疑片

  惊悚片

  恐怖片

  记录片

  ......

  ......

  灾难片

  武侠片

  古装片

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

  页码

  URL

  第一页

  第二页

  第三页

  第四页

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

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

  2. 分析网站的页面结构

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

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

  

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

  

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

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

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

  

# 我们用到的库

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 中排序后直接自动剔除了)。

  

  

  然后想看哪些影片的话,直接复制这种影片下载的磁力链接,到迅雷上面下载就好啦。

  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 框架的结构跟我的爬虫结构有着异曲同工之妙,我的这个相当于是一个简易版的爬虫框架了,纯靠自己摸索达到这个疗效,我觉得还是很有成就感的。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线