Python爬虫——新浪微博(网页版)

发表时间:2018-02-28

最近事情比较多,所以从上周就开始写的新浪微博爬虫一直拖到了现在,不过不得不说新浪微博的反扒,我只想说我真的服气了。

爬取数据前的准备

向右奔跑老大说这次的就不限制要爬取哪些内容了,但是给一个参考,有兴趣的可以搞一搞:

当我看到这个的时候感觉很有意思和搞头就想去整一整,所以我的一个想法就是去找一个粉丝比较多的人去解析他的分析信息,然后再去解析他粉丝的粉丝,以此类推(感觉解析初始用户的关注的人的粉丝会更好一点,因为他的粉丝比较多,他关注的人粉丝量肯定不会小),但是到后来我就想放弃这个想法了,因为遇到的问题真的一大堆,好了废话不多说,来看一下我抓取的信息:

抓取的信息:

1.微博标题

2.微博nickname

3.标识id

4.微博等级

5.地区

6.毕业院校

7.关注量+URL

8.粉丝量+URL

9.微博量+URL

大致获取的也就这么多信息,因为很多人的信息是不完善的,所以就先抓这么多进行测试。

一个基本的思路

确定了我们要找的信息,接下来就是去解析网页了(一个大的难题要出现了),在我看来获取网页目前遇到的:1.解析源码,2.抓包(json),但是新浪微博这个就比较烦了,他这个是在js中,并且是未加载的(只能用正则或者selenium模拟浏览器了),看到这个之后我想了一段时间并且问了罗罗攀 有没有其他的方法,不行我就用selenium,他说还是推荐正则,解析快一点,selenium是最后的选择,没办法了只好硬着头皮去写正则了,这里在测试正则是否正确,可以使用在线测试工具,进行正则的测试,不必去一遍又一遍运行代码。

源码+个人信息

关注+粉丝+微博

找到这些信息,盯着源码一直瞅,看的我头都大了,其实又快捷的方法ctrl+f

搜索框

现在信息的位置我们都清楚在哪了,那么就是写匹配信息的正则了,这个只能是自己慢慢去写,可以练习正则表达式。

URL+粉丝分页问题个人主页URL

我们先来看一个示例:http://weibo.com/p/1005051497035431/home?from=page_100505&mod=TAB&is_hot=1#place,

这个URL,给大家提个醒直接用这个是看不到主页信息的,但是在代码的测试源码中我们能看到一个location重定向的连接,是将#之后的部分替换为&retcode=6102,所以URL应该为:http://weibo.com/p/1005051497035431/home?from=page_100505&mod=TAB&is_hot=1&retcode=6102,

我点击连接测试了一下,看到的内容和第一条连接一样,并且还有一点,我们之后获取的所有连接都要替换#之后的内容,来一个示例吧:

urls = re.findall( r'class=\\"t_link S_txt1\\" href=\\"(.*?)\\"',data) careUrl = urls[ 0].replace( '\\', '').replace( '#place', '&retcode=6102') fansUrl = urls[ 1].replace( '\\', '').replace( '#place', '&retcode=6102') wbUrl = urls[ 2].replace( '\\', '').replace( '#place', '&retcode=6102')

如果不进行替换,我们拿获取后的仍然是无法获取到我们要的源码。

粉丝分页问题

我本想可以解析一个人的粉丝,就可以获取大量的数据,可还是栽在了系统限制(我在爬取的时候第五页之后就返回不到数据)

系统限制

看到这个之后,系统限制,这个又是什么,好吧只能看100个粉丝的信息,没办法了也只能继续写下去。所以说我们只要考虑5页的数据,总页数大于5页按五页对待,小于5页的正常去写就可以,这个搞明白之后,就是要去解决分页的连接了,通过三条URL进行对比:

1.http://weibo.com/p/1005051110411735/follow?relate=fans&from=100505&wvr=6&mod=headfans&current=fans#place

2.http://weibo.com/p/1005051110411735/follow?relate=fans&page=2#Pl_Official_HisRelation__60

2.http://weibo.com/p/1005051110411735/follow?relate=fans&page=3#Pl_Official_HisRelation__60

通过这两个URL我们可以看出,差别就在后半部分,除了之前我说的要将是将#之后的部分替换为&retcode=6102,之外还要改动一点,就是follow?之后的内容那么改动后,我们就从第二页去构造URL。

示例代码:

urls = ['http://weibo.com/p/1005051497035431/follow?relate=fans&page={}&retcode=6102'.format(i) for i in range(2,int(pages)+1)]

那么URL分页问题就搞定了,也可以说解决了一个难题。如果你认为新浪微博只有这些反扒的话,就太天真了,让我们接着往下看。

