利用『爬虫』 折衷解决 个人支付宝支付系统 ---- 获取账单信息

2017-08-15 17:57:07 +08:00
 Ehco1996

这个部分主要是账单信息的获取, 文笔和语法可能都有点差,大家包涵

从零开始写 Python 爬虫 --- 爬虫应用: 获取支付宝账单信息

最近想给自己的网站接入支付和自动发货系统,踩了一些坑, 其中用到一些爬虫技术,放在这里和大家分享一下。

支付宝对个人用户太不友好了,自己网站想接入支付宝支付的话,必须要签约, 签约得要营业执照。总之各种蛋疼。这不,我想出了一个「折衷的解决办法」。

解决思路

  1. 用户通过转账码给支付宝账号转账。
  2. 转账之后提交转账 「流水号」给后台服务器
  3. 后台服务器模拟登录支付宝网页版,查找最近的订单
  4. 比较数据,成功则自动发货

整个处理流程效率十分低下,但总比我手动发货好那么一点。 再次吐槽坑爹的支付宝,不开放接口!!!

支付宝账单的查询

一开始的思路是,通过 POST 数据登录支付宝 web 版。 后来尝试了一下,发现。支付宝关闭了 POST。 想要登录获取信息的话, 那么只剩下一条路了: 通过『 cookies 』登录

订单查询 v1:

手动登录支付宝 web 版本, 打开 chrome 开发工具,找到 cookies, 复制出来。

上述步骤很我们在 爬取螺纹钢数据 这篇文章里已经详细的描述过了, 我就不过多介绍了。

我就说一个将字符串快速转换为字典类型的技巧

# 将复制到 cookies 转换为字典,方便调用
raw_cookies = 'JSESSIONID=RZ13thOM1d   后面省略了,调式的话用自己的  '
cookie = SimpleCookie(raw_cookies)
cookies = {i.key: i.value for i in cookie.values()}

得到了 cookies,我们就能在登录状态下获取数据了。 主要用到的是: requests 模块的 session 下面就看看 v1 版本的整体代码吧 整体代码:

'''
尝试登录支付宝
并获账单记录
'''

import requests
from http.cookies import SimpleCookie
from bs4 import BeautifulSoup


# 自定义 headers
HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
    'Referer': 'https://consumeprod.alipay.com/record/advanced.htm',
    'Host': 'consumeprod.alipay.com',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Connection': 'keep-alive'
}


# 将复制到 cookies 转换为字典,方便调用
raw_cookies = 'JSESSIONID=RZ13thOM1dM5K05460101";     中间省略了     one=RZ250AATO/mr4CZ1cRnxgFmVR'
cookie = SimpleCookie(raw_cookies)
cookies = {i.key: i.value for i in cookie.values()}


# 尝试使用面向对象的方式来造爬虫
class Alipay_Bill_Info(object):
    '''支付宝账单信息'''

    def __init__(self, headers, cookies):
        '''
        类的初始化

        headers:请求头
        cookies: 持久化访问
        info_list: 存储账单信息的列表
        '''
        self.headers = headers
        self.cookies = cookies
        # 利用 requests 库构造持久化请求
        self.session = requests.Session()
        # 将请求头和 cookies 添加到缓存之中
        self.session.headers = self.headers
        self.session.cookies.update(self.cookies)
        self.info_list = []

    def login_status(self):
        '''判断登录状态'''
        status = self.session.get(
            'https://consumeprod.alipay.com/record/standard.htm', timeout=5, allow_redirects=False).status_code
        print(status)
        if status == 200:
            return True
        else:
            return False

    def get_data(self):
        '''
        利用 bs4 库解析 html
        并抓取数据,
        数据以字典格式保存在列表里
        '''
        status = self.login_status()
        url = 'https://consumeprod.alipay.com/record/standard.htm'
        if status:
            html = self.session.get(url).text
            soup = BeautifulSoup(html, 'lxml')
            # 抓取前五个交易记录
            trades = soup.find_all('tr', class_='J-item ')[:5]

            for trade in trades:
                # 做一个 try except 避免异常中断
                try:
                    # 分别找到账单的 时间 金额 以及流水号
                    time = trade.find('p', class_='text-muted').text.strip()
                    amount = trade.find(
                        'span', class_='amount-pay').text.strip()
                    code = trade.find(
                        'a', class_='J-tradeNo-copy J-tradeNo')['title']
                    self.info_list.append(
                        dict(time=time, amount=amount, code=code))
                except:
                    self.info_list.append({'error': '出现错误,请加站长支付宝好友获取充值码'})

        else:
            self.info_list.append({'error': '出现错误,请加站长支付宝好友获取充值码'})



