综述:先介绍开发环境,在介绍从豆瓣电影上面爬取电影的电影名称,电影信息,电影剧照等信息,最终效果是以电影名称为文件夹名,文件夹包含一个txt文件和许多张剧照。如下图所示1是电影名,2是电影信息,3是各种剧照

爬虫入门案例——爬取电影信息_爬虫入门案例                   爬虫入门案例——爬取电影信息_爬虫_02

1.Python爬虫框架

比较知名框架的是Scrapy 和PySpider。

PySpider 上手更简单,操作更加简便,增加了 WEB 界面,开发效率高,集成了phantomjs,可以用来处理复杂的js渲染的页面。
Scrapy 自定义程度高,比PySpider更底层一些,适合学习研究,需要学习的相关知识多,可以自己拿来研究分布式和多线程等等。

 

 

 

所以综上所述我选择的爬虫框架是PySpider。

2.PySplider安装配置

2.1 安装pip

pip是python的包管理工具,类似RedHat里面的yum,通过pip可以快速下载安装python软件隐藏了背后复杂的下载安装的过程

下载地址:下载pip安装包

2.2 安装phantomjs

phantomjs是一个浏览器内核程序,可以在爬虫开发过程中,模拟浏览器运行js,简化js处理过程

下载地址:phantomjs官网

2.3 使用pip安装pyspider

pip install pyspider

2.4 运行pyspider

pyspider all

2.5 通过浏览器访问pyspider:http://localhost:5000

3.具体代码

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Created on 2018-11-29 15:50:57
# Project: DBDY_01

from pyspider.libs.base_handler import *
import os;

class Handler(BaseHandler):
    base_dir = "D:\\mov"
    
    crawl_config = {
    }

    @every(minutes=24 * 60)
    def on_start(self):
        self.crawl('https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%BB%8F%E5%85%B8&sort=rank&page_limit=30&page_start=0',
                    validate_cert=False, 
                    callback=self.index_page)

    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        marr = response.json["subjects"]
        //最终数据的存储路径,经常要用到所以提取在最外面,方便使用
        for m in marr:
	        mv_id=m["id"]
            self.crawl(m["url"],
                       validate_cert=False, 
                       callback=self.mv_page,
                       save=mv_id)

    @config(age=10 * 24 * 60 * 60)
    def mv_page(self, response):
        #电影名
        mv_name = response.doc("h1 > span").text().encode("gbk")
        #电影描述
        mv_desc = response.doc("#info").text().encode("gbk")
        #创建该电影的文件夹
        mv_dir = self.base_dir+"/"+mv_name
        if not os.path.exists(mv_dir):
            os.makedirs(mv_dir)
        #创建描述信息写入文件
        mv_file = open(mv_dir+"/"+mv_name+".txt","w")
        mv_file.write(mv_desc)
        mv_file.flush()
        mv_file.close()
         #爬取剧照页面
        all_photos_url = "https://movie.douban.com/subject/"+response.save+"/all_photos"
        self.crawl(all_photos_url,
                   validate_cert=False, 
                   callback=self.all_photos_page,
                   save=mv_dir)
    @config(age=10 * 24 * 60 * 60)
    def all_photos_page(self, response):
              for x in  response.doc(".article a").items():
	              if re.match("^.*\\d+/$",x.attr.href)
	            self.crawl(x.attr.href,
		                   validate_cert=False, 
		                   callback=self.img_page,
		                   save=response.save)
    
    @config(age=10 * 24 * 60 * 60)
    def img_page(self, response):
        #图片url
        img_url = response.doc(".mainphoto > img").attr.src
        img_name =img_url.split("/")[-1]
        self.crawl(img_url,
                   validate_cert=False, 
                   callback=self.down_img_page,
                   save=response.save+"\\"+img_name )
                
    @config(age=10 * 24 * 60 * 60)
    def down_img_page(self, response):
      //拿到图片数据
        img_data = response.content
         img_file = open(response.save,"wb")
        img_file.write(img_data)
        img_file.flush()
        img_file.close()

