新浪微波早已称为我每天都会登陆上去刷新一下消息的APP,除了看看比较好玩的东西,还会看看好友在更新些在朋友圈看不到的东西,今天,就试着写爬虫爬取些微博的相关数据,比如微博正文、点赞数、评论数、转发数等等,最后把这些数据保存在mongodb数据库中。
一、分析
以女友的微博主页为例,在实际操作中发现,在开发者模式的Network选项卡下,查看第一个请求的Response:
从图上可见,这个页面里的内容结构非常简单,只是执行了JavaScript操作,这些内容并不是我们需要的原始内容返还的,而是执行JavaScript后向服务器发送Ajax请求,浏览器拿到数据后渲染出来的。何为Ajax,也就是异步的JavaScript和XML。比如,当你在刷微博时,整个页面并不是一开始全部被渲染出来,而是微博底部会有一个刷新箭头,当你不断下滑鼠标,页面才会逐渐被渲染出来。这是一种异步数据加载方式,当最初页面的数据加载完成,会再向服务器某个接口请求获取数据,然后数据经JavaScript处理呈现出来,这种方法的好处就是采用分步渲染的方式,降低了服务器直接渲染整个页面的压力。
在返回的所有请求中,我看到一个以getIndex开头的请求,请求类型Type是xhr,这就是Ajax的请求类型。
然后点击打开查看请求头Request Headers里的内容,有一个X-Requested-With:XMLHttpRequest信息,这标记的就是Ajax的请求。
二、过滤
既然已经知道了微博应用了Ajax渲染技术,那么如何从被Ajax渲染出的页面获取原始请求呢?
在请求页面,直接选择以XHR筛选,获得所有Ajax的请求,选取其中一个getIndex请求,打开:
在General里边,这是一个GET类型的请求,请求链接是https://m.weibo.cn/api/container/getIndex?type=uid&value=1976069377&containerid=1076031976069377。里边有三个参数类型:type、value、containerid。刷新微博,再打开一个GET类型的请求,发现,请求链接变为https://m.weibo.cn/api/container/getIndex?type=uid&value=1976069377&containerid=1076031976069377&page=2。
前边三个参数没有变,后边多了一个page参数,可见这个page参数代表的页码,控制分页。而前边value是用户的ID,而containerid是107603+用户ID,type类型始终没变是uid。
继而打开Preview选项卡,里边存放的便是我所需要的内容。这个内容是JSON格式的,JSON 按照 "名称 / 值对"的方式,{ "firstName": "Brett", "lastName":"McLaughlin", "email": "aaaa" },可以将 JavaScript 对象中表示的一组数据转换为字符串。
在data下边存放着两个主要信息:cardlistInfo和cards。在每个请求里边包含10个元素信息,说明每次刷新刷新10条微博。在cards的mblog里边,我看到了我需要爬取的信息,attitudes_count(点赞数目)、comments_count(评论数目)、created_at(发布时间)、reposts_count(转发数目)、text(微博正文)。
这样,大体的分析完成,现在只要向服务器请求一个接口,就可以得到10条微博信息。之后只要构造一个遍历,通过改变page参数,就可以获得所有微博数据。
三、代码
import requests
from urllib.parse import urlencode
from pyquery import PyQuery as pq
from pymongo import MongoClient
base_url = 'https://m.weibo.cn/api/container/getIndex?'
headers = {
'Host': 'm.weibo.cn',
'Referer': 'https://weibo.com/u/1976069377',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
}
client = MongoClient()
db = client['weibo']
collection = db['weibo']
max_page = 10
#构造General里Request URL请求 Request URL:https://m.weibo.cn/api/container/getIndex?type=uid&value=1976069377&containerid=1076031976069377&page=2
def get_page(page):
params = {
'type': 'uid',
'value': '1976069377',
'containerid': '1076031976069377',
'page': page
}
url = base_url + urlencode(params)
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.json()
except requests.ConnectionError as e:
print('Error', e.args)
#获取Preview选项卡里的JSON数据
def parse_page(json):
if json:
items = json.get('data').get('cards')
for item in items:
item = item.get('mblog')
weibo = {}
weibo['id'] = item.get('id')
weibo['text'] = pq(item.get('text')).text() #输出文字信息先用pq解析
weibo['attitudes'] = item.get('attitudes_count')
weibo['comments'] = item.get('comments_count')
weibo['reposts'] = item.get('reposts_count')
yield weibo
def save_to_mongo(result):
if collection.insert(result):
print('Saved to Mongo')
if __name__ == '__main__':
for page in range(1, max_page + 1):
json = get_page(page)
results = parse_page(json)
for result in results:
print(result)
save_to_mongo(result)
四:结果
输入show dbs ,显示已经建立的数据表
点击Create新建链接,可以修改name,点击Test测试链接,如果显示链接可用,点击Save保存,点击Connect进行链接本地数据库。
最后可以查看我们的数据啦。。。。。