今天我们讲的是Python的实战练习:爬取电影天堂的磁力链接。
关注公众号【老夫撸代码】回复【python01】获取python交流QQ群相关信息。
关注老夫的宝子们都知道,老夫业余时间撸了一个电影网站,主要是用Laravel搭建的,里面的数据就是用Python爬取的某个电影天堂网站资源。
在我们明确了业务需求后,主要分为以下三个步骤。
1、目标网站分析
首先,我们需要寻找适合爬虫的网站。
目前一般网站的技术架构分为以下几类:
-
前后端分离的网站
这类型网站主要是用 vue 或者 react 进行前端构建,后端使用java 或者 php 提供接口,为了便于SEO优化,可能会采用ssr进行服务端渲染。本质还是返回给前端一个完整的html页面。
-
cms网站
此类型网站本质还是返回给前端一个html页面,不同的是需要模板语言做支持,比如wordpress、织梦、帝国等等建站工具
-
js动态加载的网站
这类型的网站的一个特点是返回给前端也是一个html页面,而数据是放到 js 的全局变量里面,然后再通过操作dom把数据渲染出来。
总之,技术千变万化,我们需要透过现象看到本质,俗话说:你有张良计,我有过墙梯。
我们在百度随便搜索“电影天堂”这几个关键字的时候会显示一堆资源站,随便选一个网站,参照上面的技术架构分类进行判断。
这里老夫选择了这个网站:
F12看下源码结构,发现是直接输出网页的形式,也就是第2种方式,这是最适合爬虫爬取的数据结构。
2、技术架构
通过分析整个网站的网页结构,我们可以先从导航栏入手。
一般网站设计的基本结构是列表页 + 详情页。这里列表页就是每个电影的分类,详情页就是列表页种具体的某个电影的页面。
(列表页)
(详情页)
因此我们可以初步确定爬取的思路:
1、先爬取所有的列表页的列表数据
2、然后再进入详情页爬取资源链接
这里我们可以这样设计,把所有电影资源分类下的列表页的url保存到本地,然后通过加载本地的列表url去爬取详情页资源,这样设计的理由:
1、在开发的过程中防止爬虫出现问题,大量请求到对方服务器,造成对方服务器过载而宕机。虽然说互联网的资源大家都有平等共享的权利,但是我们开发爬虫的时候也要注意职业操守,尽可能不要给对方服务器造成损失,如果后果很严重的话可能要被请去喝茶的,切记!切记!切记!
2、保持到本地的资源文件,对于下一步操作可以省略网络请求的那一环境,可以让你爬虫的速度更快。
3、整个过程做成异步操作进行解耦,如果在爬取列表页后马上去爬取详情页,整个过程任何一个环境出现问题,需要整个程序排查。
ps: 可以进一步优化,把列表页的整个内容都保存到本地,然后依次加载本地页面即可访问到详情页。
3、编码阶段
方便确定后,我们还是采用Python的Scrapy框架来做爬虫。
首先是环境搭建,我们用虚拟环境来构建python的运行环境。
其次:构建两个spider,一个是爬取列表数据,一个是爬取详情数据。
列表spider主要是把列表页的链接保存到一个allUrl.txt的文件中,方便后续详情页爬取。
class myspider(scrapy.Spider):
name = 'msp'
domain = 'https://www.XXXXXXX'
def start_requests(self):
urls = ['/html/gndy/dyzz/index.html']
for url in urls:
yield scrapy.Request(url=self.domain+url, callback=self.pagelist)
def pagelist(self, response):
select = response.xpath('//*[@id="header"]/div/div[3]/div[6]/div[2]/div[2]/div[2]/div/select').get()
# 所有分页的链接
pages = Selector(text=select).css("option").xpath("@value").getall()
curr_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
file_path = os.path.join(curr_dir, 'pages/allUrl.txt')
with open(file_path,'w') as f:
for page in pages:
f.write(self.domain + page+"\n")
详情页spider主要是加载本地的列表数据文件,然后加载列表数据进行详情页的爬取。
class DetailSpider(scrapy.Spider):
domain = "https://www.XXXXXXX"
name = 'dmsp'
def start_requests(self):
curr_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
file_path = os.path.join(curr_dir, 'pages/allUrl.txt')
start_urls = []
with open(file_path, 'r') as f:
urls = f.readlines()
for url in urls:
start_urls.append(url[0:-1])
currUrl = start_urls[22]
yield SplashRequest(url=currUrl, callback=self.parseList, args={'wait': 10},endpoint='render.html')
def parseList(self,response):
# logging.info("parseList response: %s ", response.text)
urlSel = response.xpath('//div[@class="co_content8"]/ul//a').getall()
detailUrls = []
for url in urlSel:
obj = {}
obj['url'] = self.domain + Selector(text=url).xpath('//@href').get()
obj['title'] = Selector(text=url).xpath('//text()').get()
detailUrls.append(obj)
logger.info("detailUrls: %s ",detailUrls)
for detailUrl in detailUrls:
logger.info("当前url: %s",detailUrl['url'])
yield SplashRequest(url=detailUrl['url'], callback=self.parseDetail,args={'wait': 10},endpoint='render.html')
# yield SplashRequest(url=detailUrls[0]['url'], callback=self.parseDetail,args={'wait': 10},endpoint='render.html')
def parseDetail(self,response):
image_urls = []
title = response.xpath('//div[@class="title_all"]//h1/text()').get()
ul = response.xpath("//div[@class='co_content8']//ul")
cover = ul.xpath("//div[@id='Zoom']/img[1]/@src").get()
screenshots = ul.xpath("//div[@id='Zoom']/img[2]/@src").get()
image_urls.append(cover)
image_urls.append(screenshots)
zoom = ul.xpath(".//div[@id='Zoom']/text()").getall()
magnet = response.xpath("//div[@id='downlist']//a/text()").get()
_list = [x.strip() for x in zoom ]
_list1 = [x for x in _list if x != '' ]
_list2 = [x.replace('\u3000','aaa') for x in _list1]
logging.info("_list2: %s ", _list2)
item = MyspiderItem()
item['image_urls'] = image_urls
item['title'] = title #标题
import datetime
item['id_uuid'] = genUUid()
item['createtime'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") #更新时间
_protagonist_index = 0
_introduction_index = 0
for _item in _list2:
if _item.find('◎译') != -1:
item['movie_name_zh'] = self.splitList(_item)
elif _item.find('◎片aaaaaa名') != -1:
item['movie_name_en'] = self.splitList(_item)
elif _item.find('◎年') != -1:
item['movie_year'] = self.splitList(_item)
elif _item.find('◎产') != -1:
item['movie_places'] = self.splitList(_item)
elif _item.find('◎类') != -1:
item['movie_type'] = self.splitList(_item)
elif _item.find('◎语') != -1:
item['movie_lanage'] = self.splitList(_item)
elif _item.find('◎字') != -1:
item['movie_subtitle'] = self.splitList(_item)
elif _item.find('◎上映日期') != -1:
item['release_date'] = self.splitList(_item)
elif _item.find('◎文件格式') != -1:
item['file_form'] = self.splitList(_item)
elif _item.find('◎视频尺寸') != -1:
item['video_size'] = self.splitList(_item)
elif _item.find('◎文件大小') != -1:
item['file_size'] = self.splitList(_item)
elif _item.find('◎片aaaaaa长') != -1:
item['movie_time'] = self.splitList(_item)
elif _item.find('◎导') != -1:
item['direct'] = self.splitList(_item)
elif _item.find('◎主') != -1:
item['protagonist'] = self.splitList(_item)
_protagonist_index = _list2.index(_item)
elif _item.find('◎简') != -1:
_introduction_index = _list2.index(_item)
item['isDownload'] = 0
protagonist = item['protagonist']
for po in _list2[_protagonist_index+1:_introduction_index]:
protagonist += "," + po
item['protagonist'] = protagonist
item['introduction'] = _list2[_introduction_index+1]
item['downloadUrl'] = magnet
logger.info("item: %s ", item)
yield item
def splitList(self,item):
return item.split("aaa")[-1]
由于此网站做了反爬机制,导致爬取的过程中偶尔会只返回一个js文件,所以这里老夫在docker容器里面跑了一个Splash镜像,实际上爬虫访问的页面是Splash返回的页面。
今天我们讲的是Python的实战练习:爬取电影天堂的磁力链接。
关注公众号【老夫撸代码】回复【python01】获取python交流QQ群相关信息。
关注老夫的宝子们都知道,老夫业余时间撸了一个电影网站,主要是用Laravel搭建的,里面的数据就是用Python爬取的某个电影天堂网站资源。
在我们明确了业务需求后,主要分为以下三个步骤。
1、目标网站分析
首先,我们需要寻找适合爬虫的网站。
目前一般网站的技术架构分为以下几类:
-
前后端分离的网站
这类型网站主要是用 vue 或者 react 进行前端构建,后端使用java 或者 php 提供接口,为了便于SEO优化,可能会采用ssr进行服务端渲染。本质还是返回给前端一个完整的html页面。
-
cms网站
此类型网站本质还是返回给前端一个html页面,不同的是需要模板语言做支持,比如wordpress、织梦、帝国等等建站工具
-
js动态加载的网站
这类型的网站的一个特点是返回给前端也是一个html页面,而数据是放到 js 的全局变量里面,然后再通过操作dom把数据渲染出来。
总之,技术千变万化,我们需要透过现象看到本质,俗话说:你有张良计,我有过墙梯。
我们在百度随便搜索“电影天堂”这几个关键字的时候会显示一堆资源站,随便选一个网站,参照上面的技术架构分类进行判断。
这里老夫选择了这个网站:
F12看下源码结构,发现是直接输出网页的形式,也就是第2种方式,这是最适合爬虫爬取的数据结构。
2、技术架构
通过分析整个网站的网页结构,我们可以先从导航栏入手。
一般网站设计的基本结构是列表页 + 详情页。这里列表页就是每个电影的分类,详情页就是列表页种具体的某个电影的页面。
(列表页)
(详情页)
因此我们可以初步确定爬取的思路:
1、先爬取所有的列表页的列表数据
2、然后再进入详情页爬取资源链接
这里我们可以这样设计,把所有电影资源分类下的列表页的url保存到本地,然后通过加载本地的列表url去爬取详情页资源,这样设计的理由:
1、在开发的过程中防止爬虫出现问题,大量请求到对方服务器,造成对方服务器过载而宕机。虽然说互联网的资源大家都有平等共享的权利,但是我们开发爬虫的时候也要注意职业操守,尽可能不要给对方服务器造成损失,如果后果很严重的话可能要被请去喝茶的,切记!切记!切记!
2、保持到本地的资源文件,对于下一步操作可以省略网络请求的那一环境,可以让你爬虫的速度更快。
3、整个过程做成异步操作进行解耦,如果在爬取列表页后马上去爬取详情页,整个过程任何一个环境出现问题,需要整个程序排查。
ps: 可以进一步优化,把列表页的整个内容都保存到本地,然后依次加载本地页面即可访问到详情页。
3、编码阶段
方便确定后,我们还是采用Python的Scrapy框架来做爬虫。
首先是环境搭建,我们用虚拟环境来构建python的运行环境。
其次:构建两个spider,一个是爬取列表数据,一个是爬取详情数据。
列表spider主要是把列表页的链接保存到一个allUrl.txt的文件中,方便后续详情页爬取。
class myspider(scrapy.Spider):
name = 'msp'
domain = 'https://www.XXXXXXX'
def start_requests(self):
urls = ['/html/gndy/dyzz/index.html']
for url in urls:
yield scrapy.Request(url=self.domain+url, callback=self.pagelist)
def pagelist(self, response):
select = response.xpath('//*[@id="header"]/div/div[3]/div[6]/div[2]/div[2]/div[2]/div/select').get()
# 所有分页的链接
pages = Selector(text=select).css("option").xpath("@value").getall()
curr_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
file_path = os.path.join(curr_dir, 'pages/allUrl.txt')
with open(file_path,'w') as f:
for page in pages:
f.write(self.domain + page+"\n")
详情页spider主要是加载本地的列表数据文件,然后加载列表数据进行详情页的爬取。
class DetailSpider(scrapy.Spider):
domain = "https://www.XXXXXXX"
name = 'dmsp'
def start_requests(self):
curr_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
file_path = os.path.join(curr_dir, 'pages/allUrl.txt')
start_urls = []
with open(file_path, 'r') as f:
urls = f.readlines()
for url in urls:
start_urls.append(url[0:-1])
currUrl = start_urls[22]
yield SplashRequest(url=currUrl, callback=self.parseList, args={'wait': 10},endpoint='render.html')
def parseList(self,response):
# logging.info("parseList response: %s ", response.text)
urlSel = response.xpath('//div[@class="co_content8"]/ul//a').getall()
detailUrls = []
for url in urlSel:
obj = {}
obj['url'] = self.domain + Selector(text=url).xpath('//@href').get()
obj['title'] = Selector(text=url).xpath('//text()').get()
detailUrls.append(obj)
logger.info("detailUrls: %s ",detailUrls)
for detailUrl in detailUrls:
logger.info("当前url: %s",detailUrl['url'])
yield SplashRequest(url=detailUrl['url'], callback=self.parseDetail,args={'wait': 10},endpoint='render.html')
# yield SplashRequest(url=detailUrls[0]['url'], callback=self.parseDetail,args={'wait': 10},endpoint='render.html')
def parseDetail(self,response):
image_urls = []
title = response.xpath('//div[@class="title_all"]//h1/text()').get()
ul = response.xpath("//div[@class='co_content8']//ul")
cover = ul.xpath("//div[@id='Zoom']/img[1]/@src").get()
screenshots = ul.xpath("//div[@id='Zoom']/img[2]/@src").get()
image_urls.append(cover)
image_urls.append(screenshots)
zoom = ul.xpath(".//div[@id='Zoom']/text()").getall()
magnet = response.xpath("//div[@id='downlist']//a/text()").get()
_list = [x.strip() for x in zoom ]
_list1 = [x for x in _list if x != '' ]
_list2 = [x.replace('\u3000','aaa') for x in _list1]
logging.info("_list2: %s ", _list2)
item = MyspiderItem()
item['image_urls'] = image_urls
item['title'] = title #标题
import datetime
item['id_uuid'] = genUUid()
item['createtime'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") #更新时间
_protagonist_index = 0
_introduction_index = 0
for _item in _list2:
if _item.find('◎译') != -1:
item['movie_name_zh'] = self.splitList(_item)
elif _item.find('◎片aaaaaa名') != -1:
item['movie_name_en'] = self.splitList(_item)
elif _item.find('◎年') != -1:
item['movie_year'] = self.splitList(_item)
elif _item.find('◎产') != -1:
item['movie_places'] = self.splitList(_item)
elif _item.find('◎类') != -1:
item['movie_type'] = self.splitList(_item)
elif _item.find('◎语') != -1:
item['movie_lanage'] = self.splitList(_item)
elif _item.find('◎字') != -1:
item['movie_subtitle'] = self.splitList(_item)
elif _item.find('◎上映日期') != -1:
item['release_date'] = self.splitList(_item)
elif _item.find('◎文件格式') != -1:
item['file_form'] = self.splitList(_item)
elif _item.find('◎视频尺寸') != -1:
item['video_size'] = self.splitList(_item)
elif _item.find('◎文件大小') != -1:
item['file_size'] = self.splitList(_item)
elif _item.find('◎片aaaaaa长') != -1:
item['movie_time'] = self.splitList(_item)
elif _item.find('◎导') != -1:
item['direct'] = self.splitList(_item)
elif _item.find('◎主') != -1:
item['protagonist'] = self.splitList(_item)
_protagonist_index = _list2.index(_item)
elif _item.find('◎简') != -1:
_introduction_index = _list2.index(_item)
item['isDownload'] = 0
protagonist = item['protagonist']
for po in _list2[_protagonist_index+1:_introduction_index]:
protagonist += "," + po
item['protagonist'] = protagonist
item['introduction'] = _list2[_introduction_index+1]
item['downloadUrl'] = magnet
logger.info("item: %s ", item)
yield item
def splitList(self,item):
return item.split("aaa")[-1]
由于此网站做了反爬机制,导致爬取的过程中偶尔会只返回一个js文件,所以这里老夫在docker容器里面跑了一个Splash镜像,实际上爬虫访问的页面是Splash返回的页面。