4.代码逻辑:

看首页地址(有许多电影的展示页面):

https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0

我们可以进行分析:当我们尝试改变page_limit数值变成100时,发现页面条数并没有变成100条,而只有在点击加载更多的时候才会加载出下一页,就此推测这儿应该用的是Ajax异步调用,那么我们接着从调试器查找触发源头:xhr(代表ajax),可以找到他的访问url地址:https://movie.douban.com/j/search_subjects?type=movie&tag=%E9%9F%A9%E5%9B%BD&sort=recommend&page_limit=20&page_start=0

分析:j:代表json
 

我们试着访问这个路径,发现是许多json,有subjects对象,里面有许多电影信息。

爬虫入门案例——爬取电影信息_python实战_03

我随机选择了一个json进行展示

"subjects":{   //对象
    "rate":"6.5",   //评分
    "cover_x":896,
    "title":"女警",    //电影名称
    "url":"https:\/\/movie.douban.com\/subject\/30204142\/",   //该电影的具体内容
    "playable":false,   //是否可以播放
    "cover":"https://img3.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2553964373.webp", //电影的展示图片
    "id":"30204142",    //电影编号
    "cover_y":1280,
    "is_new":true   //是否是一个新电影
}

根据以上分析我们得到结论:这个url:https://movie.douban.com/j/search_subjects?type=movie&tag=%E9%9F%A9%E5%9B%BD&sort=recommend&page_limit=20&page_start=0,可以作为我们爬取数据的一个入口

开始写下面代码:

@every(minutes=24 * 60)
    def on_start(self):
        self.crawl('https://movie.douban.com/j/search_subjects?type=movie&tag=%E9%9F%A9%E5%9B%BD&sort=recommend&page_limit=20&page_start=0'
                   ,validate_cert=False
                   , callback=self.index_page)

on_start(self):开始入口

self.crawl('url' ,validate_cert=False , callback=self.index_page)

然后开始爬去这个地址的信息:self.crawl('地址')  

callback=self.index_page  ——爬完地址触发回调方法到index_page 方法

validate_cert=False :忽略安全校验,

我们会发现后面就有一个def index_page(self, response) 这个方法,这个方法的response就是对刚刚地址爬取完的响应对象

我们可以只在里面写一个pass,来执行一下我们写的程序,判断是否正确,运行结果:出现我们想要的所有json,说明访问时成功的,接着我们开始自己的业务需求,具体代码如下:

@config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        marr = response.json["subjects"]
        for m in marr:
            self.crawl(m["url"],validate_cert=False, callback=self.mv_page)

 

 response.json["subjects"]:拿到这个subjects对象所有的json,得到一个数组,遍历这个数组,拿到的是一个对象,这个对象就是我们的每一个电影的json,此时可以拿到这个对象的url属性,-url:https:\/\/movie.douban.com\/subject\/30204142\/,进行爬取数据,我们试着访问这个url:

爬虫入门案例——爬取电影信息_python实战_04

 

我们可以发现这个地址就是某一个电影的主页

然后开始爬取这个地址,回调:def mv_page(self, response)方法

@config(age=10 * 24 * 60 * 60)
    def mv_page(self, response):
        #电影名
        mv_name = response.doc("h1 > span").text().encode("gbk")
        #电影描述
        mv_desc = response.doc("#info").text().encode("gbk")
        #创建该电影的文件夹
        mv_dir = self.base_dir+"/"+mv_name
        if not os.path.exists(mv_dir):
            os.makedirs(mv_dir)
        #创建描述信息写入文件
        mv_file = open(mv_dir+"/"+mv_name+".txt","w")
        mv_file.write(mv_desc)
        mv_file.flush()
        mv_file.close()
         #爬取剧照页面
        all_photos_url = "https://movie.douban.com/subject/"+response.save+"/all_photos"
        self.crawl(all_photos_url,
                   validate_cert=False, 
                   callback=self.all_photos_page,
                   save=mv_dir)

