python抓取豆瓣电影

本文介绍了作者使用Python进行豆瓣电影数据抓取的实践过程,包括数据库选择MySQL,模仿Webmagic框架设计爬虫程序,以及未来可能的改进方向,如面向对象和多线程优化。
摘要由CSDN通过智能技术生成

原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

由于各种原因,可能存在诸多不足,欢迎斧正!

       工作将近半年,有一个很明显的感觉:工作很多时候不像单纯学习那样要求你掌握得多深而是要求知识面比较宽广,很多东西都要会些,至少业务是这样。当规模比较小的时候,谈性能就是耍流氓。目前工作一方面是抓取:抓取竞对数据、自动搬单、覆盖。抓取竞对相对简单,就是抓取数据帮助分析、决策;自动搬单是指将用户在本网站下的单转化成其他系统的订单 ,主要是将用户在本网站提交的参数传递到其他网站下单;覆盖是指当本网站产品数量不全时,通过点击跳转到其他网站的方式简单的扩大覆盖,提高用户体验。爬虫有着自己的技术难点,比如调度策略、url判重、应对防抓取措施(如IP封锁)等 。很多时候,感觉缺少的是工具,比如语言、框架等掌握和使用,正好上学比较熟悉C/C++,加之工作后经过公司2个月的培训,掌握了基本的Java web开发技巧,前面说的爬虫就是基于Webmagic框架编写的(Webmagic适合做垂直爬虫,个性化定制,持久化、下载页面、任务调度等都可以按业务需要自己制定编写),不过关键部分已经被公司的大神搭建好了,其他方面除了抓取网页需要看了些CSS、HTML、JavaScript等前端知识(主要也是看一些在线视频教程,虽说看的不深,但是可能快速整我技能,简单的问题也能独立解决),而提及爬虫,网上最多的就是python。python是基于C语言开发的脚本语言,提供了大量实用而简单的API     ,而且有着活跃的网络社区。所以想用python换个方式写点爬虫程序,而本次选择的抓取网站,就是一直好评颇高的豆瓣电影,这次也附庸风雅,抓取豆瓣电影。其实,我的初心是利用豆瓣的评分,然后调用百度API自动抓取网络上的最新电影,避免每次新片上映后屌丝般地在网上搜索,时间消耗过多,还容易浏览不良网站误导青少年。

     在网页抓取方面,python提供了一组丰富的API,可以解决大部分基本的爬虫问题。


一、数据库

     数据库选择目前比较通用的开源mysql,安装方便并且性能较好,关键是不花钱而且正在使用(当然,PostgreSQL也正在使用,工作的爬虫就是基于PostgreSQL数据库原型的),毕竟在天朝去IOE过程中LAMP框架比较好,不过为了换一套环境,本次脱离工作的Linux环境用的Windows开发,HeidiSQL客户端实现可视化操作数据库(用惯了Ubuntu终端命令感觉界面上很多操作受限)。其实目前一张movie表就足够了,但是后续的想法是逐渐开放注册,所以加了个user表。当然,随着后续的推进,有一天我可能也会觉得这个数据库设计弱爆了,更别说看到这篇文章的大神了,欢迎提意见啊,请喝咖啡。


