通过 AJAX分析法获取爬取微博数据的API

昨天,我说我会把之前写过的爬虫代码免费分享给大家,后来因为一些原因(公司写的代码不能分享出去?)不得不停止这种源码分享的方式。

先在这向大家说声:抱歉。

于是我想,我还是一个平台一个平台地做系统的分享。

今天我们要聊的是微博的数据采集。

一、找出微博主页唯一标示oid

一般做爬虫爬取网站,首选的都是m站,其次是wap站,最后考虑PC站,因为PC站的各种验证最多。当然,这不是绝对的,有的时候PC站的信息最全,而你又恰好需要全部的信息,那么PC站是首选。一般m站都以m开头后接域名, 这里通过m.weibo.cn去分析微博的HTTP请求。

首先在PC端打开杭州市旅游委员会的微博主页:

https://weibo.com/hangzhoutourism?topnav=1&wvr=6&topsug=1&is_hot=1

接着打开调试页面(Chrome浏览器是右击检查,其他大部分浏览器点击直接按F12),先选中右侧手机模式,然后刷新页面,这个时候地址栏上的URL就变成m站的微博主页地址,如下图1所示。

注意:当打开一个页面,再点开Network标签时是不会有信息的,这时需要在打开的情况下,刷新一下页面。

https://m.weibo.cn/p/1005051789342195?sudaref=login.sina.com.cn&display=0&retcode=6102

也有些微博主页对应的m站地址是这样形式。

https://m.weibo.com/u/2189067512

图1 微博主页及调试页面

这里的1005051789342195和2189067512就是没个微博主页的唯一标示,但是这个唯一标示为什么有些是16位的数字,有些是10位的数字呢?不妨继续在PC上进行观察。

如下图2所示,进入调试页面打开Network(注意不是手机模式),选择Doc,从返回的HTTP包中找到oid,这个oid为1789342195。通过多次抓包分析不同的微博主页,会发现以p打头的类似“p/1005051789342195”,它们的oid都是除掉100505之外的其他10位数字,而以u打头的类似“u/2189067512”,它们的oid就是u后面的10位数字。

图2 PC模式的调试页面

二、通过ajax分析法找出用户微博内容的API

找到微博用户主页的唯一标示oid之后,接下来要做的就是使用ajax分析法找出微博用户发布的微博内容信息的API。如下图3所示,打开调试页面,选中手机模式,然后刷新页面,在Network搜索API,即选择XHR进行过滤,发现有两个已经发送的API请求(API请求一般都在XHR中,其他网页请求在Doc中)。

图3 在XHR找出请求数据的API

查看这两个API返回的数据发现,第一个API返回的是用户数据,第二个API返回的是微博内容数据。以同样的方式分析大量的请求微博数据的API发现它们这个API的核心参数是containerid,并且这个containerid值为固定6位数字107603和微博用户主页的唯一标示oid组合而成。

https://m.weibo.cn/api/container/getIndex?containerid=1076031789342195

接下来就是分析API返回数据,如图6-4所示,在右边选择Preview预览返回的JSON格式的数据,点击data下的cards中任选一个card,其中的mblog标签下就有需要的微博内容数据。

图4 分析API返回的JSON格式的数据

如下图5所示,我们继续观察发现这个JSON中只有12条数据。

图5 第一次请求只有12条数据

如果需要获取更多的数据应该怎么办呢?往下滑动到下一页,继续查看请求的API内容,如下图6所示。

图6 滑动到下一页

发现在获取下一页数据时的API添加了一个新的参数page,值为2。继续往下翻页,page会变成3、4、5一直到滑动到最后一页。由此可以推断这个API获取哪一页的数据由page决定。

三、分析返回的JSON格式的微博内容

通过数据请求的API获取到返回的微博内容,以其中一个card来分析获取到的数据信息。如图7所示,发现微博内容数据是在mblog中。

图7 JSON格式数据

