部署到服务器上的pyppeteer定时项目

需求

需求分析

部门老大 要求使用爬虫抓取对应网站的公司账号的余额 并且能提供一个API供公司内部其他公示网站调用 原本商议每次请求都用爬虫请求一次,但是最好出于账号安全和系统稳定性考虑决定设置一个定时任务 每两个小时抓取一次 数据存放到缓存当中 但是也需要提供接口

1、登录

在这里插入图片描述
第一个难点 需要登录 我首先想到的是逆向,抓包查看逆向登录,开发过程中途也确实是快实现了 但是奈何这个网站的参数太多而且因为整个网站采取的IFRAME标签加载的H5如果要使用requests+逆向个人感觉很麻烦
而且访问IFRAME内部的H5时又会出现登录
在这里插入图片描述
这直接给我淦懵逼了 难道我还得又逆向一下这个登录? (我带上原本的请求头无法访问到登录之后的界面,具体原因没有深究)
看到这儿我直接就放弃了逆向 直接使用傻瓜包采用无头浏览器的方式获取页面

为什么选择pyppeteer

至于为什么使用pyppeteer而不是selenium 原因是因为爬虫项目需要放到centos上面的啊 selenium 需要按照浏览器 并且下载对应版本的webdriver就这些工作量我直接选择放弃 转头用pyppeteer (当然也有另一个原因 我不确定服务器上能否安装chrome)

安装pyppeteer

建议直接pip安装

pip install pyppeteer

当然不出意料的下载速度很慢 建议换源

数据抓取

爬虫

登录

思路:
1、抓包查看请求接口
2、使用pyppeteer请求该接口
3、等待页面渲染完毕
4、定位账号密码的输入框 并且模拟输入账号密码
5、定位登录按钮 模拟点击登录

await page.type(
        'body > uni-app > uni-layout > uni-content > uni-main > uni-page > uni-page-wrapper > uni-page-body > uni-view > uni-view.login-box-tab > uni-view.uni-container > uni-view.uni-forms.uni-forms--top > uni-form > span > uni-view:nth-child(1) > uni-view > uni-view > uni-view.uni-forms-item__content > uni-input > div > input',
        '你的账号', {'delay': 10})  # 模拟输入,输入时间:10 ms

    # 输入密码
await page.type(
   'body > uni-app > uni-layout > uni-content > uni-main > uni-page > uni-page-wrapper > uni-page-body > uni-view > uni-view.login-box-tab > uni-view.uni-container > uni-view.uni-forms.uni-forms--top > uni-form > span > uni-view.uni-forms-item.icon-container > uni-view > uni-view > uni-view.uni-forms-item__content > uni-input > div > input',
   '你的密码', {'delay': 10})
    # 模拟点击
await page.click(
    'body > uni-app > uni-layout > uni-content > uni-main > uni-page > uni-page-wrapper > uni-page-body > uni-view > uni-view.login-box-tab > uni-view.uni-container > uni-view.uni-forms.uni-forms--top > uni-form > span > uni-view.uni-button-group > uni-button')  # 模拟点击,也可以先定位元素,然后await element.click()
await asyncio.sleep(4)
print('登录成功!')
# 登录成功之后需要跳转到基础配置界面
await page.goto('https://dev.dcloud.net.cn/pages/uniLogin/index')
await asyncio.sleep(2)
print('正在跳转界面')

获取对应页面

思路:
1、抓包查看请求接口
2、使用pyppeteer请求该接口
3、等待页面渲染完毕
4、获取到iframe加载的请求接口
5、请求iframe的请求路径 获取到H5
6、数据获取

html_iframe_str = await page.content()
html_iframe = etree.HTML(html_iframe_str)
iframe_url_list = html_iframe.xpath('//iframe/@src')
if iframe_url_list:
    await page.goto(iframe_url_list[0])
    html_str = await page.content()
    html = etree.HTML(html_str)
    balances = html.xpath('//span[@class="text-pre"]/text()')
    if balances:
        return balances[0]
    else:
        return None
else:
    print(html_iframe)
    return None

其他组件

判断登录是否失效

await page.goto('https://dev.dcloud.net.cn/pages/uniLogin/index')
await asyncio.sleep(3)
title = await page.title()
if title == '登录':
    return 0, page
elif title == '基础配置':
    return 1, page
else:
    return 2, page