CREATE TABLE `user` (
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
	`user_name` VARCHAR(50) NOT NULL COMMENT '用户名',
	`password` VARCHAR(50) NOT NULL COMMENT '用户密码',
	PRIMARY KEY (`id`)
)
COMMENT='用户表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
CREATE TABLE `movie` (
	`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
	`score` DOUBLE NULL DEFAULT '0' COMMENT '评分',
	`crawler_origin` VARCHAR(50) NULL DEFAULT '0' COMMENT '爬虫来源',
	`movie_name` VARCHAR(50) NULL DEFAULT '0' COMMENT '电影名称',
	`category` VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '分类',
	`link` VARCHAR(1024) NULL DEFAULT '0' COMMENT '爬虫url',
	`country` VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '所述国度',
	PRIMARY KEY (`id`)
)
COMMENT='电影表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1024
;


二、爬虫原理    

   本次爬虫的程序设计方面,大体模仿Webmagic框架,下面是一张经典的Webmagic框架图,图片摘自原图


       由上图可以看出,一个完整而清晰的爬虫程序spider大致分为上面4个部分:任务调度、网页下载、内容抽取、数据持久化,分别对应上图中的Scheduler、DownLoader、PageProcessor、Pipeline。对于垂直爬虫,主要的工作在于PageProcessor和Pipeline,而通用爬虫可能难点更多的集中在Scheduler和PageProcessor。


三、程序设计

     本次代码只是单线程调度的,时间充足的话后续将会改成多线程的:要么单个线程分别执行整个spider,要么单个线程执行spider四个模块中的一个或几个,这样提高程序的并发度,加快抓取速度,充分利用计算和IO资源。不过感觉豆瓣电影频道防抓取策略确实比较糟糕,本地运行程序居然也能抓到几w+的数据。下面上代码,首先是一幅图,其中的GlobalVariable包存在的原因是本人还没有足够了解python,不会python面向对象设计,所以很多参数不方便在不同模块之间传递,此外站在C语言的角度,基于全局变量的面向过程程序设计一般比高度抽象封装的面向对象程序设计性能要好,可能是大学那会搞OJ刷题的缘故,居然有这种在软件开发领域不受待见的想法。


  python选用的是2.7版本,比较成熟,IDE是JetBrains PyCharm Community Edition,之前没接触过python开发,所以在知乎上找了下然后随便下载了个,个人感觉使用IDE比完全不使用开发速度要快,也是,下面这段程序边学边写,也就一天功夫。当然,在大神们眼里,这种入门级代码也要一天,确实贻笑大方了。

1.1、全局变量:GlobalVariable.py 

# encoding:utf-8
__author__ = 'xujinxj'
import Queue
import sys

reload(sys)
sys.setdefaultencoding('utf-8')
SHARE_Q = Queue.Queue()
START_URL = "http://movie.douban.com/j/search_tags?type=movie"
LIST_PER_TAG_URL = "http://movie.douban.com/j/search_subjects?type=movie&" \
                   "tag={tag_name}&sort=recommend&page_limit={page_limit}&page_start={page_start}"


1.2、 任务调度:Scheduler.py
# encoding:utf-8
__author__ = 'xujinxj'
import time

import Downloader
import GlobalVariable


def scheduler():
    while not GlobalVariable.SHARE_Q.empty():
        url = GlobalVariable.SHARE_Q.get()
        Downloader.get_page(url)
        time.sleep(1)


1.3、 网页下载:Downloader .py
# encoding:utf-8
__author__ = 'xujinxj'
import urllib2

import PageProcessor


def get_page(url):
    try:
        my_page = urllib2.urlopen(url).read().decode("utf-8")
    except urllib2.URLError, e:
        if hasattr(e, "code"):
            print "The server couldn't fulfill the request."
            print "Error code: %s" % e.code
        elif hasattr(e, "reason"):
            print "We failed to reach a server. Please check your url and read the Reason"
            print "Reason: %s" % e.reason
    PageProcessor.pipeline_processor(my_page, url)


1.4、 内容抽取:PageProcessor .py

# encoding:utf-8
__author__ = 'xujinxj'
import json

import GlobalVariable
import Pipeline


def pipeline_processor(my_page, url):
    if GlobalVariable.START_URL in url:
        pipeline_tag(my_page)
    elif "http://movie.douban.com/j/search_subjects?type=movie&" in url:
        pipeline_list(my_page, url)


def pipeline_tag(my_page):
    tags = json.loads(my_page, encoding='utf-8')
    for key in tags["tags"]:
        url = GlobalVariable.LIST_PER_TAG_URL.format(tag_name=key, sort="recommend", page_limit=20, page_start=0)
        GlobalVariable.SHARE_Q.put(url)


def pipeline_list(my_page, url):
    subjects = json.loads(my_page, encoding='utf-8')
    for key in subjects["subjects"]:
        title = key["title"]
        turl = key["url"]
        cover = key["cover"]
        is_beetle_subject = key["is_beetle_subject"]
        is_new = key["is_new"]
        rate = key["rate"]
        cover_x = key["cover_x"]
        cover_y = key["cover_y"]
        playable = key["playable"]
        id = key["id"]

        print id, title, turl, cover, is_beetle_subject, is_new, rate, cover_x, cover_y, playable
        Pipeline.insert(rate, "豆瓣爬虫", title, "", turl)

    index = url.index('page_start=')
    length = len('page_start=')
    start = int(url[index + length - len(url):]) + 20
    if len(subjects["subjects"]) >= 20:
        url = url[0:index + length] + str(start)
        GlobalVariable.SHARE_Q.put(url)



1.5、 数据持久化:Pipeline .py
<pre name="code" class="python"># encoding:utf-8
__author__ = 'xujinxj'
import mysql.connector


def insert(score, crawler_origin, movie_name, category, link):
    try:
        conn = mysql.connector.connect(host='localhost', user='root', passwd='199203211410xfcy', port=3306,
                                       database='crawler')
        cur = conn.cursor()

        cur.execute("select * from movie where movie_name='%s'", movie_name)
        if cur.fetchone() is None:
            params = (score, crawler_origin, movie_name, category, link)
            cur.execute('insert into movie(score,crawler_origin,movie_name,category,link) values(%s,%s,%s,%s,%s)',
                        params)
            conn.commit()
        else:
            print '存在重复影片'
        cur.close()
        conn.close()

    except mysql.connector.Error, e:
        print "Mysql Error %d: %s" % (e.args[0], e.args[1])


if __name__ == '__main__':
    insert("3.4", "豆瓣爬虫", "暗杀", "动作", "sdkfh ")


 

1.6、爬虫框架:Spider.py

# encoding:utf-8
__author__ = 'xujinxj'
import GlobalVariable
import Scheduler


def main():
    GlobalVariable.SHARE_Q.put(GlobalVariable.START_URL)
    Scheduler.scheduler()
    print "Spider Successful!!!"


if __name__ == '__main__':
    main()


github地址:crawler


      上面代码基本可以完成豆瓣电影的抓取,不过由于没注意设计加之抽象能力不够,代码之间的耦合度还是比较高的,后续工作(可能没机会接触这些)和自己学习的过程中,会有意识的增强抽象能力,多过设计多抽象,好的抽象设计在程序设计中并不是花哨子,确实在分模块多版本协调开发中起到重要的作用。


       后续对于这个爬虫的基本想法有以下几点:

1、代码:掌握好python后将程序改成面向对象和多线程的,同时进一步解耦。

2、设计:采用MVC思想,使爬虫核心程序高度独立于具体抓取的页面(可能目前只想单纯地拿豆瓣电影练手,所以谈不上进一步处理核心爬虫程序)

3、用途:租个小型云服务器(据说国外的比国内的如阿里云要便宜),租代理ip应对可能的封ip策略,调用第三方服务破解可能的验证码等防抓取策略,最终达到在豆瓣上获取高分电影,然后在其他网页抓取下载或者提供下载的链接,脱离新片上映后在网上不好找的窘态,当然,如果成熟后也会考虑开发注册权,提供给读这篇学习笔记的同学。


      路漫漫其修远兮,很多时候感觉想法比较幼稚。首先东西比较简单,其次工作也比较忙,还好周末可以抽时间处理这个。由于相关知识积累有限,欢迎大家提意见斧正,在此表示感谢!后续版本会持续更新…




















      



















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值