Scrapy 爬取 豆瓣电影的短评

来源:互联网 发布:手机淘宝首页装修布局 编辑:程序博客网 时间:2024/04/30 06:19

之前爬取电影信息的时候,将电影短评的url一并存起来了。
因此爬取电影短评的时候只需将数据库中存在的url 放入start_urls中就好了。

spider.py

# -*- coding: utf-8 -*-from scrapy.selector import Selectorfrom scrapy.spiders import Spiderfrom scrapy.http import Request ,FormRequestfrom comments.items import CommentsItemimport scrapyfrom scrapy import logimport MySQLdbclass CommentSpider(Spider):    name = "comments"    #allowed_domains=["movie.douban.com"]    db = MySQLdb.connect("localhost","root","123456","python" )    cursor = db.cursor()    #在爬取电影信息时已经将评论的链接也抓到数据库中(comment_url),  从数据中找到地址  作为 start_urls    cursor.execute("select comment_url from doubanmovie")    #data = cursor.fetchone() # 取一条    data = cursor.fetchall() #取所有    start_urls = data          def parse(self,response):        sel = Selector(text=response.body)        Url = response.url        start_index = Url.find('comments')        URL = Url[0:start_index+8]        ID = filter(str.isdigit,URL)        comments = sel.xpath('//*[@class="comment-info"]')        for comment in comments:                     item = CommentsItem()             item['ID'] = ID            item['user_name'] = comment.xpath('a/text()').extract()            item['user_score'] = comment.xpath('span[1]/@title').extract()             yield item        for url in sel.xpath("//*[@class='next']/@href").extract():              yield Request(URL+url,callback=self.parse)   

运行的时候出错,
显示TypeError(‘Request url must be str or unicode, got %s:’ % type(url).name).
后来发现 是因为 从数据库中取数据,data是 tuple 格式。
直接 start_urls = data; 不合适。因为 在scrapy中 start_urls是List,而且 start_urls中的元素应该是string;
于是添加了一些代码:

    temp = list(data)    start_urls = []    for i in range(len(temp)):        tem = str(temp[i])        tem2 = tem[2:len(tem)-3]        start_urls.append(tem2)

这样一来,start_urls 就符合格式了。

然后就是模拟登陆了。
豆瓣爬取短评的时候,不登录只能爬取前几页。这显然是不够的。
scrapy模拟登陆可以使用
from scrapy.http import Request ,FormRequest
而且,scrapy自动处理cookie.
关于模拟登陆,文档上更详细,也有例子:
https://scrapy-chs.readthedocs.org/zh_CN/0.24/topics/request-response.html#scrapy.http.FormRequest.from_response

看 douban 登陆界面的html

 <div class="item">    <label>帐号</label>    <input id="email" name="form_email" type="text" class="basic-input"           maxlength="60" value="邮箱/手机号" tabindex="1"/>  </div>  <div class="item">    <label>密码</label>    <input id="password" name="form_password" type="password" class="basic-input" maxlength="20" tabindex="2"/>  </div>

因此

formdata={        'form_email': '账号',        'form_password': 'passward'                          },

然后发现,登陆几次后,再登陆就要输验证码。
我的解决方式是,保存验证码的url,在浏览器中打开 手动输入验证码(这样是可以) 然后和账号信息 一起提交
python PIL 可以识别验证码,用之后发现,对于豆瓣的验证码,可以说是 完全识别不出来。
这样一来 登陆之前 先看一下 有没有验证码。
如果有,就手动输入验证码,然后和账号信息一起提交
如果没有 就只发送账号信息
因此在 spider.py中添加代码:

def start_requests(self):                      yield Request("https://accounts.douban.com/login?source=movie", meta={'cookiejar':1},callback=self.post_login)    def post_login(self,response):        print 'Preparing login'               sel = Selector(response)        nodes = sel.xpath("//*[@class='captcha_image']/@src").extract()        if nodes :                       print nodes            xerf = raw_input()            return scrapy.FormRequest.from_response(                                                    response,                                                    formdata={                                                              'captcha-solution': xerf,                                                              'form_email': '账号',                                                              'form_password': 'password'                                                      },                                                    callback=self.after_login                                                    )        return scrapy.FormRequest.from_response(                response,                formdata={'form_email': '账号',                          'form_password': 'password'                          },                callback=self.after_login        )    def after_login(self,response):        #check login succeed before going on        if "authentication failed" in response.body:            self.log("login failed", level = log.ERROR)        for url in self.start_urls :            print url            yield self.make_requests_from_url(url)    #-----------

因为我用的是 spider 不是crawlspider
因此 获取后续url的时候 用了一下代码:

for url in sel.xpath("//[@class='next']/@href").extract():              yield Request(URL+url,callback=self.parse) 

这里URL 是request 中的URL
因此需要处理一下。
观察发现 豆瓣评论的url 特点
https://movie.douban.com/subject/1292052/comments?start=24&limit=20&sort=new_score
上面代码 获得的href 是 ‘?’及其后面部分。
因此URL 我们只需要’comments’及其前面的部分。
使用,python 字符串的操作。

Url = response.url        start_index = Url.find('comments')        #start_index 保存的是 comments 前一个字符的位置        URL = Url[0:start_index+8]        #将 comments 加上

综上:
爬短评的代码 就写好了:

# -*- coding: utf-8 -*-from scrapy.selector import Selectorfrom scrapy.spiders import Spiderfrom scrapy.http import Request ,FormRequestfrom comments.items import CommentsItemimport scrapyfrom scrapy import logimport MySQLdbclass CommentSpider(Spider):    name = "comments"    #allowed_domains=["movie.douban.com"]    db = MySQLdb.connect("localhost","root","123456","python" )    cursor = db.cursor()    #在爬取电影信息时已经将评论的链接也抓到数据库中(comment_url),  从数据中找到地址  作为 start_urls    cursor.execute("select comment_url from doubanmovie")    #data = cursor.fetchone()    data = cursor.fetchall()    temp = list(data)    start_urls = []    for i in range(len(temp)):        tem = str(temp[i])        tem2 = tem[2:len(tem)-3]        start_urls.append(tem2)    print start_urls    def start_requests(self):                      yield Request("https://accounts.douban.com/login?source=movie", meta={'cookiejar':1},callback=self.post_login)    def post_login(self,response):        print 'Preparing login'               sel = Selector(response)        nodes = sel.xpath("//*[@class='captcha_image']/@src").extract()        if nodes :                       print nodes            xerf = raw_input()            return scrapy.FormRequest.from_response(                                                    response,                                                    formdata={                                                              'captcha-solution': xerf,                                                              'form_email': '1491651205@qq.com',                                                              'form_password': 'wx950417'                                                      },                                                    callback=self.after_login                                                    )        return scrapy.FormRequest.from_response(                response,                formdata={'form_email': '1491651205@qq.com',                          'form_password': 'wx950417'                          },                callback=self.after_login        )    def after_login(self,response):        #check login succeed before going on        if "authentication failed" in response.body:            self.log("login failed", level = log.ERROR)        for url in self.start_urls :            print url            yield self.make_requests_from_url(url)    #-----------    def parse(self,response):        sel = Selector(text=response.body)        Url = response.url        start_index = Url.find('comments')        URL = Url[0:start_index+8]        ID = filter(str.isdigit,URL)        comments = sel.xpath('//*[@class="comment-info"]')        for comment in comments:                     item = CommentsItem()             item['ID'] = ID            item['user_name'] = comment.xpath('a/text()').extract()            item['user_score'] = comment.xpath('span[1]/@title').extract()             yield item        for url in sel.xpath("//*[@class='next']/@href").extract():              yield Request(URL+url,callback=self.parse)   

如果用CrawlSpider 的话 可以省略 URL 的操作。

本以为可以将一部电影的所有的短评都爬下来,
但运行之后发现一个问题,走到一定深度,会获取到一个访问会超时的页面,就继续不去了。但换一个页面,爬虫又能继续走,于是一部电影就只能获取一般甚至更少的评论数。
我也不知道是什么原因。似乎是豆瓣的原因,,,。

0 0