我是个微博重度用户,工作学习之余喜欢刷刷timeline看看有什么新鲜事发生,也因此认识了不少高质量的原创大V,有分享技术资料的,比如好东西传送门;有时不时给你一点人生经验的,比如石康;有高产的段子手,比如银教授;有黄图黄段子小能手,比如阿良哥哥 木木萝希木 初犬饼…
创新互联是一家从事企业网站建设、网站设计制作、网站制作、行业门户网站建设、网页设计制作的专业的建站公司,拥有经验丰富的网站建设工程师和网页设计人员,具备各种规模与类型网站建设的实力,在网站建设领域树立了自己独特的设计风格。自公司成立以来曾独立设计制作的站点上千家。
好吧,我承认,爬黄图黄段子才是我的真实目的,前三个是掩人耳目的…(捂脸,跑开)
另外说点题外话,我一开始想使用Sina Weibo API来获取微博内容,但后来发现新浪微博的API限制实在太多,大家感受一下:
只能获取当前授权的用户(就是自己),而且只能返回最新的5条,WTF!
所以果断放弃掉这条路,改为『生爬』,因为PC端的微博是Ajax的动态加载,爬取起来有些困难,我果断知难而退,改为对移动端的微博进行爬取,因为移动端的微博可以通过分页爬取的方式来一次性爬取所有微博内容,这样工作就简化了不少。
最后实现的功能:
输入要爬取的微博用户的user_id,获得该用户的所有微博
文字内容保存到以%user_id命名文本文件中,所有高清原图保存在weibo_image文件夹中
具体操作:
首先我们要获得自己的cookie,这里只说chrome的获取方法。
用chrome打开新浪微博移动端
option+command+i调出开发者工具
点开Network,将Preserve log选项选中
输入账号密码,登录新浪微博
找到m.weibo.cn-Headers-Cookie,把cookie复制到代码中的#your cookie处
cookie
然后再获取你想爬取的用户的user_id,这个我不用多说啥了吧,点开用户主页,地址栏里面那个号码就是user_id
将python代码保存到weibo_spider.py文件中
定位到当前目录下后,命令行执行python weibo_spider.py user_id
当然如果你忘记在后面加user_id,执行的时候命令行也会提示你输入
最后执行结束
iTerm
小问题:在我的测试中,有的时候会出现图片下载失败的问题,具体原因还不是很清楚,可能是网速问题,因为我宿舍的网速实在太不稳定了,当然也有可能是别的问题,所以在程序根目录下面,我还生成了一个userid_imageurls的文本文件,里面存储了爬取的所有图片的下载链接,如果出现大片的图片下载失败,可以将该链接群一股脑导进迅雷等下载工具进行下载。
另外,我的系统是OSX EI Capitan10.11.2,Python的版本是2.7,依赖库用sudo pip install XXXX就可以安装,具体配置问题可以自行stackoverflow,这里就不展开讲了。
下面我就给出实现代码(严肃脸)
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#-*-coding:utf8-*-
import re
import string
import sys
import os
import urllib
import urllib2
from bs4 import BeautifulSoup
import requests
from lxml import etree
reload(sys)
sys.setdefaultencoding('utf-8')
if(len(sys.argv) =2):
user_id = (int)(sys.argv[1])
else:
user_id = (int)(raw_input(u"请输入user_id: "))
cookie = {"Cookie": "#your cookie"}
url = 'd?filter=1page=1'%user_id
html = requests.get(url, cookies = cookie).content
selector = etree.HTML(html)
pageNum = (int)(selector.xpath('//input[@name="mp"]')[0].attrib['value'])
result = ""
urllist_set = set()
word_count = 1
image_count = 1
print u'爬虫准备就绪...'
for page in range(1,pageNum+1):
#获取lxml页面
url = 'hu/%d?filter=1page=%d'%(user_id,page)
lxml = requests.get(url, cookies = cookie).content
#文字爬取
selector = etree.HTML(lxml)
content = selector.xpath('//span[@class="ctt"]')
for each in content:
text = each.xpath('string(.)')
if word_count = 4:
text = "%d :"%(word_count-3) +text+"\n\n"
else :
text = text+"\n\n"
result = result + text
word_count += 1
#图片爬取
soup = BeautifulSoup(lxml, "lxml")
urllist = soup.find_all('a',href=re.compile(r'^mblog/oripic',re.I))
first = 0
for imgurl in urllist:
urllist_set.add(requests.get(imgurl['href'], cookies = cookie).url)
image_count +=1
fo = open("/Users/Personals/%s"%user_id, "wb")
fo.write(result)
word_path=os.getcwd()+'/%d'%user_id
print u'文字微博爬取完毕'
link = ""
fo2 = open("/Users/Personals/%s_imageurls"%user_id, "wb")
for eachlink in urllist_set:
link = link + eachlink +"\n"
fo2.write(link)
print u'图片链接爬取完毕'
if not urllist_set:
print u'该页面中不存在图片'
else:
#下载图片,保存在当前目录的pythonimg文件夹下
image_path=os.getcwd()+'/weibo_image'
if os.path.exists(image_path) is False:
os.mkdir(image_path)
x=1
for imgurl in urllist_set:
temp= image_path + '/%s.jpg' % x
print u'正在下载第%s张图片' % x
try:
urllib.urlretrieve(urllib2.urlopen(imgurl).geturl(),temp)
except:
print u"该图片下载失败:%s"%imgurl
x+=1
print u'原创微博爬取完毕,共%d条,保存路径%s'%(word_count-4,word_path)
print u'微博图片爬取完毕,共%d张,保存路径%s'%(image_count-1,image_path)
先上结论,通过公开的api如果想爬到某大v的所有数据,需要满足以下两个条件:
1、在你的爬虫开始运行时,该大v的所有微博发布量没有超过回溯查询的上限,新浪是2000,twitter是3200。
2、爬虫程序必须不间断运行。
新浪微博的api基本完全照搬twitter,其中接口的参数特性与底层的NoSQL密不可分,建议先看点Nosql数据库的设计理念有助于更好的理解api设计。
一般来说,如果决定爬某个大v,第一步先试获取该用户的基本信息,中间会包含一条最新的status,记下其中的id号作为基准,命名为baseId。
接口中最重要的两个参数:
since_id:返回ID比since_id大的微博(即比since_id时间晚的微博),默认为0。
max_id:返回ID小于或等于max_id的微博,默认为0。
出于各种原因,获取statuses的接口,固定为按id降序排列(scan_index_forward=false),即最新的statuses返回在前。假设该微博第一天上线,就一个用户,发了一百条,id是1到100。而你在该用户发了第50条的时候开始运行的爬虫,即baseId=50。
在java项目广泛的使用。它是一个开源的、设计于提高在数据从RDBMS中取出来的高花费、高延迟采取的一种缓存方案。正因为Ehcache具有健壮性(基于java开发)、被认证(具有apache 2.0 license)、充满特色(稍后会详细介绍),所以被用于大型复杂分布式web application的各个节点中。
1. 够快
Ehcache的发行有一段时长了,经过几年的努力和不计其数的性能测试,Ehcache终被设计于large, high concurrency systems.
2. 够简单
开发者提供的接口非常简单明了,从Ehcache的搭建到运用运行仅仅需要的是你宝贵的几分钟。其实很多开发者都不知道自己用在用Ehcache,Ehcache被广泛的运用于其他的开源项目
比如:hibernate
3.够袖珍
关于这点的特性,官方给了一个很可爱的名字small foot print ,一般Ehcache的发布版本不会到2M,V 2.2.3 才 668KB。
4. 够轻量
核心程序仅仅依赖slf4j这一个包,没有之一!
5.好扩展
Ehcache提供了对大数据的内存和硬盘的存储,最近版本允许多实例、保存对象高灵活性、提供LRU、LFU、FIFO淘汰算法,基础属性支持热配置、支持的插件多
6.监听器
缓存管理器监听器 (CacheManagerListener)和 缓存监听器(CacheEvenListener),做一些统计或数据一致性广播挺好用的
如何使用?
够简单就是Ehcache的一大特色,自然用起来just so easy!
redis
redis是在memcache之后编写的,大家经常把这两者做比较,如果说它是个key-value store 的话但是它具有丰富的数据类型,我想暂时把它叫做缓存数据流中心,就像现在物流中心那样,order、package、store、classification、distribute、end。现在还很流行的LAMP PHP架构 不知道和 redis+mysql 或者 redis + mongodb的性能比较(听群里的人说mongodb分片不稳定)。
先说说reidis的特性
1. 支持持久化
redis的本地持久化支持两种方式:RDB和AOF。RDB 在redis.conf配置文件里配置持久化触发器,AOF指的是redis没增加一条记录都会保存到持久化文件中(保存的是这条记录的生成命令),如果不是用redis做DB用的话还会不要开AOF ,数据太庞大了,重启恢复的时候是一个巨大的工程!
2.丰富的数据类型
redis 支持 String 、Lists、sets、sorted sets、hashes 多种数据类型,新浪微博会使用redis做nosql主要也是它具有这些类型,时间排序、职能排序、我的微博、发给我的这些功能List 和 sorted set 的强大操作功能息息相关
3.高性能
这点跟memcache很想象,内存操作的级别是毫秒级的比硬盘操作秒级操作自然高效不少,较少了磁头寻道、数据读取、页面交换这些高开销的操作!这也是NOSQL冒出来的原因吧,应该是高性能
是基于RDBMS的衍生产品,虽然RDBMS也具有缓存结构,但是始终在app层面不是我们想要的那么操控的。
4.replication
redis提供主从复制方案,跟mysql一样增量复制而且复制的实现都很相似,这个复制跟AOF有点类似复制的是新增记录命令,主库新增记录将新增脚本发送给从库,从库根据脚本生成记录,这个过程非常快,就看网络了,一般主从都是在同一个局域网,所以可以说redis的主从近似及时同步,同事它还支持一主多从,动态添加从库,从库数量没有限制。 主从库搭建,我觉得还是采用网状模式,如果使用链式(master-slave-slave-slave-slave·····)如果第一个slave出现宕机重启,首先从master 接收 数据恢复脚本,这个是阻塞的,如果主库数据几TB的情况恢复过程得花上一段时间,在这个过程中其他的slave就无法和主库同步了。
5.更新快
这点好像从我接触到redis到目前为止 已经发了大版本就4个,小版本没算过。redis作者是个非常积极的人,无论是邮件提问还是论坛发帖,他都能及时耐心的为你解答,维护度很高。有人维护的话,让我们用的也省心和放心。目前作者对redis 的主导开发方向是redis的集群方向。
对于第一个问题,设计一个schema-(messageID,likedCount),记录每条微博的点赞数。messageID是微博的编号,likedCount是该微博的点赞人数。但是这里有两个问题需要解决,第一是并发,第二是数据量。
每条微博都有可能有很多人同时点赞,为了保证点赞人数精确就需要保证likedCount++是原子操作,这个可以由应用程序来实现,也可以用redis的事务来实现(如果redis有事务机制或者自增功能的话),但是我觉得为了性能考虑,也可以不用实现原子操作,具体原因就不展开了。
每天都上亿可能更多的微博内容产生,这样就会有上亿个新的(messageID,likedCount)生成,这样的数据量是比较大的,单机数据库比较难提供高效的服务,所以需要采取sharding的功能(有时候也叫分表分库),可能根据messageID把这些schema分散到十个或者更多的shards上(据说,sina微博有600个节点,如何三个节点组成一个shard,就有200个shards),这样每个shard处理的请求就只有原来的十分之一,从而就能提高服务的性能。
关于点赞人列表的设计,一般来说,可能想到的schema是(messageID,userID),但是这样的设计有一个小问题,就是有些大发的微博可能会得到几十万的点赞,这样就会产生几十万个条数据,这样数据有点多,读取起来可能也慢。所以可以用这样一个schema(messageID,partID,userIDs),让一个messageID对于多个userID,同时比对应太多的userID,所以加入一个新的partID,一个part存1000个userID,这样几十万个点赞,只需要存几百条数据。这样做还有一个好处,用户点击查看点赞人时的,一般都不是完全显示所有点赞人,而是一批一批显示,这样可以一次只读一条数据,就可显示一批点赞用户信息。
nosql分类太多了,下面列举几个比较常见的:
Redis:非常适合需要表达时间线的web服务,例如微博
Cassandra:只有顺序写,没有随机写的设计,满足高负荷情形的性能需求
MongoDB:面向文档,擅长处理非结构化数据
Neo4J:可以快速实现基于图的计算(如果用SQL计算可能花更长的时间)
希望对你所有帮助。