通过和微博网页上的内容对比,可以推断出text为微博标题内容,created_at为微博发布时间,id为该微博的详情地址中的区分数值(后面会说),而图片信息需要根据是图文内容还是视频内容的不同,获取方式也是不一样,分析如下。

如果card_type为9时,该card下面的内容就是正常的图文或视频内容。如果card_type为非9时,card下的内容就可能会是广告信息。如图8所示。

图8 card_type不为9时

如果该微博内容为普通图文内容时,内容的图片信息会在mblog下的pics中,如图9所示。

图9 图文内容中的图片信息

如果该微博内容为视频时,视频的封面图片信息在mblog/page_info/page_pic下的url对应的值,如图10所示。

图10 视频内容下的视频封面图信息

以上所列举的都是原创微博的情况,那么如果是转发微博的话JSON格式是怎么样的呢。首先我们先确定cards下的内容哪些是转发的哪些是原创的,确定技巧就是判断mblog下是否有retweeted_status元素,如果有则表示是转发内容,否则是原创内容,如图11所示。

图11 转发或原创内容的标示

对于转发内容,我们如何获取被转发内容的标题、发布时间及图片信息呢?如图12所示,发现对应的值是retweeted_status下的text为微博标题内容,created_at为微博发布时间,id为该微博的详情地址中的区分数值,pics为图片信息。

图12 转发图文内容

如果是转发视频内容的话,对应的视频的封面图片信息在mblog/retweeted_status /page_info/page_pic下的url对应的值,标题和发布时间信息在mblog/retweeted_status下的text和created_at,如图13所示。

图13 转发的视频信息

最后要获取的是微博的详情页面地址,前面我们在mblog下发现有一个id元素。通过多条微博的数据进行验证,可以发现微博详情页的地址为m.weibo.cn/status/+id,这个id也就是返回的JSON数据中的mblog/id。如图14所示。

图14 微博的详情页地址

四、获取微博内容的代码实现

分析完接口之后就可以开始编写爬虫代码。

首先需要拼接出完整的可以获取到JSON数据的API路径。通过前面的分析得出API中的containerid是由用户的唯一标示oid拼接而成的,并且获取方式需要分两种情况,有些用户微博主页是https://m.weibo.com/p/1005051789342195,还有些是https://m.weibo.com/u/2189067512。对于前者,它的oid是100505之后的值,后者的oid是u后面的数字。

基于这个逻辑编写获取API路径的代码。

start_url = 'https://m.weibo.com/p/1005051789342195'

或者

start_url = 'https://m.weibo.com/u/2189067512'

containerid = ''

url_head ='https://m.weibo.cn/api/container/getIndex?containerid='

if'https://m.weibo.cn/p/' in start_url:

containerid = start_url.replace('https://m.weibo.cn/p/','').replace('100505','107603')

elif'https://m.weibo.cn/u/' in start_url:

containerid = '107603' +start_url.replace('https://m.weibo.cn/u/', '')

if containerid:

origin_url ='%s%s'%(url_head,containerid)

这里获取的origin_url就是需要的我们最终需要的获取微博内容的API路径。获取到API之后就是如何解析通过API返回的JSON数据。Python自带有一个读写JSON数据的JSON函数。

JSON模块提供了一种很简单的方式来编码和解码JSON数据。其中两个主要的函数是json.dumps()和json.loads(),要比其他序列化函数库如pickle的接口少得多。

json.dumps将Python对象编码成JSON字符串:

import json

data = {

'name' : 'scrapy爬虫框架',

'count' : 2

}

json_str =json.dumps(data)

json.loads将已编码的JSON字符串解码为Python对象:

data =json.loads(json_str)

有了json.loads函数之后就可以开始编写解析返回的微博JSON格式的数据代码。

content =json.loads(response.body)

weibo_info =content.get('cards',[])

最后贴上完整代码(这块的完整代码在最后可能会进行调整,因为微博的API也是实时在变的):

# -*- coding: utf-8 -*-

