爬蟲的新模塊pyppeteer的使用

安裝

python3 -m pip install pyppeteer

最好是py3.5+javascript

手動安裝java

你懂的,天朝網絡環境很複雜,若是要用pyppeteer本身綁定的chromium,半天都下載不下來,因此咱們要手動安裝,而後在程序裏面指定executablePathnode

下載地址python

模塊介紹

啓動pyppeteer.launch

launch 瀏覽器,能夠傳入一個字典來配置幾個options,好比:web

browser = await pyppeteer.launch({
    'headless': False, # 關閉無頭模式
    'devtools': True, # 打開 chromium 的 devtools
    // 'executablePath': '你下載的Chromium.app/Contents/MacOS/Chromiu', //# 瀏覽器的存放地址
    'args': [ 
        '--disable-extensions',
        '--hide-scrollbars',
        '--disable-bundled-ppapi-flash',
        '--mute-audio',
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-gpu',
    ],
    'dumpio': True,  
})
browser = await launch({'headless': False,'dumpio':True, 'autoClose':False,'args': ['--no-sandbox', '--window-size=1366,850']})
await page.setViewport({'width':1366,'height':768})

注入腳本page.evaluate

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

咱們會看到這一步很是關鍵,由於puppeteer出於政策考慮(這個詞用的不是很好,就是那個意思)會設置window.navigator.webdrivertrue,告訴網站我是一個 webdriver 驅動的瀏覽器。有些網站比較聰明(反爬措施作得比較好),就會經過這個來判斷對方是否是爬蟲程序。ajax

截獲response和request

await page.setRequestInterception(True)
page.on('request', intercept_request)
page.on('response', intercept_response)

intercept_requestintercept_response至關因而註冊的兩個回調函數,在瀏覽器發出請求和獲取到請求以前指向這兩個函數。redis

好比能夠這樣禁止獲取圖片、多媒體資源和發起 websocket 請求:chrome

async def intercept_request(req):
    """請求過濾"""
    if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
        await req.abort()
    else:
        await req.continue_()

而後每次獲取到請求以後將內容打印出來(這裏只打印了fetchxhr類型response 的內容):json

async def intercept_response(res):
    resourceType = res.request.resourceType
    if resourceType in ['xhr', 'fetch']:
        resp = await res.text()
        print(resp)

一共有哪些resourceType,pyppeteer文檔裏面有:api

img

頁面自動下拉的代碼

健壯的代碼

請求鉤子

async def request_check(req):
    '''請求過濾'''
    if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
        await req.abort()
    else:
        await req.continue_()

await page.setRequestInterception(True)
page.on('request', request_check)

判斷加載是否完成

async def goto(page, url):
    while True:
        try:
            await page.goto(url, {
                'timeout': 0,
                'waitUntil': 'networkidle0'
            })
            break
        except (pyppeteer.errors.NetworkError,
                pyppeteer.errors.PageError) as ex:
            # 無網絡 'net::ERR_INTERNET_DISCONNECTED','net::ERR_TUNNEL_CONNECTION_FAILED'
            if 'net::' in str(ex):
                await asyncio.sleep(10)
            else:
                rais

注入JS文件

from pathlib import Path

CURDIR = Path(__file__).parent
JS_AJAX_HOOK_LIB = str(CURDIR / 'static' / 'ajaxhook.min.js')

await page.addScriptTag(path=JS_AJAX_HOOK_LIB)

這樣注入的js文件不能有中文,由於 pyppeteer 裏面打開文件用的是默認編碼,能夠 hook 住 open 函數來解決。

# 由於 pyppeteer 庫裏面 addScriptTag 用的是系統默認編碼,致使js文件裏面不能有中文
pyppeteer.frame_manager.open = lambda file: open(file, encoding='utf8')

完整的跑一個小項目

import asyncio
from pyppeteer import launch
import time

async def main():exepath = 'C:/Users/tester02/AppData/Local/Google/Chrome/Application/chrome.exe'
    browser = await launch({'executablePath': exepath, 'headless': False, 'slowMo': 30})
    page = await browser.newPage()
    await page.setViewport({'width': 1366, 'height': 768})
    await page.goto('http://192.168.2.66') // # 請求網址
    await page.type("#Login_Name_Input", "test02") # 第一個參數是選擇器,選中某個元素,第二個參數是要輸入的內容
    await page.type("#Login_Password_Input", "12345678", )
    await page.waitFor(1000) # 停頓
    await page.click("#Login_Login_Btn")
    await page.waitFor(3000)
    await browser.close()# 關閉瀏覽器

