scrapy笔记(3)-微博模拟登录及抓取微博内容
来源:互联网 发布:移动4g数据卡 编辑:程序博客网 时间:2024/05/17 01:03
参考阅读
基于python的新浪微博模拟登陆
Python模拟登录新浪微薄(使用RSA加密方式和Cookies文件
新浪微博登录rsa加密方法
模拟登录新浪微博(直接填入Cookie)
模拟登录新浪微博(Python)
1. 事前准备
- 阅读上篇scrapy笔记(2)
- 下载Fiddler并掌握其基本用法
- 阅读urllib2文档
- 下载本文我的源码
2. 微博登录分析
2.1 截包分析
以下的内容需要掌握Fiddler截包、改包重发等基本知识, 如果不想了解微博的模拟登录的流程及原理, 那么可以跳过这部分直接到第3步. 不过建议还是去熟悉下Fiddler这个前端调试神器, 当然,用其它截包工具代替也是可以的.比如Firefox的插件httpfox
微博的登录入口有好几个, 我们选择http://weibo.com/login.php 这个. 其实只要登录的逻辑不变, 其它的入口也是可以的.
然后, 我们让Fiddler开始截包, 并在登录页面上输入账号密码登录一次.
截到关于登录的包如下:
实际上截到的包更多, 但我们只需要关注图中红色标记的那几个
我们先来看看第一个
图中一栏的所有数据就是我们在模拟登录时需要填入的数据.
这些数据中除了su、sp、rsakv、servertime、nonce是经过js处理动态生成的,其它都是个固定值,可以在代码中写死.
怎么获得这些值呢?
http://login.sina.com.cn/sso/prelogin.phpentry=weibo&callback=sinaSSOController.preloginCallBack&su=yourusername&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.11)
注意上面url的su=yourusername部分, 这里的su是经过js处理后的用户名.
请求这个url可以得到servertime,nonce,pubkey,rsakv等等
2.2 查看json
我们还需要知道js是怎么处理我们填入的用户名及密码的, 即su与sp.
首先我们要在未登录状态到http://login.sina.com.cn/signup/signin.php?entry=sso 这个页面,并得到http://login.sina.com.cn/js/sso/ssologin.js 这个js文件.
查看ssologin.js的makeRequest函数, 原型如下:
var makeRequest = function (username, password, savestate) { var request = { entry: me.getEntry(), gateway: 1, from: me.from, savestate: savestate, useticket: me.useTicket ? 1 : 0 }; if (me.failRedirect) { me.loginExtraQuery.frd = 1 } request = objMerge(request, {pagerefer: document.referrer || ""}); request = objMerge(request, me.loginExtraFlag); request = objMerge(request, me.loginExtraQuery); request.su = sinaSSOEncoder.base64.encode(urlencode(username)); if (me.service) { request.service = me.service } if ((me.loginType & rsa) && me.servertime && sinaSSOEncoder && sinaSSOEncoder.RSAKey) { request.servertime = me.servertime; request.nonce = me.nonce; request.pwencode = "rsa2"; request.rsakv = me.rsakv; var RSAKey = new sinaSSOEncoder.RSAKey(); RSAKey.setPublic(me.rsaPubkey, "10001"); password = RSAKey.encrypt([me.servertime, me.nonce].join("\t") + "\n" + password) } else { if ((me.loginType & wsse) && me.servertime && sinaSSOEncoder && sinaSSOEncoder.hex_sha1) { request.servertime = me.servertime; request.nonce = me.nonce; request.pwencode = "wsse"; password = sinaSSOEncoder.hex_sha1("" + sinaSSOEncoder.hex_sha1(sinaSSOEncoder.hex_sha1(password)) + me.servertime + me.nonce) } } request.sp = password; try { request.sr = window.screen.width + "*" + window.screen.height } catch (e) { } return request};
从代码中我们可以知道su就是经过html字符转义再转成base64编码
在python中我们可以这样转化:
def get_su(user_name): username_ = urllib.quote(user_name) # html字符转义 username = base64.encodestring(username_)[:-1] return username
再看sp, 关于密码的这部分有点复杂, 我自己对密码学这部分并不大了解, 不过可以从js中看到, weibo登录对密码有两种加密方式:rsa2与wsse,我们从图2的pwnencode=rsa2可知, js处理走的是这一部分逻辑(至于另一部分wsse是什么时候用到, 我不清楚)
if ((me.loginType & rsa) && me.servertime && sinaSSOEncoder && sinaSSOEncoder.RSAKey) { request.servertime = me.servertime; request.nonce = me.nonce; request.pwencode = "rsa2"; request.rsakv = me.rsakv; var RSAKey = new sinaSSOEncoder.RSAKey(); RSAKey.setPublic(me.rsaPubkey, "10001"); password = RSAKey.encrypt([me.servertime, me.nonce].join("\t") + "\n" + password)}
可以看到servertime, nonce, rsakv都被用上了.我们只要把这部分js在python中转义就行了.
我也是看别人的文章才知道,0x10001要转化成10进制的65537, 还有要经过servertime + +'\t' + nonce + '\n' + passwd拼接字符串再进行Rsa加密, 最后转成16进制即得到sp. 代码如下
def get_sp_rsa(passwd, servertime, nonce): # 这个值可以在prelogin得到,因为是固定值,所以写死在这里 weibo_rsa_n = 'EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443' weibo_rsa_e = 65537 # 10001对应的10进制 message = str(servertime) + '\t' + str(nonce) + '\n' + passwd key = rsa.PublicKey(int(weibo_rsa_n, 16), weibo_rsa_e) encropy_pwd = rsa.encrypt(message, key) return binascii.b2a_hex(encropy_pwd)
3 模拟登录
准备工作做完了,我们要模拟正常登录那样发几个http包, 基本上是以下几个步骤
从
http://login.sina.com.cn/sso/prelogin.phpentry=weibo&callback=sinaSSOController.preloginCallBack&su=yourusername&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.11)
获取servertime,nonce,rsakv等值
- 把这些值与其它固定值一起提交到
http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.11)
, 这个地址会跳转到passport.weibo.com/wbsso/login/(省略, 见图2)/
,并返回图2下划线标注的我们需要的地址. - 用正则表达式取出图2的地址并请求, 得到如下结果则登录成功此时要保存该请求的cookie
以后每次抓取微博时的请求带上该cookie即可.
到这里, 模拟登录就算完成了. 当然这是人工的模拟登录与结合scrapy的模拟登录还是有所不同,不过区别也不会大到哪去, 只要把保存的cookie持久化到文件, scrapy每次请求时带上这个cookie就可以了,相信这部分不会有多大难度. 如果还是有困难, 请提出来, 有时间我再补完这部分内容.
4 抓取微博内容
4.24 补充说明,本来以为微博抓取内容跟其它一样简单,结果发现微博用js渲染所有内容,scrapy抓的网页源文件一个链接都没有, 考虑用其它方法解决, 正在坑中, 搞定后再来更新文章吧.
4.26 抓取微博内容, 搞了很久, 终于算是实现了.说说我的方法吧, 根据观察,微博的内容放在页面上某个script标签内.
我们可以通过正则, 取出这部分内容,然后替换response的body,再用scrapy的选择器提取其中的内容和链接,具体看代码及注释吧
# -*- coding: utf-8 -*-from scrapy import Requestfrom scrapy.contrib.spiders import CrawlSpider, Rulefrom scrapy.contrib.linkextractors import LinkExtractorfrom weibo_spider.items import WeiboSpiderItemfrom weibo_spider.spiders.login_api import get_login_cookieclass WeiboSpider(CrawlSpider): name = 'weibo' allowed_domains = ['weibo.com'] start_urls = ['http://www.weibo.com/u/1876296184'] # 不加www,则匹配不到cookie, get_login_cookie()方法正则代完善 rules = ( Rule(LinkExtractor(allow=r'^http:\/\/(www\.)?weibo.com/[a-z]/.*'), # 微博个人页面的规则,或/u/或/n/后面跟一串数字 process_request='process_request', callback='parse_item', follow=True), ) cookies = None def process_request(self, request): request = request.replace(**{'cookies': self.cookies}) return request def start_requests(self): for url in self.start_urls: if not self.cookies: self.cookies = get_login_cookie(url) # 得到该url下的cookie yield Request(url, dont_filter=True, cookies=self.cookies, meta={'cookiejar': 1}) # 这里填入保存的cookies def extract_weibo_response(self, response): # 提取script里的weibo内容,替换response script_set = response.xpath('//script') script = '' for s in script_set: try: s_text = s.xpath('text()').extract()[0].encode('utf8').replace(r'\"', r'"').replace(r'\/', r'/') except: return response if s_text.find('WB_feed_detail') > 0: script = s_text break kw = {'body': script} response = response.replace(**kw) return response def _parse_response(self, response, callback, cb_kwargs, follow=True): # 继承crawlspider这个方法,这个方法在解析页面/提取链接前调用 response = self.extract_weibo_response(response) return super(WeiboSpider, self)._parse_response(response, callback, cb_kwargs, follow) def parse_item(self, response): msg_nodes = response.xpath('//*[@class="WB_feed WB_feed_profile"][2]/div') # 提取weibo的内容div items = [] if msg_nodes: for msg in msg_nodes: item = WeiboSpiderItem() try: c = msg.xpath('.//div[@class="WB_detail"]/div/text()').extract()[0] #提取每条微博的内容,不包括at人 content = c[38:].encode('utf8') #从38位开始, 是为了去掉\n和一大堆空格 except Exception, e: pass else: item['content'] = content item['url'] = response.url items.append(item) return items
继承CrawlSpider类是因为要用到它根据定制的Rule提取/跟进链接的功能, 当然你也可以选择最基础的Spider类,不过其中的parse方法就得自己写了.
不过这份代码仍然有些问题:
- 暂时只能提取某人weibo第1页内容
- weibo向下拉滚动条会新增内容,而这部分是通过ajax动态请求json实现的,暂时只能提取第1页第1段内容
解决思路是有的
这是weibo单页地址
http://www.weibo.com/u/1832810372page=1 改page即可跳页
这是weibo分段地址
http://weibo.com/p/aj/v6/mblog/mbloglist?domain=100505&page=2&pre_page=3&pagebar=1&id=1005053190764044
domain与id可以在页面上第2个script标签内找 ,替换page和pre_page即可加载不同段的json
只要在抓取某人首页时请求page=?与pre_page=1,2,3(每页最多只有3段),就可以实现加载任一页所有内容.
于是问题来了,在scrpay中哪里嵌入这些逻辑呢?我还没想好,但我觉得如果写个小爬虫的话,用urllib2/request+beatifulsoup自己写肯定比用scrpay舒爽的多.
说实话,改出上面的代码已经折腾了我将近1天多的时间,这还是跟踪源码好半天,搞清楚这些调用的来龙去脉才弄出来的,官方的文档实在不够看.
也许该去抓移动端的包来解析一下,感觉这应该比抓PC端简单很多.
总结:
本章尽在折腾,跟踪weibo各种包并解析,同时为了达到效果跟踪调试scrapy的源码,最后也只搞出个半成品,就先挖个坑在这吧.有需要的话才花时间折腾一番.
下篇内容应该是介绍一下如何跟踪调试scrapy或者选某网站写个定时增量抓取的爬虫.
原文链接:http://www.jianshu.com/p/36a39ea71bfd
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
- scrapy笔记(3)-微博模拟登录及抓取微博内容
- scrapy模拟登录微博
- scrapy模拟登录新浪微博
- scrapy模拟登录新浪微博
- PHP CURL模拟登录新浪微博抓取页面内容 基于EaglePHP框架开发
- 模拟登录微博通,抓取新浪微博c#
- python登录新浪微博并抓取内容
- 基于scrapy的分布式爬虫抓取新浪微博个人信息和微博内容存入MySQL
- 微博登录过程分析以及scrapy模拟登录微博
- python模拟浏览器登录淘宝抓取内容
- 新浪微博内容抓取
- Scrapy笔记(11)- 模拟登录
- php 新浪通行证、新浪微博模拟统一登录 (后台网页抓取版) 2016
- HttpClient4模拟登录腾讯微博及分享功能
- HttpClient4模拟登录腾讯微博及分享功能
- Scrapy模拟表单登录
- scrapy 模拟登录
- java模拟登录内部系统抓取网页内容
- redis实现有序的消息队列
- 常量指针和指针常量
- UESTC 1170 红蓝点对
- POJ-3414-Pots
- JavaScript强化教程——Cocos2d-JS中JavaScript继承
- scrapy笔记(3)-微博模拟登录及抓取微博内容
- hdu 3197
- 144. Binary Tree Preorder Traversal
- mysqli连接数据库
- 2016/08/11 多态
- HDU1542 线段树+离散化+扫描线
- ArrayList的使用方法
- C++ Socket 一个服务器 多个客户端 (阻塞式)
- 3000门徒内部训练绝密视频(泄密版)第6课:精通Spark集群搭建与测试