V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
anhkgg
V2EX  ›  Python

pylogin 系列之 V2EX 自动领币消息提醒

  •  
  •   anhkgg ·
    anhkgg · 2017-08-18 21:30:47 +08:00 · 2841 次点击
    这是一个创建于 2660 天前的主题,其中的信息可能已经有所发展或是发生改变。

    概述

    最近开始混 v2ex,v2ex 发主题、回复都要收钱,发帖收钱还跟字数相关,之前不知道这些,发个帖子内容太多,kao,没钱了!

    虽然主题有人回复会收到钱,但是也没人回复啊,也不知道 V2EX 大佬们喜欢什么内容!

    幸好 V2EX 有个登录领币任务,每天还可以攒点钱,但是有些时候会忘啊,怎么办?...

    嗯,程序员嘛,偷懒的办法多...这就开始分析接口,自动领币!

    然后呢,发个主题,总想看看有没有大佬关注和回复,然后就时不时打开浏览器,去刷新一下页面。

    就跟大部分用 windows 的人一样,回到桌面不右键+E (刷新)一下,就感觉人生好像少了什么东西(我好像是重症患者,用 ubuntu 也要找一下刷新桌面)!

    这种情况是不是病啊?!

    然后呢,刷新很浪费时间诶,有人回复,看着还算开心嘛,但也没人回复,那不白浪费时间了嘛,还影响期待的小心情!

    所以呢,还得加上自动消息提醒功能!

    废话完毕,开始干活!

    工具:

    1. chrome/firefox
    2. f12,network
    3. python:requests、re
    

    登录

    开始分析登录接口。打开 chrome,f12,进入登录页面。只需要输入名字和密码,没有验证码,真好!

    访问的链接是:

    https://www.v2ex.com/signin
    

    然后随便输入什么名字和密码,点击登录,肯定失败,页面有提示。再看网络请求数据:

    POST https://www.v2ex.com/signin
    Host: www.v2ex.com
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) 
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    
    //发送数据
    6b79e5fdb638c190396648c486c313dca73ad9f6e4e122fafc356e54522eedc4:"111111111111111" //name
    bb4419eb55aef4106a853ce9f4642d5d58ac021f4e1fef29a230e2352da74802:"11111111111" //password
    once:"95083"
    next:"/"
    
    
    //登录错误
    <div class="box">
        <div class="header"><a href="/">V2EX</a> <span class="chevron">&nbsp;›&nbsp;</span> 登录 &nbsp;<li class="fa fa-lock"></li></div>
        <div class="problem">请解决以下问题然后再提交:<ul><li>用户名和密码无法匹配</li></ul></div>
        
        ...
        
    </div>
    

    这个请求关键点:

    1. POST 请求,url 是https://www.v2ex.com/signin
    2. 发送数据有名字和明文密码,以及两个其他不明字段
    3. 请求是 https,所以明文密码不会暴露。

    在仔细看发送的 4 个数据。 名字和密码对应的字段都是一长串字符,猜想这个是变化的,每次刷新登录页面都不一样,多次尝试下确认该猜想!

    如何获取呢,肯定是在打开登录页面时就会收到服务器返回的这两个字符串的。在登录 html 内容中一翻,看到如下:

    <div class="box">
        <div class="header"><a href="/">V2EX</a> <span class="chevron">&nbsp;›&nbsp;</span> 登录 &nbsp;<li class="fa fa-lock"></li></div>
        <div class="cell">
            <form method="post" action="/signin">
            <table cellpadding="5" cellspacing="0" border="0" width="100%">
                <tr>
                    <td width="120" align="right">用户名</td>
                    <td width="auto" align="left"><input type="text" class="sl" name="804c76d3f1472cdd8721d16f21de446186f2bae893748542ffda39963ff293f4" value="111111111111111" autofocus="autofocus" autocorrect="off" spellcheck="false" autocapitalize="off" placeholder="用户名或电子邮箱地址" /></td>
                </tr>
                <tr>
                    <td width="120" align="right">密码</td>
                    <td width="auto" align="left"><input type="password" class="sl" name="359a3968b3b6f37b05fceed766bd8995090a4fd5cdc74ba0a8cd17b44d2bc86e" value="" autocorrect="off" spellcheck="false" autocapitalize="off" /></td>
                </tr>
                <tr>
                    <td width="120" align="right"></td>
                    <td width="auto" align="left"><input type="hidden" value="79599" name="once" /><input type="submit" class="super normal button" value="登录" /></td>
                </tr>
                <tr>
                    <td width="120" align="right"></td>
                    <td width="auto" align="left"><a href="/forgot">我忘记密码了</a></td>
                </tr>
            </table>
            <input type="hidden" value="/" name="next" />
            </form>
        </div>
    </div>
    

    名字对应字段是<input type="text" class="sl" name="804c76d3f1472cdd8721d16f21de446186f2bae893748542ffda39963ff293f4"

    密码对应字段是<input type="password" class="sl" name="359a3968b3b6f37b05fceed766bd8995090a4fd5cdc74ba0a8cd17b44d2bc86e"

    可以通过正则获取到字段名。

    名字正则:r'<input type="text" class="sl" name="([\d\w]*?)"'

    密码正则:r'<input type="password" class="sl" name="([\d\w]*?)"'

    也看到了其他两个数据字段,<input type="hidden" value="79599" name="once" /><input type="hidden" value="/" name="next" />

    once对应的值每次都不一样,next的值应该是固定的/,但是为了保险,都通过正则来获取

    r'<input type="hidden" value="([\d\w]+?)" name="once" />'
    r'<input type="hidden" value="(.+?)" name="next" />'
    

    好了,到此登录请求需要的东西都分析完了,然后就是模拟接口发送请求了。

    忘了还有一点,返回状态的判断。

    前面看到登录错误的有提示信息,为了更人性化,把这个信息拿到吧。

    //登录错误
    <div class="box">
        <div class="header"><a href="/">V2EX</a> <span class="chevron">&nbsp;›&nbsp;</span> 登录 &nbsp;<li class="fa fa-lock"></li></div>
        <div class="problem">请解决以下问题然后再提交:<ul><li>用户名和密码无法匹配</li></ul></div>
    

    获取错误信息正则是这样:r'<div class="problem">.+?<ul><li>(.*?)</li></ul></div>'

    登录成功判断待会儿再分析。

    通过 py 发送模拟登陆请求,代码如下:

    payload = {
                    self.form_name:name,
                    self.form_pass:pwd,
                    'once': self.form_once,
                    'next': self.form_next
                    }
    r = self.s.post(url, data=payload, headers=headers)
    

    保存了返回数据一看,没成功啊,还是未登录的首页。

    重新再浏览器登录一下,仔细分析了一下。

    发送了登录请求之后,登录成功之后,页面自动跳转到https://www.v2ex.com,有登录信息了。

    猜测对请求的头部数据做了某些校验。

    看看请求的头部数据,如下:

    Host: www.v2ex.com
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) ...
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate, br
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 167
    Referer: https://www.v2ex.com/signin
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1
    

    一般来说可能会对 host,referer 等字段检查,加入尝试一下。

    headers = {
        #'Host': 'www.v2ex.com',
        #'origin':'https://www.v2ex.com',
        'referer':'https://www.v2ex.com/signin',                
        }
    

    成功登录,屏蔽其中一些字段,发现只需要加入 referer 即可登录。

    获取登录成功状态,可以看到登录成功后,会有用户账户信息,如下:

    <a href="/member/anhkgg" class="top">anhkgg</a>&nbsp;&nbsp;&nbsp;
    <a href="https://workspace.v2ex.com/" target="_blank" class="top">工作空间</a>&nbsp;&nbsp;&nbsp;
    <a href="/notes" class="top">记事本</a>&nbsp;&nbsp;&nbsp;<a href="/t" class="top">时间轴</a>&nbsp;&nbsp;&nbsp;<a href="/settings" class="top">设置</a>&nbsp;&nbsp;&nbsp;
    <a href="#;" onclick="if (confirm('确定要从 V2EX 登出?')) { location.href= '/signout?once=54090'; }" class="top">登出</a></td>
    

    那么只需要搜索是否存在<a href="/member/anhkgg"即可。正则表达式是:r'<a href="/member/.+?">'。找到该内容表示登录成功。

    退出

    登录成功了,顺便看一下退出的接口。抓包看一下,发现访问了如下链接:

    https://www.v2ex.com/signout?once=71351
    

    又见到 once 字段,值又是每次不同的。那么也只有动态获取一下了。在前面登录成功的信息中其实可以看到有退出接口的内容。

     onclick="if (confirm('确定要从 V2EX 登出?')) { location.href= '/signout?once=54090'; }" class="top">登出</a></td>
    

    通过正则获取一下 once:r"location.href= '/signout\?once=([\d\w]+?)'",然后模拟退出。

    url = 'https://www.v2ex.com/signout'
    payload = { 'once': self.signout_once}
    self.s.get(url, params=payload)
    

    新评论

    接着就看看我需要的功能了。

    首先是获取最新评论条数。找到对应 html 的内容,如下:

    </a></div><a href="/notifications" class="fade">0 条未读提醒</a></div>
    

    非常简单,关键字 notifications,正则一搜即可拿到。

    r'</div><a href="/notifications" class="fade">(\d+?)(.*?)</a>'

    不在细说。

    为了能主动提醒我是否有最新消息,登录成功后,没 10 分钟刷新一下https://www.v2ex.com,再获取评论条数即可。

    有新评论就通知我。

    领取每日奖励

    嗯,钱的事还是挺重要的。

    首页右侧,每天会出现领取今日奖励的按钮,什么时候出现不知道(过了凌晨 12 点?),点击后跳转到领取页面,再点击领取按钮就拿到钱了!

    第一步,拿到领取页面的链接。看下面,是固定的,终于省了一点点事。

    <div class="box"><div class="inner"><li class="fa fa-gift" style="color: #f90;"></li> &nbsp;<a href="/mission/daily">领取今日的登录奖励</a></div></div>
    

    通过下面的代码跳到领取页面。

    url = 'https://www.v2ex.com/mission/daily'
    r= self.s.get(url)
    

    然后看看领取按钮对应的链接,又见 once ! so,链接不是固定的了。

    <div class="cell">
            <h1>每日登录奖励 20170818</h1>
            <input type="button" class="super normal button" value="领取 X 铜币" onclick="location.href = '/mission/daily/redeem?once=48881';" />
        </div>
    

    动态获取 once 对应的值,正则跟退出接口很像:r"'/mission/daily/redeem\?once=([\d\w]+?)'"

    然后模拟请求领取奖励。

    url = 'https://www.v2ex.com/mission/daily/redeem'
    payload = { 'once': once}
    r = self.s.get(url, params=payload)
    

    总结

    好了,到这里分析就完成了。分析内容非常详细,然后也贴了些关键代码,所以完整代码就暂时不提供了!

    v2ex 登录通过变化的名字和密码字段,以及 once 的值,增加了一定的分析成本,但是总的来说,还是没什么难度!挡不了多少人!

    其他自动回复啊,最新主题啊...等等,各位看官自行脑洞了!

    pylogin 系列还将继续,尽请关注!

    博客原文:https://anhkgg.github.io/pylogin-v2ex-login-analyze/

    4 条回复    2017-08-19 04:30:17 +08:00
    Yourshell
        1
    Yourshell  
       2017-08-18 22:33:09 +08:00
    对菜鸡很友好哈
    eoo
        2
    eoo  
       2017-08-18 22:47:43 +08:00 via Android
    我只能说一点难度都没有。。。。。
    hzwei
        3
    hzwei  
       2017-08-19 00:09:39 +08:00 via Android
    你写代码注释时也喜欢在每句话后面加个“!”吗?
    ysc3839
        4
    ysc3839  
       2017-08-19 04:30:17 +08:00 via Android
    我似乎有一次遇到了点领取奖励,让你再点一次才能领取的情况,提醒你一下。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1032 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 19:28 · PVG 03:28 · LAX 11:28 · JFK 14:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.