推荐学习书目
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 · Aug 18, 2017 · 3573 views
    This topic created in 3207 days ago, the information mentioned may be changed or developed.

    概述

    最近开始混 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 replies    2017-08-19 04:30:17 +08:00
    Yourshell
        1
    Yourshell  
       Aug 18, 2017
    对菜鸡很友好哈
    eoo
        2
    eoo  
       Aug 18, 2017 via Android
    我只能说一点难度都没有。。。。。
    hzwei
        3
    hzwei  
       Aug 19, 2017 via Android
    你写代码注释时也喜欢在每句话后面加个“!”吗?
    ysc3839
        4
    ysc3839  
       Aug 19, 2017 via Android
    我似乎有一次遇到了点领取奖励,让你再点一次才能领取的情况,提醒你一下。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1642 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 742ms · UTC 16:24 · PVG 00:24 · LAX 09:24 · JFK 12:24
    ♥ Do have faith in what you're doing.