# 测试一下:
test = Alipay_Bill_Info(HEADERS, cookies)
test.get_data()

print(test.info_list)

'''
OUT:

200
[{'time': '07:34', 'amount': '- 3.00', 'code': '2017081521001004100329637047'},
    {'time': '07:08', 'amount': '- 100.00', 'code': '2017081521001004100329622812'},
    {'time': '05:37', 'amount': '+ 0.14', 'code': '20170815344111650101'},
    {'time': '01:08', 'amount': '+ 10.00','code': '20170815200040011100040078948930'},
    {'time': '22:23', 'amount': '+ 10.00', 'code': '20170814200040011100060079678223'}]
'''

可以看到 我需要的前 5 个订单记录的 时间,流水,金额都已经出来了。 来对比一下网页版截图:

订单查询 V2

上面那个爬虫虽然实现了爬取账单信息的功能,

但实际上,完全没有用有没有?

因为 每次都得我们手动输入 cookies

那这个爬虫完全没有存在的 意义了!

当然,这是夸张的说法, 通过上面那个爬虫,我们实践证明了爬取账单的可能性。

剩下的只需要想办法 自动获取 cookies 就行了!

如何自动获取 cookies 呢?

想出的一个比较笨的方法:

说起来很简单有没有?

但实际上我整整调试了一个下午才成功啊!!!

你觉得是为啥呢?

当然是支付宝坑爹的认证机制:

说多了都是辛酸泪,大家调试的时候,注意点就好。

下面给出获取 cookies 的代码片段

    def wait_input(self, ele, str):
        '''减慢账号密码的输入速度'''
        for i in str:
            ele.send_keys(i)
            time.sleep(0.5)

    def get_cookies(self):
        '''获取 cookies'''

        # 初始化浏览器对象
        sel = webdriver.PhantomJS()
        sel.maximize_window()
        sel.get(Login_Url)
        sel.implicitly_wait(3)
        # 找到用户名字输入框
        uname = sel.find_element_by_id('J-input-user')
        uname.clear()
        print('正在输入账号.....')
        self.wait_input(uname, self.user)
        time.sleep(1)
        # 找到密码输入框
        upass = sel.find_element_by_id('password_rsainput')
        upass.clear()
        print('正在输入密码....')
        self.wait_input(upass, self.passwd)
        # 截图查看
        # sel.save_screenshot('1.png')
        # 找到登录按钮
        butten = sel.find_element_by_id('J-login-btn')
        time.sleep(1)
        butten.click()

        # sel.save_screenshot('2.png')
        print(sel.current_url)
        # 跳转到账单页面
        print('正在跳转页面....')
        sel.get(Bill_Url)
        sel.implicitly_wait(3)
        # sel.save_screenshot('3.png')

        # 获取 cookies 并转换为字典类型
        cookies = sel.get_cookies()
        cookies_dict = {}
        for cookie in cookies:
            if 'name' in cookie and 'value' in cookie:
                cookies_dict[cookie['name']] = cookie['value']

        return cookies_dict

        # 关闭浏览器
        sel.close()

    def set_cookies(self):
        '''将获取到的 cookies 加入 session'''
        c = self.get_cookies()
        self.session.cookies.update(c)
        print(self.session.cookies)

这个部份主要是利用了 selenuim 模拟 PhanomJS 来模拟浏览器,登录支付宝账号密码 最后获取 cookies,并格式化。

有详细的注释,详细都能看懂的吧!

对于浏览器模拟爬虫不熟悉的,可以看我以前写的文章:

效果图:

可以看到,我们已经可以完全自动化的查询账单信息了。

剩下的就是后台支付系统的把编写了, 由于不涉及到爬虫的相关技术 我就不 pull 上来啦~

最后附上全部代码:

'''
尝试登录支付宝
并获取账单记录

通过 seleium 登录支付宝,
获取 cookies
'''

import requests
from selenium import webdriver
from bs4 import BeautifulSoup
import time

# 登录 url
Login_Url = 'https://auth.alipay.com/login/index.htm?goto=https%3A%2F%2Fwww.alipay.com%2F'
# 账单 url
Bill_Url = 'https://consumeprod.alipay.com/record/standard.htm'