初始化

    browser = await launch(
        {'headless': True, 'args': ['--disable-infobars', '--no-sandbox'], 'userDataDir': r'.\test',
         'ignoreDefaultArgs': ['--enable-automation']})
    page = await browser.newPage()

    await page.evaluateOnNewDocument('() =>{ Object.defineProperties(navigator,'
                                     '{ webdriver:{ get: () => false } }) }')
    await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')

    await page.setUserAgent(
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5")
    await page.setViewport(viewport={'width': 1536, 'height': 768})
    return page, browser

析构函数

    await browser.close()
    print('成功退出!')

启动函数

loop = asyncio.get_event_loop()
    task1 = loop.create_task(run())
    task1.add_done_callback(partial(callback))
    loop.run_until_complete(task1)
    result = task1.result()
    data = {
        "data": {
            "account_id": "{}".format(result.get('balance', None))
        }
    }
    print(data)
    return data

完整代码

import asyncio
from pyppeteer import launch
from lxml import etree
from functools import partial
import redis, json, datetime


# 初始化
async def init_func():
    browser = await launch(
        {'headless': False, 'args': ['--disable-infobars', '--no-sandbox'], 'userDataDir': r'.\test',
         'ignoreDefaultArgs': ['--enable-automation']})
    page = await browser.newPage()

    await page.evaluateOnNewDocument('() =>{ Object.defineProperties(navigator,'
                                     '{ webdriver:{ get: () => false } }) }')
    await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')

    await page.setUserAgent(
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5")
    await page.setViewport(viewport={'width': 1536, 'height': 768})
    return page, browser


# 析构
async def del_func(browser):
    await browser.close()
    print('成功退出!')


# 登录
async def login(page):
    #  输入账号
    await page.type(
        'body > uni-app > uni-layout > uni-content > uni-main > uni-page > uni-page-wrapper > uni-page-body > uni-view > uni-view.login-box-tab > uni-view.uni-container > uni-view.uni-forms.uni-forms--top > uni-form > span > uni-view:nth-child(1) > uni-view > uni-view > uni-view.uni-forms-item__content > uni-input > div > input',
        '账号', {'delay': 10})  # 模拟输入,输入时间:10 ms

    # 输入密码
    await page.type(
        'body > uni-app > uni-layout > uni-content > uni-main > uni-page > uni-page-wrapper > uni-page-body > uni-view > uni-view.login-box-tab > uni-view.uni-container > uni-view.uni-forms.uni-forms--top > uni-form > span > uni-view.uni-forms-item.icon-container > uni-view > uni-view > uni-view.uni-forms-item__content > uni-input > div > input',
        '密码', {'delay': 10})
    # 模拟点击
    await page.click(
        'body > uni-app > uni-layout > uni-content > uni-main > uni-page > uni-page-wrapper > uni-page-body > uni-view > uni-view.login-box-tab > uni-view.uni-container > uni-view.uni-forms.uni-forms--top > uni-form > span > uni-view.uni-button-group > uni-button')  # 模拟点击,也可以先定位元素,然后await element.click()
    await asyncio.sleep(8)
    print('登录成功!')
    # 登录成功之后需要跳转到基础配置界面
    await page.goto('https://dev.dcloud.net.cn/pages/uniLogin/index')
    await asyncio.sleep(8)
    print('正在跳转界面')
    return page


# 查看余额
async def get_balance(page):
    html_iframe_str = await page.content()
    html_iframe = etree.HTML(html_iframe_str)
    iframe_url_list = html_iframe.xpath('//iframe/@src')
    if iframe_url_list:
        await page.goto(iframe_url_list[0])
        html_str = await page.content()
        html = etree.HTML(html_str)
        balances = html.xpath('//span[@class="text-pre"]/text()')
        if balances:
            return balances[0]
        else:
            return None
    else:
        print(html_iframe)
        return None


# 判断登录是否失效
async def judge_cookie(page):
    await page.goto('https://dev.dcloud.net.cn/pages/uniLogin/index')
    await asyncio.sleep(8)
    title = await page.title()
    if title == '登录':
        return 0, page
    elif title == '基础配置':
        return 1, page
    else:
        return 2, page


# 回调
def callback(data: dict):
    return data


# 启动
async def run():
    page, browser = await init_func()
    statu_code, page = await judge_cookie(page)
    if statu_code == 0:
        # 重新登录
        page = await login(page)
        balance = await get_balance(page)

    elif statu_code == 1:
        # 直接获取文本
        balance = await get_balance(page)
    else:
        # 页面更改 需要改代码
        balance = '页面更改 需要更改对应脚本'
    await del_func(browser)
    data = {
        'code': statu_code,
        'balance': balance,
    }
    print(data)
    return data


# 外部调用
def run_s():
    loop = asyncio.get_event_loop()
    task1 = loop.create_task(run())
    task1.add_done_callback(partial(callback))
    loop.run_until_complete(task1)
    result = task1.result()
    data = {
        "data": {
            "account_id": "{}".format(result.get('balance', None)),
            'date': str(datetime.datetime.now())
        }
    }
    print(data)
    pool = redis.ConnectionPool(host='redis主机IP', port=端口, password='密码', db=)
    server = redis.Redis(connection_pool=pool)
    server.delete('balance')  # 删除以前的队列
    server.lpush('balance', json.dumps(data))  # 创建本地队列
    return data


if __name__ == '__main__':
    run_s()

API

@app.route('/dcloud1/')
def dcloud_get_balance():
    redis_pool = REDISPOOL(queue_name='balance')
    result = redis_pool.get_test(index=1)
    print(result)
    if result:
        return result
    else:
        return 'redis为空'

直接在redis中取数据

定时任务

直接使用crontab发布定时任务 完成更新

crontab-e

小结

1、最好还是麻烦一点用逆向至少稳定 性能也应该比较好
2、现在这一版比较low 因为要得比较急 没有花时间去仔细弄
3、pyppeteer可以直接在centos中使用
4、pyppeteer支持异步 在高并发上应该比selenium更加的优秀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值