在def mv_page(self, response)方法:需要在当前的页面元素删选出我们想要的电影名称,电影信息

response.doc()-可以接收css选择器,通过选择器来查找自己需要的元素(在编辑页面:有enable css selector helper,点击就可以选择自己想要元素对应的css)

.text().encode("gbk")  在得到容器后需要拿到里面内容,调用.text,window版本默认是gbk的,不调用会报错,乱码

根据以上内容我们就可以得到自己想要的元素,之后把需要内容就行存储在本地。

自己在本地建立一个文件夹,将路径写在:base_dir,这个路径经常会用到,所以我们把它提取到外面,来进行引用。

至此,我们的目标已完成2/3,只剩下剧照没有爬取。

爬取剧照:发现它的css选择器嵌套在span标签,无法单独根据css标签拿到剧照路径,下一步应该在调试器获取这个地址,进行删选

全部剧照的地址:<a href="https://movie.douban.com/subject/30204142/all_photos">图片52</a>

我们分析这个url发现:它的标红是电影编号,那么我们可以根据电影编号自己拼出这个地址

我们可以在index_page方法得到的电影编号,而使用它mv_page这个方法,他的传递用的是:

现在index_page方法拿到id,在回调函数中补充:save=mv_id,那么在mv_page这个方法就可以用response.save获取id

在获取到id后,我们就可以将其拼接在地址,从而进行爬取,触发回调方法:all_photos_page

进行测试,我们确实可以爬取到下图信息

爬虫入门案例——爬取电影信息_经验分享_05

我们来分析这个网页,当我们点开全部剧照图片时,发现我们不是要这些小图片,而是点开每个图片的大图片,而我们想要的图片是被一个超链接给包起来了,所以我们需要得到这个超链接并且进行删选

这个url找不到它对应的css选择器,所以我们可以稍微往上一级走一点,拿到所有url后,我们进行遍历(.items),此时拿到所有的连接,但是我们仔细观察,发现并不是所有的连接格式都正确,我们只要删选最后是以数字结尾就可以,用正则进行匹配

  @config(age=10 * 24 * 60 * 60)
    def all_photos_page(self, response):
              for x in  response.doc(".article a").items():
	              if re.match("^.*\\d+/$",x.attr.href)
            self.crawl(x.attr.href,
                          validate_cert=False, 
                          callback=self.img_page,
                         save=response.save)

最后过滤好的url:https://movie.douban.com/photos/photo/2552999044/,即单个剧照的大图

现在我们的目标是:从这个页面中拿到这个图片的地址:

 @config(age=10 * 24 * 60 * 60)
    def img_page(self, response):
         #图片url
        img_url = response.doc(".mainphoto > img").attr.src
        self.crawl(img_url,
                   validate_cert=False, 
                   callback=self.down_img_page,
                  )

我们爬取这个地址后拿到的是图片的二进制,需要拿到这个图片的内容

 

@config(age=10 * 24 * 60 * 60)
    def down_img_page(self, response):
      //拿到图片数据
        img_data = response.content
        img_file = open(response.save["mv_img_path"],"wb")
        img_file.write(img_data)
        img_file.flush()
        img_file.close()

现在问题是文件存储路径:目标是存在每一个电影文件夹下面,

为了防止存储的剧照图片重名,那么原来人家的剧照名称是什么现在也必须是什么

<img src="https://img3.doubanio.com/view/photo/l/public/p2552999043.webp" width="686">

在路径的最后是图片的名称: img_name =img_url.split("/")[-1]

路径:在def mv_page写save=mv_dir,在all_photos_page用save=response.save,在img_page用save=response.save+"\\"+img_name,那么在down_img_page中路径就用response.save得到剧照的路径

 

至此全部完成,一篇教学文章花费了很久,自己操作很快,但是这篇教学就比较繁琐,不过也不负众望,搞定这篇实战案例

有什么好的想法大家可以评论