asyncio.get_event_loop().run_until_complete(main()) # 執行main函數

登錄谷歌

import asyncio
import time
from pyppeteer import launch


async def gmailLogin(username, password, url):
    #'headless': False若是想要瀏覽器隱藏更改False爲True
    # 127.0.0.1:1080爲代理ip和端口,這個根據本身的本地代理進行更改,若是是vps裏或者全局模式能夠刪除掉'--proxy-server=127.0.0.1:1080'
    browser = await launch({'headless': False, 'args': ['--no-sandbox', '--proxy-server=127.0.0.1:1080']})
    page = await browser.newPage()
    await page.setUserAgent(
        'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36')

    await page.goto(url)

    # 輸入Gmail
    await page.type('#identifierId', username)
    # 點擊下一步
    await page.click('#identifierNext > content')
    page.mouse  # 模擬真實點擊
    time.sleep(10)
    # 輸入password
    await page.type('#password input', password)
    # 點擊下一步
    await page.click('#passwordNext > content > span')
    page.mouse  # 模擬真實點擊
    time.sleep(10)
    # 點擊安全檢測頁面的DONE
    # await page.click('div > content > span')#若是本機以前登陸過,而且page.setUserAgent設置爲以前登陸成功的瀏覽器user-agent了,
    # 就不會出現安全檢測頁面,這裏若是有須要的本身根據需求進行更改,可是仍是推薦先用經常使用瀏覽器登陸成功後再用python程序進行登陸。

    # 登陸成功截圖
    await page.screenshot({'path': './gmail-login.png', 'quality': 100, 'fullPage': True})
    #打開谷歌全家桶跳轉,以Youtube爲例
    await page.goto('https://www.youtube.com')
    time.sleep(10)


if __name__ == '__main__':
    username = '你的gmail包含@gmail.com'
    password = r'你的gmail密碼'
    url = 'https://gmail.com'
    loop = asyncio.get_event_loop()
    loop.run_until_complete(gmailLogin(username, password, url))
# 代碼由三分醉編寫,網址www.sanfenzui.com,參考以下文章:
# https://blog.csdn.net/Chen_chong__/article/details/82950968

登錄淘寶

async def taobao_login(username, password, url):
    """
    淘寶登陸主程序
    :param username: 用戶名
    :param password: 密碼
    :param url: 登陸網址
    :return: 登陸cookies
    """
    ###
    await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
    page.mouse
    time.sleep(1)
    # 輸入用戶名,密碼
    await page.type('#TPL_username_1', username, {'delay': input_time_random() - 50})   # delay是限制輸入的時間
    await page.type('#TPL_password_1', password, {'delay': input_time_random()})
    time.sleep(2)
    # 檢測頁面是否有滑塊。原理是檢測頁面元素。
    slider = await page.Jeval('#nocaptcha', 'node => node.style')  # 是否有滑塊

    if slider:
        print('當前頁面出現滑塊')
        # await page.screenshot({'path': './headless-login-slide.png'}) # 截圖測試
        flag, page = await mouse_slide(page=page)  # js拉動滑塊過去。
        if flag:
            await page.keyboard.press('Enter')  # 確保內容輸入完畢,少數頁面會自動完成按鈕點擊
            print("print enter", flag)
            await page.evaluate('''document.getElementById("J_SubmitStatic").click()''')  # 若是沒法經過回車鍵完成點擊,就調用js模擬點擊登陸按鈕。
            time.sleep(2)
            cookies_list = await page.cookies()
            print(cookies_list)
            return await get_cookie(page)  # 導出cookie 完成登錄後就能夠拿着cookie玩各類各樣的事情了。
    else:
        print("")
        await page.keyboard.press('Enter')
        print("print enter")
        await page.evaluate('''document.getElementById("J_SubmitStatic").click()''')
        await page.waitFor(20)
        await page.waitForNavigation()

        try:
            global error  # 檢測是不是帳號密碼錯誤
            print("error_1:", error)
            error = await page.Jeval('.error', 'node => node.textContent')
            print("error_2:", error)
        except Exception as e:
            error = None
        finally:
            if error:
                print('確保帳戶安全從新入輸入')
                # 程序退出。
                loop.close()
            else:
                print(page.url)
                return await get_cookie(page)