import scrapy

from scrapy.http import Request

import json

# 微博内容

class WeiBoWBSpider(scrapy.Spider):

name = 'weibo_wb_spider'

allowed_domains = ['weibo.com']

def __init__(self, task_id=None, object_urls=None, *args, **kwargs):

super(WeiBoWBSpider, self).__init__(*args, **kwargs)

self.start_urls = ['https://m.weibo.cn/p/1005052803301701',

'https://m.weibo.cn/u/2189067512']

def start_requests(self):

for start_url in self.start_urls:

containerid = ''

url_head = 'https://m.weibo.cn/api/container/getIndex?containerid='

if 'https://m.weibo.cn/p/' in start_url:

containerid = start_url.replace('https://m.weibo.cn/p/','').replace('100505','107603')

elif 'https://m.weibo.cn/u/' in start_url:

containerid = '107603' + start_url.replace('https://m.weibo.cn/u/', '')

if containerid:

origin_url = '%s%s'%(url_head,containerid)

yield Request(origin_url,callback=self.parse)

def parse(self, response):

content = json.loads(response.body)

weibo_info = content.get('data').get('cards',[])

for info in weibo_info:

if info.get('mblog') and info.get('mblog').get('text'):

title = (info['mblog']['text']).encode('utf8')

url = "https://m.weibo.cn/status/%s" % info["mblog"]["mid"]

time_str = info.get('mblog').get('created_at').encode('utf8')

picture_urls = ''

if info.get('mblog').get('page_info'):

if info.get('mblog').get('page_info').get('media_info'):

picture_urls = info.get('mblog').get('page_info').get('page_pic')['url']

if not picture_urls:

if info.get('mblog').get('pics'):

pics = map(lambda x:x.get('url'),info["mblog"]["pics"])

picture_urls = ','.join(pics)

print '======微博内容======'

print title

print url

print time_str

print picture_urls

打印出的运行结果(部分)包括微博标题、微博的内容地址、发布时间以及图片地址为:

======微博内容======

#杭州新忆#

【杭州旅游】秋已渐远,冬即将来,空气中缕缕的桂花香味慢慢散去,立冬后的杭州,别有一番韵味……这里有杭州实时旅游资讯,旅游攻略,还有更多杭州大小故事,杭州旅游指南,陪你度过在杭州旅游的每一天!

https://m.weibo.cn/status/4171385368567714

11-07

https://wx4.sinaimg.cn/orj360/6aa731f3ly1fl9a3kc6y2j20m80et75z.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fl9a3ls4xwj20m80ej41x.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fl9a3mkh5sj20m80eagoe.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fl9a3n6ziwj20m80eqabl.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fl9a3j6z99j20by0bydgp.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fl9a3nwwcij20m80erdh4.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fl9a3oxlpaj20go0b1mzu.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fl9a3pij35j20dw08wjs1.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fl9a3pyq5uj20m80fbn1v.jpg

======微博内容======#晚安#好多好多美好的事情,就应该遇见,而不是追逐,或者等待。

https://m.weibo.cn/status/4184615768518710

2小时前

https://wx3.sinaimg.cn/orj360/6aa731f3ly1fmfhlea736j20c80iejt2.jpg

======微博内容======#深夜发吃#【吐司奶布丁】口感软嫩滑爽,很适合宝宝食用。制作中要用中火,火候不宜过大,否则容易蒸老,影响口感。

https://m.weibo.cn/status/4184585657322759

4小时前

https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmfe4pozlmj20e60c2n0l.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmfe4q6h5yj20e60c2jtd.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmfe4qvxgpj20e60c2mz2.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmfe4rcv90j20e60c2dho.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fmfe4rnuvzj20e60c2dhs.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fmfe4so4tfj20e60c2di7.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmfe4tqbfuj20e60c2gnu.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmfe4u23f3j20e60c2di5.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmfe4wlw8yj20e60c2q6d.jpg