布满荆棘的路

整个获取过程就是各种坑,之前主要是说了数据的获取方式和URL及粉丝分页的问题,现在我们来看一下新浪微博的一些反扒:

首先,在请求的时候必须加cookies进行身份验证,这个挺正常的,但是在这来说他真的不是万能的,因为cookie也是有生存期的,这个在获取个人信息的时候还没什么问题,但是在获取粉丝页面信息的时候就出现了过期的问题,那该怎么解决呢,想了很久,最后通过selenium模拟登录解决了,这个之后在详细说,总之,这一点要注意。

然后,另外一个点,不是每一个人的源码都是一样的,怎么说呢最明显的自己可以去对比下,登录微博后看一下自己粉丝的分页那部分源码和你搜索的那个用户的源码一样不,除此之外其他的源码信息也有不一样,我真的指向说一句,大公司就是厉害。

用户源码

自己本人的源码

大家仔细看应该可以看出来不同,所以整体来说新浪微博挺难爬。

代码

代码这一块,确实没整好,问题也比较多,但是可以把核心代码贴出来供大家参考和探讨(感觉自己写的有点乱)

说一下代码结构,两个主类和三个辅助类:

两个主类:第一个类去解析粉丝id,另一个类去解析详细信息(解析的时候会判断id是否解析过)

三个辅助类:第一个去模拟登陆返回cookies(再爬取数据的过程中,好像是只调用了一次,可能是代码的问题),第二个辅助类去返回一个随机代理,第三个辅助类将个人信息写入mysql。

下边我就将两个主类的源码贴出来,把辅助类相关其他的信息去掉仍然是可以运行的。

