最近在coursera.org(在线学习平台)上学SNA(Social Network Analysis,社交网络分析)。有兴趣的同学可以去看一眼:https://class.coursera.org/sna-002/,课程讲的很有意思,等回头我上完全部课程打算再写下详细总结和思考。
为什么要抓取豆瓣小组数据?
课程要做一个带编程的final project,大概内容就是自己找一个网络数据集,然后按照课程中提供的方法进行分析。其实最难的部分是找数据,两种方法:自己抓,或者是找现成的。对于后者,国内有个叫数据堂的数据分享网站做的不错,上面有豆瓣,淘宝,微博等各种数据,不过有些数据需要收费;国外有个叫Konect的网站专注于复杂网络数据。
看了一圈现成的数据都不太满意,决定自己抓取了。目标锁定豆瓣小组,打算分析小组之间的互相联系。
如何用Scrapy抓取网页?
还不太了解Scrapy的同学直接看这里http://www.oschina.net/p/scrapy,这边就不赘述基本功能和安装方式。
1,先建立一个scrapy项目,项目名称doubanscrapy startproject douban
1 | scrapy startproject douban |
会生成一个项目目录,内部结构大概如下:
02 | scrapy.cfg ==> project的配置文件,一般不用改 |
05 | items.py ==> 定义抓取的数据结构 |
07 | settings.py ==》 项目的具体配置,需要更改 |
08 | spiders/ ==》 这个是定义蜘蛛机器人的目录 |
2, 在item.py中定义要抓取的数据结构:定义了一个DoubanItem,属性包括name,url, total member number, relative groups, active users。
定义完DoubanItem后,你就可以在实现蜘蛛机器人的代码里返回DoubanItem的实例,Scrapy会自动序列化并导出到JSON/XML等。
01 | from scrapy.item import Item, Field |
03 | class DoubanItem(Item): |
09 | RelativeGroups = Field() |
3, 定义一个最简单的蜘蛛机器人: 抓取豆瓣小组的首页并保存在一个文件里。我们在spiders目录下新建一个蜘蛛文件:BasicGroupSpider.py,程序内容如下:
01 | from scrapy.spider import BaseSpider |
02 | from scrapy.item import Item |
03 | from douban.items import DoubanItem |
05 | class GroupTestSpider(BaseSpider): |
07 | allowed_domains = [ "douban.com" ] |
09 | "http://www.douban.com/group/" , |
12 | def parse( self , response): |
13 | self .log( "Fetch douban homepage page: %s" % response.url) |
14 | open ( "test.data" , "wb" ).write(response.body) |
可以看到:程序扩展了BaseSpider类来创建一个自定义的蜘蛛。BaseSpider是Scrapy中自定义的最简单蜘蛛,它没有爬行功能,只抓取在start_urls里面定义的网址,并调用parse方法处理每个抓取的response。
然后运行在command line中执行如下命令,将运行上述蜘蛛机器人,并将log写到test.log中。
1 | scrapy crawl Test --logfile= test .log |
4, 接下来要抓取某一个豆瓣小组页面,例如http://www.douban.com/group/WHV/, 然后解析出小组的名称,成员总数,以及相关的友情小组和推荐小组的URL——这些信息将用于构建小组之间的连接。
为此需要引入一个包HTML解析包:
1 | from scrapy.selector import HtmlXPathSelector |
重新定义
BasicGroupSpider.py
如下:
01 | from scrapy.spider import BaseSpider |
02 | from scrapy.selector import HtmlXPathSelector |
03 | from scrapy.item import Item |
04 | from douban.items import DoubanItem |
07 | class GroupTestSpider(BaseSpider): |
09 | allowed_domains = [ "douban.com" ] |
11 | "http://www.douban.com/group/WHV/" , |
14 | def __get_id_from_group_url( self , url): |
15 | m = re.search( "^http://www.douban.com/group/([^/]+)/$" , url) |
21 | def parse( self , response): |
23 | self .log( "Fetch group home page: %s" % response.url) |
25 | hxs = HtmlXPathSelector(response) |
29 | item[ 'groupName' ] = hxs.select( '//h1/text()' ).re( "^\s+(.*)\s+$" )[ 0 ] |
32 | item[ 'groupURL' ] = response.url |
33 | groupid = self .__get_id_from_group_url(response.url) |
36 | members_url = "http://www.douban.com/group/%s/members" % groupid |
37 | members_text = hxs.select( '//a[contains(@href, "%s")]/text()' % members_url).re( "\((\d+)\)" ) |
38 | item[ 'totalNumber' ] = members_text[ 0 ] |
41 | item[ 'RelativeGroups' ] = [] |
42 | groups = hxs.select( '//div[contains(@class, "group-list-item")]' ) |
44 | url = group.select( 'div[contains(@class, "title")]/a/@href' ).extract()[ 0 ] |
45 | item[ 'RelativeGroups' ].append(url) |
为了解析抓取到的网页,parse方法做了较大改动:
- 我们使用了HtmlXPathSelector从response中建立一个dom对象hxs
- 为了得到小组名称,使用hxs.select('//h1/text()')得到h1标题的内容,然后用re("^\s+(.*)\s+$")过滤到标题的空格
- 为了得到小组的相关小组,使用hxs.select('//div[contains(@class, "group-list-item")]')得到一个小组列表,然后在for循环中select小组的URL,并append到item['RelativeGroups']数组中
运行如下command命令:
1 | scrapy crawl Test --logfile= test .log -o test .json -t json |
Scrapy会把解析后返回的item序列化为json格式并保存在test.json文件中。
到此为止完成了抓取工作的一半——蜘蛛还不能自动爬行来解析网页,下篇博客打算讲讲如何让蜘蛛在网页间爬行,以及如何操作cookie等内容。
用Scrapy抓取豆瓣小组数据(二)http://my.oschina.net/chengye/blog/124162