Python爬虫(一)别说话快上车:改写第一个爬虫程序
背景交代:因为准备研究生的一门选修课“可视化”期末作业,需要将几个csv文件的电影数据可视化。我想用电影海报来可视化07-11年最赚好莱坞电影总票房数据,因此开始接触爬虫,至今(2015.4.25)不到一周……将来或许一些科研项目搜集素材会用到,所以打算把学习爬虫技术作为一个长期任务,开挖新坑。
本篇的爬虫是基于Python2.7+Scrapy编写的。关于开发环境的搭建可见
http://www.cnblogs.com/txw1958/archive/2012/07/12/scrapy_installation_introduce.html
本程序的部分代码参考了
http://www.cnblogs.com/Shirlies/p/4537931.html
不知为何我的爬虫总是无法成功爬豆瓣(403错误,被屏蔽),所以改成了爬imdb。
这个文件用来定义要抓取的项目的成员
#coding=utf-8
#Define here the models for your scraped items
from scrapy.item import Item, Field
class TutorialItem(Item):
#define the fields for your item here like:
movie_name = Field()
movie_picture = Field()
这部分我没有修改,照搬的原文内容。
可能是scrapy版本的关系,使用原教程里的’HtmlXpathSelector.select()’等函数会提示过时函数的warning,并且推荐使用’Selector.xpath()’,所以做了修改。还有更多关于数据过滤的内容,写在代码注释里吧
#coding=utf-8
from scrapy.spiders import Spider
from scrapy.http import Request
from scrapy.selector import Selector #原文里的HtmlXpathSelector全部换成了Selector
from tutorial.items import TutorialItem
import re
import urllib
class ImdbSpider(Spider):
name = "imdb"
allowed_domains = ["www.imdb.com"]
start_urls = []
def start_requests(self):
file_object = open('movie_name.txt','r') #电影名存储在movie_name.txt文件里
try:
url_head = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" #imdb电影搜索的url头
for line in file_object:
self.start_urls.append(url_head + line + '&s=tt')
#&s=tt是限定搜索仅限标题,可以过滤掉电影名正好是影人名导致爬到影人照片的情况
for url in self.start_urls:
yield self.make_requests_from_url(url)
finally:
file_object.close()
def parse(self, response):
#open("test.html",'wb').write(response.body)#测试response是否正确
hxs = Selector(response)
#为了防止爬到空白图片(如缺少海报的电影或者TV Series),加一个循环,遇到空白预览图往下搜
i = 1
movie_pic = hxs.xpath('id("main")/div/div[2]/table/tr[1]/td[1]/a/img/@src').extract()
while movie_pic[0].find('nopicture') == -1:
i++
movie_pic = hxs.xpath('id("main")/div/div[2]/table/tr[%d]/td[1]/a/img/@src' % i).extract()
#如果是所有的结果都没有预览图的情况,还是要跳出循环的
if not movie_pic:
break
#open("movie_pic.txt", 'w').write(str(movie_pic))#测试海报url是否正确
#为了减少request量,这里使用了一个偷懒的方法:
#因为imdb搜索结果页的海报预览图和电影具体信息页面的海报预览图在url上只有表示尺寸值的字符串差别,
#所以这里直接替换,就不需要从@href的链接访问进去了。如果需要更大的海报,到相应的页面去找,可以发现只要把
#“UX182_CR0,0,32,44_AL_.jpg”换成“SX640_SY720_.jpg”就行了
if movie_pic:
movie_pic[0]=movie_pic[0].replace('32','182')
movie_pic[0]=movie_pic[0].replace('44','268')
#下载海报预览图
item = TutorialItem()
item['movie_picture'] = ''.join(movie_pic).strip()
movie_name_file = open('movie_name.txt','r')
try:
for line in movie_name_file:
item['movie_name'] = line.strip()
if movie_pic:
urllib.urlretrieve(movie_pic[0].strip(),'pictures/' + line.strip() + '.jpg')
finally:
movie_name_file.close()
yield item
主函数和原文类似,稍稍修改了部分,使之具有读取csv文件的功能,以及添加了一些用于数据过滤的代码
#coding=utf-8
import os
import csv
#read csv
csvfile = file('movie2011.csv','r')
reader = csv.reader(csvfile)
movie_nm = []
for line in reader:
if line[0] != '' && line[0]!='Film' && line[0]!= 'Average':
movie_nm.append(line[0])
csvfile.close()
#读取电影数据
try:
for x in movie_nm:
#数据整理
write_name = x.replace('_','+')#换掉空格,否则搜索时会丢失后面的内容
write_name = write_name.replace('\'','')
write_name = write_name.replace(':', '%3A')#片名中的冒号在url中是以%3A表示的
if write_name.find('(')!= -1:
write_name = write_name[:write_name.find('(')]#去掉括号内容,否则影响搜索结果
#print "name is :" + write_name
#把电影名写到中间文件中去,让爬虫读取。和原文机制是一样的,写一个,爬一个
movie_name_file = open('movie_name.txt','w')
try:
movie_name_file.write(write_name)
finally:
movie_name_file.close()
#该爬虫程序会从movie_name中读取电影名来爬虫
os.system(r"scrapy crawl imdb")
finally:
print "finished crawl"
爬完之后就得到了相应的海报
计算07年~11年的这些电影总票房,然后在world里截个图,作为拼接的底片。然后使用专门做拼接马赛克图的AndreaMosaic软件拼接这些海报(一共大概650张海报),就得到如下效果了:
这篇文章只是一个往容器里塞东西式改写的爬虫,没有涉及到爬虫核心的技术,更多描述在数据处理和结果过滤等方面,很浅。不过也算是一个根据本地文件爬电影海报的爬虫参考啦