1.fansSpider.py #-*- coding:utf-8 -*-importrequests importre importrandom fromproxy importProxy fromgetCookie importCOOKIE fromtime importsleep fromstore_mysql importMysql fromweibo_spider importweiboSpider classfansSpider(object):headers = [ { "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"}, { "user-agent": "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1"}, { "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3"}, { "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"}, { "user-agent": "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"} ] def__init__(self):self.wbspider = weiboSpider() self.proxie = Proxy() self.cookie = COOKIE() self.cookies = self.cookie.getcookie() field = [ 'id'] self.mysql = Mysql( 'sinaid', field, len(field) + 1) self.key = 1defgetData(self,url):self.url = url proxies = self.proxie.popip() printself.cookies printproxies r = requests.get( "https://www.baidu.com", headers=random.choice(self.headers), proxies=proxies) whiler.status_code != requests.codes.ok: proxies = self.proxie.popip() r = requests.get( "https://www.baidu.com", headers=random.choice(self.headers), proxies=proxies) data = requests.get(self.url,headers=random.choice(self.headers), cookies=self.cookies, proxies=proxies,timeout= 20).text #print datainfos = re.findall( r'fnick=(.+?)&f=1\\',data) ifinfos isNone: self.cookies = self.cookie.getcookie() data = requests.get(self.url, headers=random.choice(self.headers), cookies=self.cookies, proxies=proxies, timeout= 20).text infos = re.findall( r'fnick=(.+?)&f=1\\', data) fans = [] forinfo ininfos: fans.append(info.split( '&')[ 0]) try: totalpage = re.findall( r'Pl_Official_HisRelation__6\d+\\">(\d+)<',data)[- 1] printtotalpage except: totalpage = 1# totalpage = re.findall(r'Pl_Official_HisRelation__\d+\\">(\d+)<', data)[-1]Id = [one forone inre.findall( r'usercard=\\"id=(\d+)&',data)] self.totalid = [Id[i] fori inrange( 1,len(fans)* 2+ 1, 2)] ifint(totalpage) == 1: forone inself.totalid: self.wbspider.getUserData(one) item = {} forone inself.totalid: item[ 1] = one self.mysql.insert(item) fansurl = 'http://weibo.com/p/100505'+ one + '/follow?from=page_100505&wvr=6&mod=headfollow&retcode=6102'# fansurl = 'http://weibo.com/p/100505' + one + '/follow?relate=fans&from=100505&wvr=6&mod=headfans&current=fans&retcode=6102'fan.getData(fansurl) elifint(totalpage) >= 5: totalpage= 5self.mulpage(totalpage) # if self.key == 1:# self.mulpage(totalpage)# else:# self.carepage(totalpage)# def carepage(self,pages):# #self.key=1# urls = ['http://weibo.com/p/1005051497035431/follow?page={}&retcode=6102'.format(i) for i in range(2, int(pages) + 1)]# for url in urls:# sleep(2)# print url.split('&')[-2]# proxies = self.proxie.popip()# r = requests.get("https://www.baidu.com", headers=random.choice(self.headers), proxies=proxies)# print r.status_code# while r.status_code != requests.codes.ok:# proxies = self.proxie.popip()# r = requests.get("https://www.baidu.com", headers=random.choice(self.headers), proxies=proxies)# data = requests.get(url, headers=random.choice(self.headers), cookies=self.cookies, proxies=proxies,# timeout=20).text# # print data# infos = re.findall(r'fnick=(.+?)&f=1\\', data)# if infos is None:# self.cookies = self.cookie.getcookie()# data = requests.get(self.url, headers=random.choice(self.headers), cookies=self.cookies,# proxies=proxies,# timeout=20).text# infos = re.findall(r'fnick=(.+?)&f=1\\', data)# fans = []# for info in infos:# fans.append(info.split('&')[0])# Id = [one for one in re.findall(r'usercard=\\"id=(\d+)&', data)]# totalid = [Id[i] for i in range(1, len(fans) * 2 + 1, 2)]# for one in totalid:# # print one# self.totalid.append(one)# for one in self.totalid:# sleep(1)# self.wbspider.getUserData(one)# item = {}# for one in self.totalid:# item[1] = one# self.mysql.insert(item)# fansurl = 'http://weibo.com/p/100505'+one+'/follow?from=page_100505&wvr=6&mod=headfollow&retcode=6102'# #fansurl = 'http://weibo.com/p/100505' + one + '/follow?relate=fans&from=100505&wvr=6&mod=headfans&current=fans&retcode=6102'# fan.getData(fansurl)defmulpage(self,pages):#self.key=2urls = [ 'http://weibo.com/p/1005051497035431/follow?relate=fans&page={}&retcode=6102'.format(i) fori inrange( 2,int(pages)+ 1)] forurl inurls: sleep( 2) printurl.split( '&')[- 2] proxies = self.proxie.popip() r = requests.get( "https://www.baidu.com", headers=random.choice(self.headers), proxies=proxies) printr.status_code whiler.status_code != requests.codes.ok: proxies = self.proxie.popip() r = requests.get( "https://www.baidu.com", headers=random.choice(self.headers), proxies=proxies) data = requests.get(url, headers=random.choice(self.headers), cookies=self.cookies, proxies=proxies, timeout= 20).text # print datainfos = re.findall( r'fnick=(.+?)&f=1\\', data) ifinfos isNone: self.cookies = self.cookie.getcookie() data = requests.get(self.url, headers=random.choice(self.headers), cookies=self.cookies, proxies=proxies, timeout= 20).text infos = re.findall( r'fnick=(.+?)&f=1\\', data) fans = [] forinfo ininfos: fans.append(info.split( '&')[ 0]) Id = [one forone inre.findall( r'usercard=\\"id=(\d+)&', data)] totalid = [Id[i] fori inrange( 1, len(fans) * 2+ 1, 2)] forone intotalid: #print oneself.totalid.append(one) forone inself.totalid: sleep( 1) self.wbspider.getUserData(one) item ={} forone inself.totalid: item[ 1]=one self.mysql.insert(item) #fansurl = 'http://weibo.com/p/1005055847228592/follow?from=page_100505&wvr=6&mod=headfollow&retcode=6102'fansurl = 'http://weibo.com/p/100505'+one+ '/follow?relate=fans&from=100505&wvr=6&mod=headfans&current=fans&retcode=6102'fan.getData(fansurl) if__name__ == "__main__": url = 'http://weibo.com/p/1005051497035431/follow?relate=fans&from=100505&wvr=6&mod=headfans&current=fans&retcode=6102'fan = fansSpider() fan.getData(url)

中间注释的一部分,因为代码在调试,大家参考正则和一些处理方式即可

