几天前做了一份笔试题,需要获取若干应用的上百条评论。我在想把一页一页的评论抓取下来放到表格里呈现是不是更加方便,那就写一个吧。
配置环境: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模拟登陆。登陆后随便发送一个请求,在头部查看。