更新:因豆瓣版本更新,正文内代码已失效,修复后的代码放到了Github,主要变化:
- 退2保平安,改用Python 3(free style)
- 加入了随机生成User Agent(Scrapy的user agent明明白白表示自己是个机器人...这是旧代码抓不到任何数据的原因)
- 更新了rate的xpath
- 现在github repo里共有三个玩具爬虫,分别使用beautifulsoup、scrapy、selenium
下面是本文初始版,详述Python 2下的Scrapy方法:
第一次接触scrapy,想找个简单的页面尝试下,于是就抓了豆瓣电影TOP250。没什么技术含量,主要收获是了解了scrapy的基本功能、xpath以及python中Unicode的编码问题。
只想看结果的话请往后翻。
实现
items.py (scrapy)
import scrapy
class Movie250Item(scrapy.Item):
rank = scrapy.Field()
title = scrapy.Field()
link = scrapy.Field()
star = scrapy.Field()
rate = scrapy.Field()
quote = scrapy.Field()
spider_movie250.py (scrapy)
import scrapy
from movie250.items import Movie250Item
class Movie250Spider(scrapy.Spider):
"""docstring for Movie250Spider"""
name = 'movie250'
allowed_domains = ["douban.com"]
start_urls = [
"http://movie.douban.com/top250/"
]
def parse(self, response):
for info in response.xpath('//div[@class="item"]'):
item = Movie250Item()
item['rank'] = info.xpath('div[@class="pic"]/em/text()').extract()
item['title'] = info.xpath('div[@class="pic"]/a/img/@alt').extract()
item['link'] = info.xpath('div[@class="pic"]/a/@href').extract()
item['star'] = info.xpath('div[@class="info"]/div[@class="bd"]/div[@class="star"]/span/em/text()').extract()
item['rate'] = info.xpath('div[@class="info"]/div[@class="bd"]/div[@class="star"]/span/text()').extract()
item['quote'] = info.xpath('div[@class="info"]/div[@class="bd"]/p[@class="quote"]/span/text()').extract()
yield item
# 翻页
next_page = response.xpath('//span[@class="next"]/a/@href')
if next_page:
url = response.urljoin(next_page[0].extract())
yield scrapy.Request(url, self.parse)
以上代码写好后进入在Console里输入以下命令,以爬取数据并存入json文件中:
scrapy crawl movie250 -o items.json
得到的json文件采样:
[{"star": ["9.6"], "title": ["\u8096\u7533\u514b\u7684\u6551\u8d4e"], "quote": ["\u5e0c\u671b\u8ba9\u4eba\u81ea\u7531\u3002"], "rank": ["1"], "rate": ["628644\u4eba\u8bc4\u4ef7"], "link": ["http://movie.douban.com/subject/1292052/"]},
{"star": ["9.4"], "title": ["\u8fd9\u4e2a\u6740\u624b\u4e0d\u592a\u51b7"], "quote": ["\u602a\u8700\u9ecd\u548c\u5c0f\u841d\u8389\u4e0d\u5f97\u4e0d\u8bf4\u7684\u6545\u4e8b\u3002"], "rank": ["2"], "rate": ["598654\u4eba\u8bc4\u4ef7"], "link": ["http://movie.douban.com/subject/1295644/"]}]
最后自己再写一个output得到结果(主要注意编码问题,注释里已进行解释,print语句有点长Orz):
output.py
import json
def readMovieJson():
inFile = open("D:\Users\Simon\movie250\items.json",'r',0)
text = inFile.read() # text是str
movie_dict = json.loads(text) # movie_dict是list
for movie in movie_dict: # movie是dict
rank = movie["rank"][0] # rank等都是Unicode
title = movie["title"][0]
link = movie["link"][0]
star = movie["star"][0]
rate = movie["rate"][0]
if movie["quote"]:
quote = movie["quote"][0]
else: quote = "暂无".decode("utf-8")
# str和Unicode不能混用,要么将Unicode类型encode为其他编码,
要么将str类型decode为其他编码
# python的内部使用Unicode,str如“电影: ”是字节串,由Unicode
经过编码(encode)后的字节组成的
# 与下句不同的另一种组合字符串方式:print "电影: " + title.encode("utf-8")
print "top".decode("utf-8") + rank + ".".decode("utf-8") + \
title + " 评分".dec1ode("utf-8") + star + \
'('.decode("utf-8") + rate + ')'.decode("utf-8") + \
"\n链接:".decode("utf-8") + link + \
"\n豆瓣评论:".decode("utf-8") + quote + "\n"
结果
top1.肖申克的救赎 评分9.6(628644人评价)
链接:http://movie.douban.com/subject/1292052/
豆瓣评论:希望让人自由。
top2.这个杀手不太冷 评分9.4(598654人评价)
链接:http://movie.douban.com/subject/1295644/
豆瓣评论:怪蜀黍和小萝莉不得不说的故事。
top3.阿甘正传 评分9.4(528435人评价)
链接:http://movie.douban.com/subject/1292720/
豆瓣评论:一部美国近现代史。
top4.霸王别姬 评分9.4(430468人评价)
链接:http://movie.douban.com/subject/1291546/
豆瓣评论:风华绝代。
top5.美丽人生 评分9.4(291120人评价)
链接:http://movie.douban.com/subject/1292063/
豆瓣评论:最美的谎言。
(下略)