# 登录用户名和密码
USERNMAE = ''
PASSWD = ''

# 自定义 headers
HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
    'Referer': 'https://consumeprod.alipay.com/record/advanced.htm',
    'Host': 'consumeprod.alipay.com',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Connection': 'keep-alive'
}


class Alipay_Bill_Info(object):
    '''支付宝账单信息'''

    def __init__(self, headers, user, passwd):
        '''
        类的初始化

        headers:请求头
        cookies: 持久化访问
        info_list: 存储账单信息的列表
        '''
        self.headers = headers
        # 初始化用户名和密码
        self.user = user
        self.passwd = passwd
        # 利用 requests 库构造持久化请求
        self.session = requests.Session()
        # 将请求头添加到缓存之中
        self.session.headers = self.headers
        # 初始化存储列表
        self.info_list = []

    def wait_input(self, ele, str):
        '''减慢账号密码的输入速度'''
        for i in str:
            ele.send_keys(i)
            time.sleep(0.5)

    def get_cookies(self):
        '''获取 cookies'''

        # 初始化浏览器对象
        sel = webdriver.PhantomJS()
        sel.maximize_window()
        sel.get(Login_Url)
        sel.implicitly_wait(3)
        # 找到用户名字输入框
        uname = sel.find_element_by_id('J-input-user')
        uname.clear()
        print('正在输入账号.....')
        self.wait_input(uname, self.user)
        time.sleep(1)
        # 找到密码输入框
        upass = sel.find_element_by_id('password_rsainput')
        upass.clear()
        print('正在输入密码....')
        self.wait_input(upass, self.passwd)
        # 截图查看
        # sel.save_screenshot('1.png')
        # 找到登录按钮
        butten = sel.find_element_by_id('J-login-btn')
        time.sleep(1)
        butten.click()

        # sel.save_screenshot('2.png')
        print(sel.current_url)
        # 跳转到账单页面
        print('正在跳转页面....')
        sel.get(Bill_Url)
        sel.implicitly_wait(3)
        # sel.save_screenshot('3.png')

        # 获取 cookies 并转换为字典类型
        cookies = sel.get_cookies()
        cookies_dict = {}
        for cookie in cookies:
            if 'name' in cookie and 'value' in cookie:
                cookies_dict[cookie['name']] = cookie['value']

        return cookies_dict

        # 关闭浏览器
        sel.close()

    def set_cookies(self):
        '''将获取到的 cookies 加入 session'''
        c = self.get_cookies()
        self.session.cookies.update(c)
        print(self.session.cookies)

    def login_status(self):
        '''判断登录状态'''
        # 添加 cookies
        self.set_cookies()
        status = self.session.get(
            Bill_Url, timeout=5, allow_redirects=False).status_code
        print(status)
        if status == 200:
            return True
        else:
            return False

    def get_data(self):
        '''
        利用 bs4 库解析 html
        并抓取数据,
        数据以字典格式保存在列表里
        '''
        status = self.login_status()
        if status:
            html = self.session.get(Bill_Url).text
            soup = BeautifulSoup(html, 'lxml')
            # 抓取前五个交易记录
            trades = soup.find_all('tr', class_='J-item ')[:5]

            for trade in trades:
                # 做一个 try except 避免异常中断
                try:
                    # 分别找到账单的 时间 金额 以及流水号
                    time = trade.find('p', class_='text-muted').text.strip()
                    amount = trade.find(
                        'span', class_='amount-pay').text.strip()
                    code = trade.find(
                        'a', class_='J-tradeNo-copy J-tradeNo')['title']
                    self.info_list.append(
                        dict(time=time, amount=amount, code=code))
                except:
                    self.info_list.append({'error': '出现错误,请加站长支付宝好友获取充值码'})

        else:
            self.info_list.append({'error': '出现错误,请加站长支付宝好友获取充值码'})
        return self.info_list



# test:
test = Alipay_Bill_Info(HEADERS, USERNMAE, PASSWD)

data = test.get_data()

print(data)

每天的学习记录都会 同步更新到: 微信公众号:findyourownway
知乎专栏: https://zhuanlan.zhihu.com/Ehco-python
blog:www.ehcoblog.ml
Github: https://github.com/Ehco1996/Python-crawler

