Snippets: Python: Puppeteer: General

 20th August 2020 at 2:19pm

这里通过一段示例代码来描述如何使用 Python 的 puppeteer binding 库 pyppeteer

import asyncio

from pyppeteer import launch


async def main():
    # 启动 Headless Chrome,需要带 `--no-sandbox` 参数,不然会报错
    # 具体原因不详,参考 https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox_development.md
    browser = await launch({'headless': True, 'args': ['--no-sandbox']})
    page = await browser.newPage()
    # 注意,不带 headless 参数时,实时的 viewport 大小并不是这个值,可能受各系统 DPI 参数 / UI 缩放等影响
    await page.setViewport({"width": 1366, "height": 768})

    for enterprise in Enterprise.select().where(Enterprise.status == 'pending'):
        print(f"Running {enterprise.name}")

        # 打开网页
        await page.goto('https://www.szcredit.org.cn/web/index.html')
        # 聚焦
        await page.focus('input[name="txtKeyword"]')
        # 输入
        await page.type('input[name="txtKeyword"]', enterprise.name)
        # 点击按钮
        await page.click('#btnSend')

        # 由于点完按钮会发 AJAX 请求显示验证码,这里强行等 2 秒
        await page.waitFor(2000)

        # 由于 headless 和非 headless 模式下 viewport 不一,为了调试,只好弄两套 clip 参数
        headless_clip = {'x': 550, 'y': 250, 'width': 320, 'height': 175}
        not_headless_clip = {'x': 550, 'y': 250, 'width': 320, 'height': 175}
        clip = headless_clip if HEADLESS else not_headless_clip

        # 截图,即可截全图、滚动截图(加上参数 fullPage: True),也可只截一块;保存在 code.png 上
        # WARNING: 这段代码是为了截验证码图片,但它不是好的实践。参考 wiki 中截取图片的内容
        await page.screenshot({'path': 'code.png', 'clip': clip})

        code = input("Verify Code: ")

        # 网页用了 iframe,从 frame 列表中找到你要的那个
        verify_code_frame = [f for f in page.frames if f.url.endswith('/web/GSPT/ShowCheckCode.aspx')][0]
        await verify_code_frame.focus('#txtCheckCode')
        await verify_code_frame.type('#txtCheckCode', code)
        await page.click('.layui-layer-btn0')
        # 点上面按钮后会有新 tab 弹出,等待加载完成
        await page.waitForNavigation()

        # 等待特定的 DOM 元素出现,但是效果似乎不是想象中的
        await page.waitForSelector('#result', {'visible': True})
        await page.waitFor(2000)

        # 这段代码演示如何从网页结构中提取数据,比较纠结
        anchors = await page.querySelectorAll("#result a")
        for anchor in anchors:
            info = await page.evaluate('(anchor) => Object({"href": anchor.href, "text": anchor.innerText})', anchor)
            if info['text'] == enterprise.name:
                enterprise.sz_credit_url = info['href']
                enterprise.status = 'success'
                print(f"URL of {enterprise.name} saved: {enterprise.sz_credit_url}")
                break
        else:
            enterprise.status = 'skipped'
        enterprise.save()


if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(main())

判断某一 xpath 在页面中是否存在:

anchors = await page.xpath(f'//a[text()="股东登记信息"]')
if anchors:
    print("Exists")
else:
    print("Not exists")