2.weibo_spider.py # -*- coding:utf-8 -*-importrequests importre fromstore_mysql importMysql importMySQLdb classweiboSpider(object):headers = { "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"} cookies = { 'TC-Page-G0': '1bbd8b9d418fd852a6ba73de929b3d0c', 'login_sid_t': '0554454a652ee2a19c672e92ecee3220', '_s_tentry': '-', 'Apache': '8598167916889.414.1493773707704', 'SINAGLOBAL': '8598167916889.414.1493773707704', 'ULV': '1493773707718:1:1:1:8598167916889.414.1493773707704:', 'SCF': 'An3a20Qu9caOfsjo36dVvRQh7tKzwKwWXX7CdmypYAwRoCoWM94zrQyZ-5QJPjjDRpp2fBxA_9d6-06C8vLD490.', 'SUB': '_2A250DV37DeThGeNO7FEX9i3IyziIHXVXe8gzrDV8PUNbmtAKLWbEkW8qBangfcJP4zc_n3aYnbcaf1aVNA..', 'SUBP': '0033WrSXqPxfM725Ws9jqgMF55529P9D9WhR6nHCyWoXhugM0PU8VZAu5JpX5K2hUgL.Fo-7S0ecSoeXehB2dJLoI7pX9PiEIgij9gpD9J-t', 'SUHB': '0jBY7fPNWFbwRJ', 'ALF': '1494378549', 'SSOLoginState': '1493773739', 'wvr': '6', 'UOR': ',www.weibo.com,spr_sinamkt_buy_lhykj_weibo_t111', 'YF-Page-G0': '19f6802eb103b391998cb31325aed3bc', 'un': 'fengshengjie5 @ live.com'} def__init__(self):field = [ 'title', 'name', 'id', 'wblevel', 'addr', 'graduate', 'care', 'careurl', 'fans', 'fansurl', 'wbcount', 'wburl'] conn = MySQLdb.connect(user= 'root', passwd= '123456', db= 'zhihu', charset= 'utf8') conn.autocommit( True) self.cursor = conn.cursor() self.mysql = Mysql( 'sina', field, len(field) + 1) defgetUserData(self,id):self.cursor.execute( 'select id from sina where id=%s',(id,)) data = self.cursor.fetchall() ifdata: passelse: item = {} #test = [5321549625,1669879400,1497035431,1265189091,5705874800,5073663404,5850521726,1776845763]url = 'http://weibo.com/u/'+id+ '?topnav=1&wvr=6&retcode=6102'data = requests.get(url,headers=self.headers,cookies=self.cookies).text #print dataid = url.split( '?')[ 0].split( '/')[- 1] try: title = re.findall( r'<title>(.*?)</title>',data)[ 0] title = title.split( '_')[ 0] except: title= u''try: name = re.findall( r'class=\\"username\\">(.+?)<',data)[ 0] except: name = u''try: totals = re.findall( r'class=\\"W_f\d+\\">(\d*)<',data) care = totals[ 0] fans = totals[ 1] wbcount = totals[ 2] except: care = u''fans = u''wbcount = u''try: urls = re.findall( r'class=\\"t_link S_txt1\\" href=\\"(.*?)\\"',data) careUrl = urls[ 0].replace( '\\', '').replace( '#place', '&retcode=6102') fansUrl = urls[ 1].replace( '\\', '').replace( '#place', '&retcode=6102') wbUrl = urls[ 2].replace( '\\', '').replace( '#place', '&retcode=6102') except: careUrl = u''fansUrl = u''wbUrl = u''profile = re.findall( r'class=\\"item_text W_fl\\">(.+?)<',data) try: wblevel = re.findall( r'title=\\"(.*?)\\"',profile[ 0])[ 0] addr = re.findall( u'[\u4e00-\u9fa5]+', profile[ 1])[ 0] # 地址except: profile1 = re.findall( r'class=\\"icon_group S_line1 W_fl\\">(.+?)<',data) try: wblevel = re.findall( r'title=\\"(.*?)\\"', profile1[ 0])[ 0] except: wblevel = u''try: addr = re.findall( u'[\u4e00-\u9fa5]+', profile[ 0])[ 0] except: addr = u''try: graduate = re.findall( r'profile&wvr=6\\">(.*?)<',data)[ 0] except: graduate = u''item[ 1] = title item[ 2] =name item[ 3] =id item[ 4] =wblevel item[ 5] =addr item[ 6] =graduate item[ 7] =care item[ 8] =careUrl item[ 9] =fans item[ 10] =fansUrl item[ 11] =wbcount item[ 12] =wbUrl self.mysql.insert(item)

写的比较乱,大家将就着看,还是我说的只是一个Demo

辅助类之一(存mysql,可以参考Mr_Cxy的python对Mysql数据库的操作小例),其他的两个关于随机代理和获取cookie,在下篇文章会详细讲解

运行结果+数据结果

测试结果

运行截图

200为状态码,说明请求成功

mysql数据库存储结果

总结

目前新浪微博是遇到问题最多的一个,不过也学到了很多知识,比如正则表达式,随机代理等等,在学习的过程中就是遇到的问题越多,积累的越多,进步越快,所以遇到问题和出错也是幸事。说一下代码运行过程中存在遇到的问题吧(可以一块交流解决):

1.有两个id一直在循环,可能是循环那一块存在问题,可以一块交流,解决后会更新文章。

2.解析的速度(单线程比较慢,后续写scrapy版)

3.去重(目前是在将解析过的id写入数据库,然后在解析前进行判断)

这差不多就是一个简单的思路,目前存在一些问题,可以作为参考,有问题的可以一块交流解决。

我有一个Python学习交流QQ群:638121273 禁止闲聊,非喜勿进!