今日頭條文章

import asyncio
from pyppeteer import launch

async def main():
    # headless參數設爲False,則變成有頭模式
    browser = await launch(
        # headless=False
    )
    
    page = await browser.newPage()
    
    # 設置頁面視圖大小
    await page.setViewport(viewport={'width':1280, 'height':800})
    
    # 是否啓用JS,enabled設爲False,則無渲染效果
    await page.setJavaScriptEnabled(enabled=True)
    
    await page.goto('https://www.toutiao.com/')
    
    # 打印頁面cookies
    print(await page.cookies())
    
    # 打印頁面文本
    print(await page.content())
    
    # 打印當前頁標題
    print(await page.title())
    
    # 抓取新聞標題
    title_elements = await page.xpath('//div[@class="title-box"]/a')
    for item in title_elements:
        # 獲取文本
        title_str = await (await item.getProperty('textContent')).jsonValue()
        print(await item.getProperty('textContent'))
        # 獲取連接
        title_link = await (await item.getProperty('href')).jsonValue()
        print(title_str)
        print(title_link)
    
    # 關閉瀏覽器
    await browser.close()
    
asyncio.get_event_loop().run_until_complete(main())

Pyppeteer和Puppeteer的不一樣點

  1. Pyppeteer支持字典和關鍵字傳參,Puppeteer只支持字典傳參
# Puppeteer只支持字典傳參
browser = await launch({'headless': True})
# Pyppeteer支持字典和關鍵字傳參
browser = await launch({'headless': True})
browser = await launch(headless=True)
  1. 元素選擇器方法名 $變爲querySelector
# Puppeteer使用$符
Page.$()/Page.$$()/Page.$x()
# Pyppeteer使用Python風格的函數名
Page.querySelector()/Page.querySelectorAll()/Page.xpath()
# 簡寫方式爲:
Page.J(), Page.JJ(), and Page.Jx()
  1. Page.evaluate() 和 Page.querySelectorEval()的參數

Puppeteer的evaluate()方法使用JavaScript原生函數或JavaScript表達式字符串。Pyppeteer的evaluate()方法只使用JavaScript字符串,該字符串能夠是函數也能夠是表達式,Pyppeteer會進行自動判斷。但有時會判斷錯誤,若是字符串被判斷成了函數,而且報錯,能夠添加選項force_expr=True,強制Pyppeteer做爲表達式處理。

獲取頁面內容:

content = await page.evaluate('document.body.textContent', force_expr=True)

獲取元素的內部文字:

element = await page.querySelector('h1')
title = await page.evaluate('(element) => element.textContent', element)

爬蟲例子

# -*- coding: utf-8 -*-

import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq

# 最好指定一下本身瀏覽器的位置,若是不指定會自動下載,太慢了...
executable_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"


# 示例一: 渲染頁面
async def crawl_page():
    # 打開瀏覽器
    browser = await launch(executablePath=executable_path)

    # 打開tab
    page = await browser.newPage()

    # 輸入網址回車
    await page.goto('http://quotes.toscrape.com/js/')

    # 獲取內容並解析
    doc = pq(await page.content())
    print('Quotes:', doc('.quote').length)

    # 關閉瀏覽器
    await browser.close()


# 示例二:截圖,保存pdf,執行js
async def save_pdf():
    browser = await launch(executablePath=executable_path)
    page = await browser.newPage()
    await page.goto('http://quotes.toscrape.com/js/')

    # 網頁截圖保存
    await page.screenshot(path='example.png')

    # 網頁導出 PDF 保存
    await page.pdf(path='example.pdf')

    # 執行 JavaScript
    dimensions = await page.evaluate('''() => {
            return {
                width: document.documentElement.clientWidth,
                height: document.documentElement.clientHeight,
                deviceScaleFactor: window.devicePixelRatio,
            }
        }''')

    print(dimensions)

    await browser.close()


if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(crawl_page())
    # asyncio.get_event_loop().run_until_complete(save_pdf(

如何創建一個cookie池

必須多個帳號,那麼如何註冊多個淘寶帳號呢。。

  1. 能夠經過第三方提供手機號驗證碼服務商,經過pyppeteer註冊帳號,保存帳號信息
  2. 登陸帳號並保存在redis
  3. 開線程檢查帳號是否已過時,若過時從新登陸便可
相關文章
相關標籤/搜索