这是分解
我的第一个项目
的第一篇,如题,今天作者主要给包括作者在内一些爬虫新手们分享下抓取豆瓣影评遇到的一些坑,以及如何解决。
相信抓取过豆瓣的新手们,不难发现豆瓣的反爬虫原理:
不带cookies访问,访问过快或者达到限制,直接封ip;
带cookies访问,访问过快虽然不会封ip,但令人头疼的是,它会重定向,经常要你登录,因为后台发现你是个可能是个机器人。
作者也是个菜鸟,第一次遇到这种问题,心里瞬间炸毛了。
虽然炸毛归炸毛,为了不被反爬虫,我不得不冷静地捋了一遍,又搜索了相关教程,得到了反反爬虫的解决方案:
代理ip池,cookies池。
至于代理ip池,免费的ip有多个网站提供,有大佬写了打包程序放在github上,这好说,借用一下轮子就好。
至于cookies池,构建多个账户轮询,谁行谁上。
后来,我发现了构建cookies池,这是个坑,因为人家豆瓣不管你登录几个账号,只要你是同一个ip登录,cookies显示都是一样的!
关于查看cookies,可以尝试登录不同账号,检查响应头中的cookies即可,相信你会发现,不管几个账号,cookies都是一样的,而且cookies中貌似还有个登陆次数统计值,所以吧,构建cookies池根本不适用爬取豆瓣影评。
这是第一个坑,前前后后浪费了我至少一个星期时间(当然,由于周末写代码时间较少,这样想,有点平衡了)。
现在我想起来都经不住感叹一句,我的时间哇,效率真是低的可以!
虽然几经波折,还是被我找到网上有大佬破解了,只要随机cookies中一个key,也就是bin的值,就可以被豆瓣认为是新的cookies。不过是很久以前的文章了,我测试了一下,还是会报错,不过稍稍修改一下,竟然能用了,真是几经波折,可喜可贺。
了解了这些后,我们开启用scrapy抓取豆瓣。
首先是新建一个project。
这里,作者在webspider目录新建名为DoubanComments项目名。
接下来进入项目中,在spiders下新建douban.py,也可以输入shell指令(一定要进入项目名中输入,至少要在WebSpider中)。
这样初始化项目也就完成了。
在开始spider编写开始之前,应该先设置好你的items,也就是定义好你要保存的字段名。
修改items为如下:
接下来就是保存数据,修改pipelines.py选择mongodb为数据库保存数据,当然,你必须在settings.py中指定数据库的host,port以及数据库名dbname,作为MoviecommentsPipeline的初始化参数。
pipelines类作为整个spider中数据过滤以及数据保存的中重要的一环,除了procee_item外,还有两个重要的方法,一个是open_spider,另一个是close_spider,也就是可以在两个方法执行过程中进行我们想要的操作,例如在前者中写数据库连接和后者中写数据库关闭操作。
由于我们整个项目很可能要面临很多个需求链接,这些个需求链接由用户输入,添加给我们的spider,当一个需求完成后,就开始下载另一个需求,而两个需求必然是不同的,也就是对应于存储于mongo中表名是不同的,是非固定的。举个例子,前一个需求表名可能设置是comments_1,后一个是comments_2,也就是我们这里的表名不能写死,要写成动态的,最好的方式是从spider爬取start_urls时,就绑定一个动态的表名,我们这里只要使用方法process_item来获取这个名称就好了。
这里我们尝试获取spider绑定的属性coollection,如果没有就返回表名为comments_id_lost。
那么,整个管道就写完了。
我们再回到douban.py。
parse用来解析spider中start_urls中的urls,可以返回Request,也可以返回解析后的item,这需要我们来改写。
随便打开豆瓣的一个comments页面,我们需要获取该页面所有信息,包含用户的评论信息以及个人网页链接的一些信息,也就是分为两级页面。
作者这里提供一个继承于scrapy.Spider的子类spider版本,当然也可以考虑用crawlspider的子类来编写。
这里我们在第一级页面中返回一个Request,设置item作为传递参数,传给self.parse_info,用于下载解析第二级页面。
页面自动翻页:
解析第二级页面,接受parse中传递过来的item,将还未下载的字段数据下载完后,集体打包给管道piplines。
下载完毕后,我们还差一点,那就是动态设置mongo表名collection。
这里,作者君提供一种方法:
打X的部分可以先忽略,后面在打造豆瓣影评系统的时候会用到。
首先spider初始化一个属性former_tbname,用来存储前一个需求的表名,将新匹配到的表名赋值给spider绑定的属性名collection即可,这样就实现了mongo数据库存储动态表名。
领取专属 10元无门槛券
私享最新 技术干货