======微博内容======#世界真奇妙#芝加哥的冬天

https://m.weibo.cn/status/4184555507799312

6小时前

https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmfant8vprj20j60ny125.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmfao2st73j20j60nydok.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmfao3fjjdj20j60ny125.jpg,https://wx3.sinaimg.cn/orj360/6aa731f3ly1fmfao62k8rj20j60ny12e.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmfao6iy2nj20j60nyqed.jpg,https://wx3.sinaimg.cn/orj360/6aa731f3ly1fmfao7lq4ij20j60nytjr.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmfao83bhrj20j60nyaiw.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fmfao8k8ccj20j60nyakv.jpg,https://wx3.sinaimg.cn/orj360/6aa731f3ly1fmfao8v014j20j60nygsi.jpg

======微博内容======#乐活杭州#【收藏!杭州超适合拍照的场地攻略】拍照老司机总结的这几年在杭州拍照的场地,满满的干货,简直朋友圈乐活指南!快约起小伙伴拍照去吧~via.@林走心

https://m.weibo.cn/status/4184525577386862

8小时前

https://wx1.sinaimg.cn/orj360/6aa731f3ly1fmf78dzspqj20j62umwpq.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmf78earamj20j62lrdpb.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmf78ekinhj20j62lr49a.jpg,https://wx3.sinaimg.cn/orj360/6aa731f3ly1fmf78f23uaj20j62tan90.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmf78fmu1aj20j6365n9v.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fmf78g6q8nj20j62er7f7.jpg,https://wx1.sinaimg.cn/orj360/6aa731f3ly1fmf78gnofmj20j63m6dv4.jpg,https://wx2.sinaimg.cn/orj360/6aa731f3ly1fmf78h0dplj20j62lr7hd.jpg,https://wx4.sinaimg.cn/orj360/6aa731f3ly1fmf78hf67lj20j63t7nds.jpg

======微博内容======

转发微博

https://m.weibo.cn/status/4184503343729236

9小时前

======微博内容======

转发微博

https://m.weibo.cn/status/4184503226504812

9小时前

五、定义ITEM和通过Pipeline保存数据到MySQL数据库

我在简书之前已经讲解过ITEM,本文不再重复讲解(如果需要请留言),直接贴上定义的ITEM代码:

# encoding=utf-8



from scrapy.item import Item, Field



# 微博内容


class WeiBoListItem(Item):


# 微博标题


title = Field()


# 微博内容地址


url = Field()


# 微博的发布时间


time_str = Field()


# 微博带的图片地址


picture_urls = Field()

同样的,通过Pipeline保存数据到Mysql数据库也直接贴上对应的代码:

# -*- coding: utf-8 -*-

import MySQLdb

import MySQLdb.cursors

# 设置字符集,防止编码参数出错

import sys

reload(sys)

sys.setdefaultencoding("utf-8")

from scrapy.exporters import JsonItemExporter

# 提交数据到mysql

class DataSubmitMysqlPipeline(object):

def __init__(self):

# 填写数据库用户名、数据库名、数据库用户密码、数据库url

self.conn = MySQLdb.connect(user='root', db='spider_db', passwd='root',

host='127.0.0.1',charset="utf8", use_unicode=True)

def process_item(self, item, spider):

insert_sql = """

insert into tb_weibo(title, url, time_str, picture_urls)

values(%s,%s,%s,%s)

"""

到此就完成了整个爬取微博内容的爬虫代码。

六、总结

通过ajax分析法找出获取请求数据的API是最高效的爬虫方式,然而这种方式也不是每个平台都像微博这块的这样顺利,很多网站也会对这些API设置反爬策略。常见的反爬策略包括如下几点:

1.cookie校验;

2.referer校验;

3.url中的参数校验。

当然对于这种反爬的反反爬策略肯定也是有的,将在后面Scrapy突破反爬的限制进行详细讲解。

如果对你有用,欢迎关注和转发。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181014G13H6700?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券