34138 次点击
所在节点    Python
79 条回复
lifeintools
2017-08-16 07:51:48 +08:00
mark 非常感兴趣 谢谢 lz PS: 其实也可以写 chrome 插件来实现这个功能。
thundernet8
2017-08-16 08:09:22 +08:00
我觉得你这个好费劲 实际上支付宝的 cookies 可以持久很长时间有效的 所以我做了这个脚本 https://github.com/thundernet8/AlipayOrdersSupervisor 唯一不好的是隔一段时间换下 cookie
Jasmine2016
2017-08-16 08:41:52 +08:00
我想吐槽。。。登陆太快和太慢都不行,简直没人权
qq292382270
2017-08-16 08:48:39 +08:00
我对个人支付宝的自动付款研究了两年. 已经有两三款很不错的成品,稳定几个月不掉线没问题.另外个人帐号实时数据功能也搞定了...
![Markdown]( http://i1.bvimg.com/606162/f78c742a04a38da2.png)
kikyous
2017-08-16 08:53:57 +08:00
有一种思路是开个淘宝店,出售类似充值卡的东西,然后利用自动发卡平台发送卡号和密码

然后用户自己充入个人帐号

就是把支付环节转移到淘宝上面
est
2017-08-16 08:54:21 +08:00
直接收支付宝的邮件即可。
youthdou
2017-08-16 09:04:22 +08:00
mark 一下。
q409195961
2017-08-16 09:24:17 +08:00
楼主写得 666,有些网站也是直接转账,然后输入流水号充值的。

另:button 不是 butten
audoe
2017-08-16 09:24:49 +08:00
@est 这个思路可以,简单,稳定
LevineChen
2017-08-16 09:34:21 +08:00
楼主的方法,无法处理二次验证吧. 几年前图形验证码好过. 现在改成滑块,但终归也找到合适的方法绕过.
headless 效率较低其实登录完全可以接口形式完成,登录稳定还可以过二次验证
yangxin0
2017-08-16 09:40:21 +08:00
@qq292382270 支付宝不是支持自动付款吗
734506700qq
2017-08-16 09:47:26 +08:00
这个想法不错,可以试试
laoertongzhi
2017-08-16 10:01:52 +08:00
@qq292382270

能看下你这边提供服务的网站么,很好奇
Ehco1996
2017-08-16 10:16:43 +08:00
@thundernet8
原本我也想 做持久化的,可是现在貌似出了啥验证机制,十几分钟 cookie 就挂了。

@audoe
@est
支付宝邮件提醒必须你没有绑定手机号,不然只会再 app 上或者短信提醒。我查到的资料是这样的,一开始我也想通过邮件来检测的

@LevineChen
是的 二次验证不行,请问可以分享一下你的方法么

@qq292382270
膜拜大佬 能分享一下么
LevineChen
2017-08-16 10:18:12 +08:00
@Ehco1996 我怕分享出来大家都没得用了
Ehco1996
2017-08-16 10:20:18 +08:00
@LevineChen

好吧,那还是自己偷偷在地下用好了
嘿嘿嘿
LevineChen
2017-08-16 10:26:42 +08:00
@Ehco1996 既然有人可以实现,你就可以好好研究研究了呀.
qq292382270
2017-08-16 10:52:23 +08:00
@yangxin0 这玩意的命名我一直都很纠结.. 功能就是获取到指定个人号的收款记录.并做成订单处理之类的一系列逻辑...
@laoertongzhi 本来是没有网站的.做出来的程序只是自己几个朋友私下的用..前一个月才开始打算正规做 .. 自己一个人撸网站和 app 的前后台,心力憔悴.. 所以问题来了,还没上线,一些细节问题没考虑好.. 不过最迟这周应该可以发布.. 但应该不会来 V2 这里宣传.. 这玩意有点违法
@Ehco1996 还差一点.. 这周应该可以发布..
zhuf
2017-08-16 10:54:31 +08:00
@qq292382270 有没详细介绍的啊,最近刚好有这需求
qq292382270
2017-08-16 10:57:45 +08:00
@zhuf 我直接手打好咯.. 网站还差一点才上线.. 核心功能是,实时监控支付宝个人号(微信号也支持的),有新到帐订单后,立刻取出订单号,付款名,备注等等信息. 发送到服务器里面. 延迟差不多在 1-3 秒左右.
流程: 服务器创建订单->生成二维码->用户扫码支付或转账->服务器收到监控程序发来的新订单数据 ->处理订单为支付成功.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/383179

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX