• 主页
  • 关于
    • 五斤 photo

      五斤

      '今天也要搬很多砖呢.'

    • 关于
    • Email
    • Instagram
    • Github
  • 文章
    • 所有文章
    • 所有标签
  • 相册
  • 友链
  • 订阅

Scrapy抓取应用评分信息

04 Oct 2017 | 已载入 次

花费 ~9 分钟

几天前做了一份笔试题,需要获取若干应用的上百条评论。我在想把一页一页的评论抓取下来放到表格里呈现是不是更加方便,那就写一个吧。

配置环境:mac+python2.7+scrapy1.4     >Mac下Scrapy配置教程

浏览器:Google Chrome

IDE:Pycharm

抓取网站:蝉大师

为什么是蝉大师?比较各个数据平台,发现在蝉大师获取用户评价不需要登陆,而且通过js加载的分页内容能很方便地找到接口。这样写爬虫的成本就降低了嘛。

在蝉大师里打开到豆瓣应用下的评分与评论页面,拉到底部可以看到评论详情。打开你懂的F12,点击下一页,可以在NetWork资源视图里看到刷新的页面内容,就是那个20.html。点击它可以看到内容预览,显然乱码是因为中文编码造成的。

复制它的Url地址并在新的标签页中打开。观察到它的地址是 https://www.chandashi.com/apps/reviewdetail/appId/907002334/country/cn/startDay/20170921/endDay/ 20171006/type/good/p/2/pageSize/20.html,这个地址已经和我一星期前打开的地址不一样了,但是无妨,很容易就能看懂,p分栏后的2就代表当前加载的页数。打开这个页面的源码,下面就要基于标签结构进行解析了。

<table class="table table-striped text-center table-comment">
	<tr></tr>
	<!--表头-->
	<tr class="th">
		<th class="col-xs-3 text-center">发布时间(按最新排序)</th>
		<th class="col-xs-3 text-center">评分</th>
		<th class="col-xs-6 text-center">评论内容</th>
	</tr>
	<!--内容-->
	<tr>
		<td>2017-09-29 14:31</td>
       	<td><span class="info-value rateit" data-rateit-value="5" data-rateit-ispreset="true" data-rateit-readonly="true"></span></td>
       	<td class="text-left">
			<div><span class="title">用了很多年的豆瓣,一直很安静</span><span>By 卡门卡门卡门不重复</span></div>
			<div>电影+书,是生活最好的治愈。用了很多年,这个平台给我打开了一扇很大的门。希望豆瓣越来越好,希望更纯粹一点。</div></td>
	</tr>
  	<tr>
		<td>2017-09-29 13:58</td>
       	<td><span class="info-value rateit" data-rateit-value="1" data-rateit-ispreset="true" data-rateit-readonly="true"></span></td>
       	<td class="text-left">
        	<div><span class="title">为什么随便停止三天</span><span>By 卧槽蠢哭了fudge</span></div>
			<div>没有发违法违规的内容,就给我停用三天,作为一个有责任的平台,就用机器人来鉴定,这么不人性化</div></td>
/*****************以下省略******************/

可以看到每个用户的评分信息都是被包裹在table下的一个<tr>里,代表一行。一行里又包裹了评论时间、评论内容、用户名等信息,需要注意,用户打分在第二个<td>标签的data-rateit-value属性里。

首先定义我需要的元素。打开一个新建的Scrapy项目,打开items.py,编写需要抓取的条目。

import scrapy
class CrawlItem(scrapy.Item):
    title=scrapy.Field()   	#评价标题
    time=scrapy.Field()    	#评价时间
    score=scrapy.Field()   	#评价分值
    content=scrapy.Field()	#评论内容
    pass

然后在Spider文件夹里新建myscrapy.py,爬虫的结构就在这里定义了。为爬虫指定一个名字必不可少,启动爬虫时就是根据爬虫的名称来寻找。在start_urls里定义需要爬取的页面。有时为了伪造成自然的浏览页面,我们要加上headers里定义User-Agent或在settings.py里加上USER_AGENT来伪装成浏览器环境。很多数据网站都有反爬虫策略,其中最基础的就是每秒发送请求次数的限制,因此download_delay必不可少,一般设置成1秒以上。

在parse函数里定义提取Item的路径。接触过一点前端的都会知道CSS选择器,通过元素名称或属性来获取元素。Scarpy使用的是Xpath结合CSS选择器,可以方便地考虑到一些难以用CSS定位的元素。这里就是一个很好的例子。Xpath路径既可以向子节点扩展,也可以向父节点扩展。所以selector并不一定需要指定为所有item的父节点。

import scrapy
from scrapy import Request
from scrapy.selector import Selector
from myscrapy.items import CrawlItem

class Comments(scrapy.Spider):
    name="comments"			#指定爬虫的名字
    allowed_domains = ["www.chandashi.com"]
    start_urls = [
       "https://www.chandashi.com/apps/reviewdetail/appId/907002334/country/
       cn.html/p/1.html",
       "https://www.chandashi.com/apps/reviewdetail/appId/907002334/country/
       cn.html/p/2.html"     #爬取页面的链接,这是一个星期前加载评论的Url
    ]
    download_delay = 2		#设置下载延迟为2秒,以免爬虫被ban
    
    headers = {
       'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36'
    }						

    def parse(self, response):
        item = CrawlItem()
        selector = scrapy.Selector(response)
        apps = selector.xpath('//td[@class="text-left"]')
        for app in apps:
            item['title'] = app.xpath(
                './/span[@class="title"]/text()').extract()[0]
            item['time'] = app.xpath(
                'ancestor::tr/td[1]/text()').extract()[0].strip()
            item['score'] = app.xpath(
                'ancestor::tr/td[2]/span[@class="info-value rateit"]/@data-rateit-value').extract()[0]
            item['content'] = app.xpath(
                'div[2]/text()').extract()[0].strip('\n')
            yield item

Xpath很好学,差不多看半个钟头就可以上手了。到这里就算写完一个入门级小爬虫了。需要注意在Pycharm下编译,需要指定工程文件夹为根目录。然后在终端进入爬虫项目所在的路径,输入scrapy crawl comments -o comments.csv ,爬虫就可以运行,并将结果保存为csv文件。

csv文件直接打开会存在中文乱码问题,所以要在excel内导入数据并设置字符编码为Unicode UTF8。还有一个问题, 就是导入csv是以英文逗号换行符为分隔符,所以在导入前要检查用户评论里是否携带了换行符,否则导入的格式会混乱。数据量不算太大时可以手工删除,或者干脆在爬虫里检测换行符并用其他字符替换。

以上就完成了从蝉大师抓取评论的部分,并没有什么棘手的问题。如果说要从蝉大师抓取App排行榜前1000名的信息,那坑就稍多了:

首先,查看蝉大师的应用排行榜的源码,只有前200名,后面的应用信息都是通过js异步加载出来的。查看源码后,发现js服务端渲染经过代码混淆,基本是没办法直接抓包拿到接口的,所以要先获取js渲染完成后的页面,再进行解析。可用的框架有PhantomJS。

其次一个小问题,要回看几个月以前的排名,需要提供Cookie模拟登陆。登陆后随便发送一个请求,在头部查看。



Python 分享 Tweet +1