Pyppeteer(1)

一、常規(guī)操作

點(diǎn)擊
  點(diǎn)擊用page.click方法,默認(rèn)是css-selector。

await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')

輸入
  輸入用page.type方法,第一個(gè)參數(shù)是選擇器,第二個(gè)參數(shù)是要輸入的字符串柬甥,第三個(gè)參數(shù)是延時(shí)設(shè)置。

await page.type('#TPL_username_1', '123123', {'delay': input_time_random() - 50})
await page.type('#TPL_password_1', '232322332', {'delay': input_time_random()})
import asyncio
import random
from pyppeteer import launch

def input_time_random():
    return random.randint(100, 151)

async def main():
    browser = await launch({'headless':False})
    page = await browser.newPage()
    await page.goto('https://login.taobao.com')
    await page.waitFor(3 * 1000)
    await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
    await page.waitFor(3 * 1000)
    await page.type('#TPL_username_1', '123123', {'delay': input_time_random() - 50})
    await page.type('#TPL_password_1', '232322332', {'delay': input_time_random()})
    await page.waitFor(3 * 1000)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

滾動(dòng)

await page.evaluate('window.scrollBy(0, window.innerHeight)') #淘寶滾動(dòng)加載用

獲取元素坐標(biāo)

常用于獲取驗(yàn)證碼相關(guān)坐標(biāo)

el = await page.querySelector('#nc_1_n1z')
box = await el.boundingBox()
await page.hover('#nc_1_n1z') #鼠標(biāo)移動(dòng)方塊上
await page.mouse.down() #鼠標(biāo)拖動(dòng)操作包括按下其垄、移動(dòng)暗甥、放開(kāi)
await page.mouse.move(box['x']+1000,box['y'], {'delay': random.randint(1000, 2000),'steps':3})
await page.mouse.up()

二、常用函數(shù)

page.goto(url)

請(qǐng)求指定url
比較常用的用法是:page.goto(url,{'waitUntil':'load'})
waitUntil的參數(shù)有:load,domcontentloaded,networkidle0,networkidle2

networkidle0表示when there are no more than 0 network connections for at least 500 ms.
newwordidle2表示when there are no more than 2 network connections for at least 500 ms

DOM文檔的加載步驟為:

  • 解析HTML結(jié)構(gòu)捉捅。
  • 加載外部腳本和樣式表文件。
  • 解析并執(zhí)行腳本代碼虽风。
  • DOM樹(shù)構(gòu)建完成棒口。 //domcontentloaded
  • 加載圖片等外部文件。
  • 頁(yè)面加載完畢辜膝。 //load

page.waitfor(time)
設(shè)置頁(yè)面等待時(shí)間,單位是毫秒无牵,常用于設(shè)置操作間隔,讓page能加載完成指定目標(biāo)厂抖,如等待3秒鐘:

page.waitfor(3*1000)

page.waitForSelector(selector)/page.waitForXPath(xpath)
等待目標(biāo)元素加載完成茎毁,默認(rèn)timeout是30秒,可以輔助指定位置元素是否已經(jīng)加載完成。

page.waitForNavigation()
等到某動(dòng)作完成七蜘,常用的是配合其他動(dòng)作一起使用谭溉,如:

await asyncio.wait([
    page.click('a.my-link'),
    page.waitForNavigation(),
])

這段代碼表示,等待連接點(diǎn)擊并跳轉(zhuǎn)完成橡卤。

page.J(css selector)/page.querySelector(css selector)
通過(guò)css selector定位元素扮念,前面是縮寫(xiě)函數(shù)

page.Jx(xpath)/page.xpath(xpath)
通過(guò)xpath定位元素,前面是縮寫(xiě)函數(shù)

page.content()
獲取頁(yè)面當(dāng)前加載網(wǎng)頁(yè)的document,用法:

doc = await page.content()

page.cookies()
獲取頁(yè)面當(dāng)前的cookies,常用如:

...登錄后...
cookies= await page.cookies()
dosomething(cookies)

page.eveluate(jsstr)
執(zhí)行js碧库,js代碼用字符串書(shū)寫(xiě)柜与,注意引號(hào)的使用

page.evaluateOnNewDocument(jsstr)
用法同上,不過(guò)在頁(yè)面新打開(kāi)一個(gè)document時(shí)才生效嵌灰,上面的函數(shù)是當(dāng)前document生效弄匕。

page.hover(selector)
指針移動(dòng)到selector定位的元素位置

page.screenshot()
頁(yè)面截屏

page.setCacheEnabled()
是否啟用緩存,默認(rèn)是True

page.setJavaScriptEnabled()
是否允許加載js,默認(rèn)是True

page.setRequestInterception()
是否允許請(qǐng)求和返回注入沽瞭,默認(rèn)是False

page.setUserAgent()
設(shè)置UA

page.setViewport()
用法:

await page.setViewport({'width':xx,'height':xx})

2. 元素選擇器方法名 $變?yōu)閝uerySelector

# Pyppeteer使用Python風(fēng)格的函數(shù)名
Page.querySelector()
Page.querySelectorAll()
Page.xpath() 

# 簡(jiǎn)寫(xiě)方式為:
Page.J(), Page.JJ(), and Page.Jx()

Page.evaluate() 和 Page.querySelectorEval()的參數(shù)

Pyppeteer的evaluate()方法只使用JavaScript字符串迁匠,該字符串可以是函數(shù)也可以是表達(dá)式,Pyppeteer會(huì)進(jìn)行自動(dòng)判斷秕脓。但有時(shí)會(huì)判斷錯(cuò)誤柒瓣,如果字符串被判斷成了函數(shù),并且報(bào)錯(cuò)吠架,可以添加選項(xiàng)force_expr=True芙贫,強(qiáng)制Pyppeteer作為表達(dá)式處理。

獲取頁(yè)面內(nèi)容:

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

獲取元素的內(nèi)部文字:

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

示例

import asyncio
from pyppeteer import launch


async def main():
    # headless參數(shù)設(shè)為False傍药,則變成有頭模式
    # Pyppeteer支持字典和關(guān)鍵字傳參磺平,Puppeteer只支持字典傳參

    # 指定引擎路徑
    # exepath = r'C:\Users\Administrator\AppData\Local\pyppeteer\pyppeteer\local-chromium\575458\chrome-win32/chrome.exe'
    # browser = await launch({'executablePath': exepath, 'headless': False, 'slowMo': 30})

    browser = await launch(
        # headless=False,
        {'headless': False}
    )
    page = await browser.newPage()
    await page.setViewport(viewport={'width': 1280, 'height': 800})         # 設(shè)置頁(yè)面視圖大小
    await page.setJavaScriptEnabled(enabled=True)       # 是否啟用JS,enabled設(shè)為False拐辽,則無(wú)渲染效果
    res = await page.goto('https://www.toutiao.com/', options={'timeout': 1000})# 超時(shí)間見(jiàn) 1000 毫秒
    resp_headers = res.headers                              # 響應(yīng)頭
    resp_status = res.status                                # 響應(yīng)狀態(tài)

    # 等待
    await asyncio.sleep(2)
    # 第二種方法拣挪,在while循環(huán)里強(qiáng)行查詢某元素進(jìn)行等待
    while not await page.querySelector('.t'):
        pass

    await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')# 滾動(dòng)到頁(yè)面底部
    await asyncio.sleep(2)
    await page.screenshot({'path': 'toutiao.png'})          # 截圖 保存圖片
    print(await page.cookies())                             # 打印頁(yè)面cookies

    """  打印頁(yè)面文本 """
    print(await page.content())                             # 獲取所有 html 內(nèi)容

    # 在網(wǎng)頁(yè)上執(zhí)行js 腳本
    dimensions = await page.evaluate(pageFunction='''() => {
            return {
                width: document.documentElement.clientWidth,        // 頁(yè)面寬度
                height: document.documentElement.clientHeight,      // 頁(yè)面高度
                deviceScaleFactor: window.devicePixelRatio,         // 像素比 1.0000000149011612
            }
        }''', force_expr=False)                                     # force_expr=False  執(zhí)行的是函數(shù)
    print(dimensions)

    #  只獲取文本  執(zhí)行 js 腳本  force_expr為T(mén)rue則執(zhí)行的是表達(dá)式
    content = await page.evaluate(pageFunction='document.body.textContent', force_expr=True)
    print(content)
    print(await page.title())                                           # 打印當(dāng)前頁(yè)標(biāo)題

    # 抓取新聞內(nèi)容  可以使用 xpath 表達(dá)式
    """
    # Pyppeteer 三種解析方式
    Page.querySelector()        # 選擇器
    Page.querySelectorAll()
    Page.xpath()                # xpath  表達(dá)式
    
    # 簡(jiǎn)寫(xiě)方式為:
    Page.J(), Page.JJ(), and Page.Jx()
    """
    element = await page.querySelector(".feed-infinite-wrapper > ul>li")  # 紙抓取一個(gè)
    print(element)
    # 獲取所有文本內(nèi)容  執(zhí)行 js
    content = await page.evaluate('(element) => element.textContent', element)
    print(content)

    # elements = await page.xpath('//div[@class="title-box"]/a')
    elements = await page.querySelectorAll(".title-box a")
    for item in elements:
        print(await item.getProperty('textContent'))
        # <pyppeteer.execution_context.JSHandle object at 0x000002220E7FE518>

        # 獲取文本
        title_str = await (await item.getProperty('textContent')).jsonValue()

        # 獲取鏈接
        title_link = await (await item.getProperty('href')).jsonValue()
        print(title_str)
        print(title_link)

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


asyncio.get_event_loop().run_until_complete(main())
import asyncio
import pyppeteer
from collections import namedtuple
 
headers = {
    'date': 'Sun, 28 Apr 2019 06:50:20 GMT',
    'server': 'Cmcc',
    'x-frame-options': 'SAMEORIGIN\nSAMEORIGIN',
    'last-modified': 'Fri, 26 Apr 2019 09:58:09 GMT',
    'accept-ranges': 'bytes',
    'cache-control': 'max-age=43200',
    'expires': 'Sun, 28 Apr 2019 18:50:20 GMT',
    'vary': 'Accept-Encoding,User-Agent',
    'content-encoding': 'gzip',
    'content-length': '19823',
    'content-type': 'text/html',
    'connection': 'Keep-alive',
    'via': '1.1 ID-0314217270751344 uproxy-17'
}
 
Response = namedtuple("rs", "title url html cookies headers history status")
 
 
async def get_html(url):
    browser = await pyppeteer.launch(headless=True, args=['--no-sandbox'])
    page = await browser.newPage()
    res = await page.goto(url, options={'timeout': 10000})
    data = await page.content()
    title = await page.title()
    resp_cookies = await page.cookies()  # cookie
    resp_headers = res.headers  # 響應(yīng)頭
    resp_status = res.status  # 響應(yīng)狀態(tài)
    print(data)
    print(title)
    print(resp_headers)
    print(resp_status)
    return title
 
 
if __name__ == '__main__':
    url_list = [
        "https://www.toutiao.com",
        "http://jandan.net/ooxx/page-8#comments",
        "https://www.12306.cn/index"
    ]
    task = [get_html(url) for url in url_list]
 
    loop = asyncio.get_event_loop()
    results = loop.run_until_complete(asyncio.gather(*task))
    for res in results:
        print(res)

三、快速上手

例一:爬取http://quotes.toscrape.com/js/ 全部頁(yè)面數(shù)據(jù)

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

async def main():
    browser = await launch()
    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()

asyncio.get_event_loop().run_until_complete(main())

運(yùn)行結(jié)果:10
1. launch 方法會(huì)新建一個(gè) Browser 對(duì)象;
2. 調(diào)用 newPage方法相當(dāng)于瀏覽器中新建了一個(gè)選項(xiàng)卡俱诸,同時(shí)新建了一個(gè) Page 對(duì)象;
3. 然后Page對(duì)象調(diào)用了goto方法就相當(dāng)于在瀏覽器中輸入了這個(gè)URL菠劝,瀏覽器跳轉(zhuǎn)到了對(duì)應(yīng)的頁(yè)面進(jìn)行加載;
4. 加載完成之后再調(diào)用 content 方法,返回當(dāng)前瀏覽器頁(yè)面的源代碼;
5. 然后進(jìn)一步地睁搭,我們用 pyquery 進(jìn)行同樣地解析赶诊,就可以得到 JavaScript 渲染的結(jié)果了;

在這個(gè)過(guò)程中,我們沒(méi)有配置 Chrome 瀏覽器园骆,沒(méi)有配置瀏覽器驅(qū)動(dòng)舔痪,免去了一些繁瑣的步驟,同樣達(dá)到了 Selenium 的效果锌唾,還實(shí)現(xiàn)了異步抓取锄码,爽歪歪!

例二:模擬網(wǎng)頁(yè)截圖,保存 PDF滋捶,另外還可以執(zhí)行自定義的 JavaScript 獲得特定的內(nèi)容

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('http://quotes.toscrape.com/js/')
    await page.screenshot(path='example.png')
    await page.pdf(path='example.pdf')
    dimensions = await page.evaluate('''() => {
        return {
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight,
            deviceScaleFactor: window.devicePixelRatio,
        }
    }''')

    print(dimensions)
    # >>> {'width': 800, 'height': 600, 'deviceScaleFactor': 1}
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

這里我們又用到了幾個(gè)新的 API痛悯,完成**了網(wǎng)頁(yè)截圖保存、網(wǎng)頁(yè)導(dǎo)出 PDF 保存炬太、執(zhí)行 JavaScript **并返回對(duì)應(yīng)數(shù)據(jù)灸蟆。

  • 首先 screenshot 方法可以傳入保存的圖片路徑,另外還可以指定保存格式 type亲族、清晰度 quality炒考、是否全屏 fullPage、裁切 clip 等各個(gè)參數(shù)實(shí)現(xiàn)截圖霎迫。
  • 然后斋枢,可見(jiàn)其內(nèi)容也是 JavaScript 渲染后的內(nèi)容,另外這個(gè)方法還可以指定放縮大小 scale知给、頁(yè)碼范圍 pageRanges瓤帚、寬高 width 和 height、方向 landscape 等等參數(shù)涩赢,導(dǎo)出定制化的 pdf 用這個(gè)方法就十分方便戈次。
  • 最后我們又調(diào)用了 evaluate 方法執(zhí)行了一些 JavaScript,JavaScript 傳入的是一個(gè)函數(shù)筒扒,使用 return 方法返回了網(wǎng)頁(yè)的寬高怯邪、像素大小比率三個(gè)值,最后得到的是個(gè) JSON 格式的對(duì)象花墩,內(nèi)容如下:
{'width': 800, 'height': 600, 'deviceScaleFactor': 1}

總之悬秉,利用 Pyppeteer 我們可以控制瀏覽器執(zhí)行幾乎所有動(dòng)作,想要的操作和功能基本都可以實(shí)現(xiàn)冰蘑,用它來(lái)自由地控制爬蟲(chóng)當(dāng)然就不在話下了和泌。

例三:今日頭條

import asyncio
from pyppeteer import launch

async def main():
    # headless參數(shù)設(shè)為False,則變成有頭模式
    browser = await launch(
        # headless=False
    )
    
    page = await browser.newPage()
    
    # 設(shè)置頁(yè)面視圖大小
    await page.setViewport(viewport={'width':1280, 'height':800})
    
    # 是否啟用JS祠肥,enabled設(shè)為False武氓,則無(wú)渲染效果
    await page.setJavaScriptEnabled(enabled=True)
    
    await page.goto('https://www.toutiao.com/')
    
    # 打印頁(yè)面cookies
    print(await page.cookies())
    
    # 打印頁(yè)面文本
    print(await page.content())
    
    # 打印當(dāng)前頁(yè)標(biāo)題
    print(await page.title())
    
    # 抓取新聞標(biāo)題
    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)
    
    # 關(guān)閉瀏覽器
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

例四:與百度首頁(yè)交互

import time
import asyncio
from pyppeteer import launch


async def main():
    browser = await launch(headless=False)
    page = await browser.newPage()
    await page.setViewport({'width': 1200, 'height': 800})
    await page.goto('https://www.baidu.com')
    await page.type('input#kw.s_ipt', 'python')             # 在搜索框中輸入python
    await page.click('input#su')                            # 點(diǎn)擊搜索按鈕

    # 第一種方法等待元素加載,強(qiáng)行等待5秒
    # await asyncio.sleep(5)

    # 第二種方法等待元素加載仇箱,在while循環(huán)里強(qiáng)行查詢某元素進(jìn)行等待
    while not await page.querySelector('.t'):
        pass

    # 滾動(dòng)到頁(yè)面底部
    await page.evaluate('window.scrollBy(0, window.innerHeight)')

    # 這些等待方法都不好用
    # await page.waitForXPath('h3', timeout=300)
    # await page.waitForNavigation(waitUntil="networkidle0")
    # await page.waitForFunction('document.getElementByTag("h3")')
    # await page.waitForSelector('.t')
    # await page.waitFor('document.querySelector("#t")')
    # await page.waitForNavigation(waitUntil='networkidle0')
    # await page.waitForFunction('document.querySelector("").inner??Text.length == 7')

    title_elements = await page.xpath('//h3[contains(@class,"t")]/a')
    for item in title_elements:
        title_str = await (await item.getProperty('textContent')).jsonValue()
        print(title_str)
    await browser.close()


asyncio.get_event_loop().run_until_complete(main())

問(wèn)題1:css選擇器定位

image

image

問(wèn)題2:找各個(gè)標(biāo)題

.t就是這些標(biāo)題所在

image
image

例五:

class GetJsEncryptPage():
?
    def __init__(self):
        self.loop = asyncio.get_event_loop()
        self.log = ICrawlerLog('spider').save
?
    async def main(self, url, ):  # 定義main協(xié)程函數(shù)县恕,
        # 以下使用await 可以針對(duì)耗時(shí)的操作進(jìn)行掛起
        browser = await launch({'headless': True, 'args': ['--no-sandbox', '--disable-infobars',
                                                           # '--proxy-server={}'.format(get_ip()),
                                                           ],})  # 啟動(dòng)pyppeteer 屬于內(nèi)存中實(shí)現(xiàn)交互的模擬器
        page = await browser.newPage()  # 啟動(dòng)個(gè)新的瀏覽器頁(yè)面標(biāo)簽
        await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36")
        cookies = {}
        try:
            await page.goto(url)  # 訪問(wèn)頁(yè)面
            # 始終讓window.navigator.webdriver=false
            # navigator是windiw對(duì)象的一個(gè)屬性,同時(shí)修改plugins工碾,languages,navigator 且讓
            # await page.setJavaScriptEnabled(enabled=True)  # 使用 JS 渲染
            await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')  # 以下為插入中間js百姓,將淘寶會(huì)為了檢測(cè)瀏覽器而調(diào)用的js修改其結(jié)果渊额。
            await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {},  }; }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
            await page.goto(url)  # 訪問(wèn)頁(yè)面
            # content = await page.content()  # 獲取頁(yè)面內(nèi)容
            await asyncio.sleep(2)
        except:
            await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
            await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {},  }; }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
            # await page.evaluate('window.open("{}");'.format(url))
            await page.evaluate('window.location="{}";'.format(url))
            # await page.goto(url)  # 訪問(wèn)登錄頁(yè)面
        try:
            cookies = await self.get_cookie(page)
        except Exception as e:
            await browser.close()
        finally:
            await browser.close()
?
        return cookies
?
    async def get_cookie(self, page):
        # res = await page.content()
        cookies_list = await page.cookies()
        cookies = {}
        for cookie in cookies_list:
            cookies[cookie.get('name')] =  cookie.get('value')
        return cookies
?
    def retry_if_result_none(self, result):
        return result is None
?
    def input_time_random(self, ):
        return random.randint(100, 151)
?
    def work(self, url):
        pass
?
    def run(self, url, func):
        result = {}
        try:
            # task = asyncio.wait([])
            result = self.loop.run_until_complete(func(url))  # 將協(xié)程注冊(cè)到事件循環(huán),并啟動(dòng)事件循環(huán)
        except Exception as e:
            self.log.info('協(xié)程被動(dòng)結(jié)束, chrome關(guān)閉')
            for task in asyncio.Task.all_tasks():
                task.cancel()
                self.loop.stop()
                self.loop.run_forever()
        # self.loop.close()
        return result

四、詳細(xì)用法

https://miyakogi.github.io/pyppeteer/reference.html

1. 開(kāi)啟瀏覽器

啟動(dòng) Chrome 進(jìn)程并返回瀏覽器實(shí)例

使用 Pyppeteer 的第一步便是啟動(dòng)瀏覽器旬迹,首先我們看下怎樣啟動(dòng)一個(gè)瀏覽器火惊,其實(shí)就相當(dāng)于我們點(diǎn)擊桌面上的瀏覽器圖標(biāo)一樣,把它開(kāi)起來(lái)奔垦。用 Pyppeteer 完成同樣的操作屹耐,只需要調(diào)用 launch 方法即可。

我們先看下 launch 方法的 API椿猎,鏈接為:https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.launcher.launch惶岭,其方法定義如下:

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">pyppeteer.launcher.launch(options: dict = None, **kwargs) → pyppeteer.browser.Browser</pre>

可以看到它處于 launcher 模塊中,參數(shù)沒(méi)有在聲明中特別指定犯眠,返回類(lèi)型是 browser 模塊中的 Browser 對(duì)象按灶,也就是說(shuō)瀏覽器對(duì)象實(shí)例。另外觀察源碼發(fā)現(xiàn)這是一個(gè) async 修飾的方法筐咧,所以調(diào)用它的時(shí)候需要使用 await鸯旁。

調(diào)用 launch 方法即可,相關(guān)參數(shù)介紹:

  • ignoreHTTPSErrors (bool): 是否要忽略 HTTPS 的錯(cuò)誤量蕊,默認(rèn)是 False铺罢。
  • headless (bool): 是否啟用 Headless 模式曼库,即無(wú)界面模式伐庭,如果 devtools 這個(gè)參數(shù)是 True 的話义钉,那么該參數(shù)就會(huì)被設(shè)置為 False念颈,否則為 True娱节,即默認(rèn)是開(kāi)啟無(wú)界面模式的衰琐。
  • executablePath (str): 可執(zhí)行文件的路徑吗垮,如果指定之后就不需要使用默認(rèn)的 Chromium 了葫盼,可以指定為已有的 Chrome 或 Chromium蛋勺。
  • slowMo (int|float): 通過(guò)傳入指定的時(shí)間瓦灶,可以減緩 Pyppeteer 的一些模擬操作。
  • args (List[str]): 在執(zhí)行過(guò)程中可以傳入的額外參數(shù)抱完。
  • ignoreDefaultArgs (bool): 不使用 Pyppeteer 的默認(rèn)參數(shù)贼陶,如果使用了這個(gè)參數(shù),那么最好通過(guò) args 參數(shù)來(lái)設(shè)定一些參數(shù)巧娱,否則可能會(huì)出現(xiàn)一些意想不到的問(wèn)題碉怔。這個(gè)參數(shù)相對(duì)比較危險(xiǎn),慎用禁添。
  • handleSIGINT (bool): 是否響應(yīng) SIGINT 信號(hào)撮胧,也就是可以使用 Ctrl + C 來(lái)終止瀏覽器程序,默認(rèn)是 True老翘。
  • handleSIGTERM (bool): 是否響應(yīng) SIGTERM 信號(hào)芹啥,一般是 kill 命令锻离,默認(rèn)是 True。
  • handleSIGHUP (bool): 是否響應(yīng) SIGHUP 信號(hào)墓怀,即掛起信號(hào)汽纠,比如終端退出操作,默認(rèn)是 True傀履。
  • dumpio (bool): 是否將 Pyppeteer 的輸出內(nèi)容傳給 process.stdout 和 process.stderr 對(duì)象虱朵,默認(rèn)是 False。
  • userDataDir (str): 即用戶數(shù)據(jù)文件夾钓账,即可以保留一些個(gè)性化配置和操作記錄碴犬。
  • env (dict): 環(huán)境變量,可以通過(guò)字典形式傳入官扣。
  • devtools (bool): 是否為每一個(gè)頁(yè)面自動(dòng)開(kāi)啟調(diào)試工具翅敌,默認(rèn)是 False。如果這個(gè)參數(shù)設(shè)置為 True惕蹄,那么 headless 參數(shù)就會(huì)無(wú)效蚯涮,會(huì)被強(qiáng)制設(shè)置為 False。
  • logLevel (int|str): 日志級(jí)別卖陵,默認(rèn)和 root logger 對(duì)象的級(jí)別相同遭顶。
  • autoClose (bool): 當(dāng)一些命令執(zhí)行完之后,是否自動(dòng)關(guān)閉瀏覽器泪蔫,默認(rèn)是 True棒旗。
  • loop (asyncio.AbstractEventLoop): 時(shí)間循環(huán)對(duì)象。
  • devtools (bool): 是否為每一個(gè)頁(yè)面自動(dòng)開(kāi)啟調(diào)試工具撩荣,默認(rèn)是 False铣揉。如果這個(gè)參數(shù)設(shè)置為 True,那么 headless 參數(shù)就會(huì)無(wú)效餐曹,會(huì)被強(qiáng)制設(shè)置為 False逛拱。

好了,知道這些參數(shù)之后台猴,我們可以先試試看朽合。

示例一:首先可以試用下最常用的參數(shù) headless,如果我們將它設(shè)置為 True 或者默認(rèn)不設(shè)置它饱狂,在啟動(dòng)的時(shí)候我們是看不到任何界面的曹步,如果把它設(shè)置為 False,那么在啟動(dòng)的時(shí)候就可以看到界面了休讳,一般我們在調(diào)試的時(shí)候會(huì)把它設(shè)置為 False讲婚,在生產(chǎn)環(huán)境上就可以設(shè)置為 True,我們先嘗試一下關(guān)閉 headless 模式:

import asyncio
from pyppeteer import launch

async def main():
    await launch(headless=False)
    await asyncio.sleep(100)

asyncio.get_event_loop().run_until_complete(main())

運(yùn)行之后看不到任何控制臺(tái)輸出俊柔,但是這時(shí)候就會(huì)出現(xiàn)一個(gè)空白的 Chromium 界面了筹麸。但是可以看到這就是一個(gè)光禿禿的瀏覽器而已纳猫,看一下相關(guān)信息


image.png

看到了,這就是 Chromium竹捉,上面還寫(xiě)了開(kāi)發(fā)者內(nèi)部版本,可以認(rèn)為是開(kāi)發(fā)版的 Chrome 瀏覽器就好尚骄。

示例二:開(kāi)啟調(diào)試模式块差。比如在寫(xiě)爬蟲(chóng)的時(shí)候會(huì)經(jīng)常需要分析網(wǎng)頁(yè)結(jié)構(gòu)還有網(wǎng)絡(luò)請(qǐng)求,所以開(kāi)啟調(diào)試工具還是很有必要的倔丈,我們可以將 devtools 參數(shù)設(shè)置為 True憨闰,這樣每開(kāi)啟一個(gè)界面就會(huì)彈出一個(gè)調(diào)試窗口,非常方便需五,示例如下:

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch(devtools=True)
    page = await browser.newPage()
    await page.goto('https://www.baidu.com')
    await asyncio.sleep(100)

asyncio.get_event_loop().run_until_complete(main())

剛才說(shuō)過(guò) devtools 這個(gè)參數(shù)如果設(shè)置為了 True鹉动,那么 headless 就會(huì)被關(guān)閉了,界面始終會(huì)顯現(xiàn)出來(lái)宏邮。在這里我們新建了一個(gè)頁(yè)面泽示,打開(kāi)了百度,界面運(yùn)行效果如下:


示例三:可以看到上面的一條提示:"Chrome 正受到自動(dòng)測(cè)試軟件的控制"蜜氨,這個(gè)提示條有點(diǎn)煩械筛,那咋關(guān)閉呢?這時(shí)候就需要用到 args 參數(shù)了飒炎,禁用操作如下

browser = await launch(headless=False, args=['--disable-infobars'])

另外有人就說(shuō)了埋哟,這里你只是把提示關(guān)閉了,有些網(wǎng)站還是會(huì)檢測(cè)到是 webdriver 吧郎汪,比如淘寶檢測(cè)到是 webdriver 就會(huì)禁止登錄了赤赊,我們可以試試:

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch(headless=False)
    page = await browser.newPage()
    await page.goto('https://www.taobao.com')
    await asyncio.sleep(100)

asyncio.get_event_loop().run_until_complete(main())

運(yùn)行時(shí)候進(jìn)行一下登錄,然后就會(huì)彈出滑塊煞赢,自己手動(dòng)拖動(dòng)一下抛计,然后就報(bào)錯(cuò)了,界面如下:

image.png

2. 最大化窗口

如果你運(yùn)行了上面的代碼耕驰,你會(huì)發(fā)現(xiàn)爷辱,打開(kāi)的頁(yè)面只在窗口左上角一小塊顯示,看著很別扭朦肘,這是因?yàn)閜yppeteer默認(rèn)窗口大小是800*600饭弓,所以,調(diào)整一下吧媒抠。需要設(shè)置下 window-size 還有 viewport弟断,代碼如下:

import asyncio
from pyppeteer import launch

width, height = 1366, 768

async def main():
    browser = await launch(headless=False,args=[f'--window-size={width},{height}'])
    page = await browser.newPage()
    await page.setViewport({'width': width, 'height': height})
    await page.goto('https://www.taobao.com')
    await asyncio.sleep(100)

asyncio.get_event_loop().run_until_complete(main())

這樣整個(gè)界面就正常了:

image.png

3. 執(zhí)行js腳本

(1)規(guī)避webdriver檢測(cè):

有時(shí)候,為了達(dá)成某些目的(例如屏蔽網(wǎng)站原有js)趴生,我們不可避免得需要執(zhí)行一些js腳本阀趴。執(zhí)行js腳本通過(guò)evaluate方法昏翰。如下所示,我們通過(guò)js來(lái)修改window.navigator.webdriver屬性的值刘急,由此繞過(guò)網(wǎng)站對(duì)webdriver的檢測(cè):

import asyncio
from pyppeteer import launch
 
async def main():
js1 = '''() =>{
 
    Object.defineProperties(navigator,{
    webdriver:{
        get: () => false
        }
    })
}'''
 
js2 = '''() => {
    alert (
        window.navigator.webdriver
    )
}'''
browser = await launch({'headless':False, 'args':['--no-sandbox'],})
 
page = await browser.newPage()
await page.goto('https://h5.ele.me/login/')
await page.evaluate(js1)
await page.evaluate(js2)
 
asyncio.get_event_loop().run_until_complete(main())

在上面代碼中棚菊,通過(guò)page.evalute方法執(zhí)行了兩段js腳本,第一段腳本將webdriver的屬性值設(shè)為false叔汁,第二段代碼在此讀取 webdriver屬性值统求,輸出為false。

OK据块,那剛才所說(shuō)的 webdriver 檢測(cè)問(wèn)題怎樣來(lái)解決呢码邻?其實(shí)淘寶主要通過(guò) window.navigator.webdriver 來(lái)對(duì) webdriver 進(jìn)行檢測(cè),所以我們只需要使用 JavaScript 將它設(shè)置為 false 即可另假,代碼如下:

import asyncio
from pyppeteer import launch


async def main():
    browser = await launch(headless=False, args=['--disable-infobars'])
    page = await browser.newPage()
    await page.goto('https://login.taobao.com/member/login.jhtml?redirectURL=https://www.taobao.com/')
    await page.evaluate(
        '''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
    await asyncio.sleep(100)

asyncio.get_event_loop().run_until_complete(main())

Object.defineProperty()
  會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性像屋,或者修改一個(gè)對(duì)象的現(xiàn)有屬性,并返回這個(gè)對(duì)象边篮。如果不指定configurable, writable, enumerable 己莺,則這些屬性默認(rèn)值為false,如果不指定value, get, set戈轿,則這些屬性默認(rèn)值為undefined

這里沒(méi)加輸入用戶名密碼的代碼篇恒,當(dāng)然后面可以自行添加,下面打開(kāi)之后凶杖,我們點(diǎn)擊輸入用戶名密碼胁艰,然后這時(shí)候會(huì)出現(xiàn)一個(gè)滑動(dòng)條,這里滑動(dòng)的話智蝠,就可以通過(guò)了腾么,如圖所示:

import asyncio
import random
from pyppeteer import launch

def input_time_random():
    return random.randint(100, 151)

async def main():
    browser = await launch({'headless':False})
    page = await browser.newPage()
    await page.evaluateOnNewDocument(
        '''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
    await page.evaluateOnNewDocument('''() =>{ window.navigator.chrome = { runtime: {},  }; }''')
    await page.evaluateOnNewDocument('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
    await page.evaluateOnNewDocument('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')

    await page.goto('https://login.taobao.com')
    await page.waitFor(4 * 1000)
    # await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
    await page.waitFor(3 * 1000)
    await page.type('#TPL_username_1', '123123', {'delay': input_time_random() - 50})
    await page.type('#TPL_password_1', '232322332', {'delay': input_time_random()})
    await page.waitFor(2 * 1000)
    el = await page.querySelector('#nc_1_n1z')
    box = await el.boundingBox()
    await page.hover('#nc_1_n1z')
    await page.mouse.down()
    await page.mouse.move(box['x']+1000,box['y'], {'delay': random.randint(1000, 2000),'steps':3})
    await page.mouse.up()
    await page.waitFor(5 * 1000)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

OK,這樣的話我們就成功規(guī)避了 webdriver 的檢測(cè)杈湾,使用鼠標(biāo)拖動(dòng)模擬就可以完成淘寶的登錄了解虱。

還有另一種方法可以進(jìn)一步免去淘寶登錄的煩惱,那就是設(shè)置用戶目錄漆撞。平時(shí)我們已經(jīng)注意到殴泰,當(dāng)我們登錄淘寶之后,如果下次再次打開(kāi)瀏覽器發(fā)現(xiàn)還是登錄的狀態(tài)浮驳。這是因?yàn)樘詫毜囊恍╆P(guān)鍵 Cookies 已經(jīng)保存到本地了悍汛,下次登錄的時(shí)候可以直接讀取并保持登錄狀態(tài)。那么這些信息保存在哪里了呢至会?其實(shí)就是保存在用戶目錄下了离咐,里面不僅包含了瀏覽器的基本配置信息,還有一些 Cache奉件、Cookies 等各種信息都在里面宵蛀,如果我們能在瀏覽器啟動(dòng)的時(shí)候讀取這些信息昆著,那么啟動(dòng)的時(shí)候就可以恢復(fù)一些歷史記錄甚至一些登錄狀態(tài)信息了。這也就解決了一個(gè)問(wèn)題:很多朋友在每次動(dòng) Selenium 或 Pyppeteer 的時(shí)候總是是一個(gè)全新的瀏覽器术陶,那就是沒(méi)有設(shè)置用戶目錄凑懂,如果設(shè)置了它,每次打開(kāi)就不再是一個(gè)全新的瀏覽器了梧宫,它可以恢復(fù)之前的歷史記錄征候,也可以恢復(fù)很多網(wǎng)站的登錄信息。

那么這個(gè)怎么來(lái)做呢祟敛?很簡(jiǎn)單,在啟動(dòng)的時(shí)候設(shè)置 userDataDir 就好了兆解,示例如下:

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch(headless=False, userDataDir='./userdata', args=['--disable-infobars'])
    page = await browser.newPage()
    await page.goto('https://www.taobao.com')
    await asyncio.sleep(100)

asyncio.get_event_loop().run_until_complete(main())

好馆铁,這里就是加了一個(gè) userDataDir 的屬性,值為 userdata锅睛,即當(dāng)前目錄的 userdata 文件夾埠巨。我們可以首先運(yùn)行一下,然后登錄一次淘寶现拒,這時(shí)候我們同時(shí)可以觀察到在當(dāng)前運(yùn)行目錄下又多了一個(gè) userdata 的文件夾辣垒,里面的結(jié)構(gòu)是這樣子的:

[圖片上傳失敗...(image-4a5a94-1587565252771)]

具體的介紹可以看官方的一些說(shuō)明,如:https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md印蔬,這里面介紹了 userdatadir 的相關(guān)內(nèi)容勋桶。

再次運(yùn)行上面的代碼,這時(shí)候可以發(fā)現(xiàn)現(xiàn)在就已經(jīng)是登錄狀態(tài)了侥猬,不需要再次登錄了例驹,這樣就成功跳過(guò)了登錄的流程。當(dāng)然可能時(shí)間太久了退唠,Cookies 都過(guò)期了鹃锈,那還是需要登錄的。

(2)執(zhí)行js程序:拖動(dòng)滾輪瞧预。調(diào)用evaluate方法屎债。

import asyncio
from pyppeteer import launch

width, height = 1366, 768

async def main():
    browser = await launch(headless=False)
    page = await browser.newPage()
    await page.setViewport({'width': width, 'height': height})
    await page.goto('https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action=')
    await asyncio.sleep(3)
    #evaluate可以返回js程序的返回值
    dimensions = await page.evaluate('window.scrollTo(0,document.body.scrollHeight)')
    await asyncio.sleep(3)
    print(dimensions)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

5. 設(shè)置userAgent設(shè)置代理IP

browser = await launch({'headless': True, 'timeout': 500, 'args': ['--disable-extensions', 
                                                                   '--hide-scrollbars',
                                                                   '--disable-bundled-ppapi-flash',
                                                                   '--mute-audio', 
                                                                   '--no-sandbox',
                                                                   '--disable-setuid-sandbox',
                                                                   '--disable-gpu', 
                                                                   '--proxy-server={}'.format(get_ip()),
                                                                   ], })
page = await browser.newPage()
await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36")
await page.setViewport({'width': 1000,'height': 3480,})

6.模擬操作

pyppeteer提供了Keyboard和Mouse兩個(gè)類(lèi)來(lái)實(shí)現(xiàn)模擬操作垢油,前者是用來(lái)實(shí)現(xiàn)鍵盤(pán)模擬盆驹,后者實(shí)現(xiàn)鼠標(biāo)模擬(還有其他觸屏之類(lèi)的就不說(shuō)了)。

主要來(lái)說(shuō)說(shuō)輸入和點(diǎn)擊:

import os

os.environ['PYPPETEER_HOME'] = 'D:\Program Files'
import asyncio
from pyppeteer import launch


async def main():
    browser = await launch(headless=False, args=['--disable-infobars'])
    page = await browser.newPage()
    await page.goto('https://h5.ele.me/login/')
    await page.type('form section input', '12345678999')    # 模擬鍵盤(pán)輸入手機(jī)號(hào)
    await page.click('form section button')                 # 模擬鼠標(biāo)點(diǎn)擊獲取驗(yàn)證碼
    await asyncio.sleep(200)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main()) 

上面的模擬操作中滩愁,無(wú)論是模擬鍵盤(pán)輸入還是鼠標(biāo)點(diǎn)擊定位都是通過(guò)css選擇器召娜,似乎pyppeteer的type和click直接模擬操作定位都只能通過(guò)css選擇器(或者是我在官方文檔中沒(méi)找到方法),當(dāng)然惊楼,要間接通過(guò)xpath先定位玖瘸,然后再模擬操作也是可以的秸讹。下一小節(jié)中模擬登陸外賣(mài)平臺(tái)就是用這種方法,不過(guò)雅倒,這種方法要麻煩一些璃诀,不推薦。

7. 節(jié)點(diǎn)交互

import asyncio
from pyppeteer import launch


async def main():
    # headless參數(shù)設(shè)為False蔑匣,則變成有頭模式
    browser = await launch(
        headless=False
    )
    page = await browser.newPage()

    await page.setViewport(viewport={'width': 1280, 'height': 800})  # 設(shè)置頁(yè)面視圖大小
    await page.goto('https://www.baidu.com/')
    # 節(jié)點(diǎn)交互
    await page.type('#kw', '周杰倫', {'delay': 1000})      # id選擇器
    await asyncio.sleep(3)
    await page.click('#su')
    await asyncio.sleep(3)

    # 使用選擇器選中標(biāo)簽進(jìn)行點(diǎn)擊
    alist = await page.querySelectorAll('.s_tab_inner > a')
    a = alist[3]
    await a.click()
    await asyncio.sleep(3)
    await browser.close()
asyncio.get_event_loop().run_until_complete(main())

8. 執(zhí)行自定義js---

注入攔截和篩選請(qǐng)求和返回劣欢。page.on監(jiān)聽(tīng)請(qǐng)求與響應(yīng), 并對(duì)請(qǐng)求和響應(yīng)進(jìn)行修改和過(guò)濾

page.on(event, function) ,指定監(jiān)聽(tīng)事件, 與處理函數(shù)
例如: page.on('request', intercept_response) 
?
# 請(qǐng)求處理函數(shù)
async def request_check(req):
        '''請(qǐng)求過(guò)濾'''
        if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
            await req.abort()
        else:
            await req.continue_()
?
# 響應(yīng)處理函數(shù)
async def intercept_response(res):
        resourceType = res.request.resourceType
        if resourceType in ['image', 'media']:
            resp = await res.text()
            print(resp)

下面這個(gè)例子經(jīng)常用來(lái):

  • 加快網(wǎng)頁(yè)加載速度
  • 快速篩選數(shù)據(jù)api接口

做新聞爬蟲(chóng)的時(shí)候,遇到網(wǎng)頁(yè)有視頻其實(shí)挺尷尬的裁良,首先如果加載視頻會(huì)導(dǎo)致打開(kāi)網(wǎng)頁(yè)比較慢凿将,有時(shí)甚至?xí)?dǎo)致瀏覽器超時(shí)崩潰,其次是視頻的加載可能不同時(shí)帶入一些廣告的超鏈接价脾,對(duì)于提取新聞內(nèi)容會(huì)造成干擾牧抵。
通過(guò)page.setRequestInterception參數(shù)開(kāi)啟注入。

import asyncio
from pyppeteer import launch

async def inject_request(req):
    """
    resourceType:
        document, stylesheet, image, media, font, script, texttrack, xhr, fetch, eventsource, websocket, manifest, other
    """
    if req.resourceType in ['media','image']:
        await req.abort()
    else:
        await req.continue_()

async def inject_response(res):

    if res.request.resourceType in ['xhr']:
        print(res.request.url)
    
async def main():
    browser = await launch({'headless':False})
    page = await browser.newPage()
    await page.setRequestInterception(True)
    page.on('request', inject_request)
    page.on('response',inject_response)
    await page.goto('https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0')
    await page.waitFor(5 * 1000)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())
image

輸出:

https://m.douban.com/j/puppy/frodo_landing?include=anony_home
https://movie.douban.com/j/search_tags?type=movie&source=
https://movie.douban.com/j/search_tags?type=movie&tag=%E7%83%AD%E9%97%A8&source=
https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0
https://movie.douban.com/j/subject_abstract?subject_id=24389792
https://movie.douban.com/j/subject_abstract?subject_id=27119724

先分析inject_request部分:不請(qǐng)求圖片和媒體資源侨把。

async def inject_request(req):
    if req.resourceType in ['media','image']:
        await req.abort()
    else:
        await req.continue_()

一般用得比較多的是一個(gè)屬性兩個(gè)方法:

一個(gè)屬性:
  resourceType,表示請(qǐng)求的資源類(lèi)型犀变,有document, stylesheet, image, media, font, script, texttrack, xhr, fetch, eventsource, websocket, manifest, other(加粗的是比較常用的資源類(lèi)型)

兩個(gè)方法:
  abort(),跳過(guò)當(dāng)前請(qǐng)求
  continue_(),繼續(xù)當(dāng)前請(qǐng)求

inject_response部分

async def inject_response(res):

    if res.request.resourceType in ['xhr']:
        print(res.request.url)

一般js動(dòng)態(tài)加載的數(shù)據(jù)連接在xhr資源,所以我這里把網(wǎng)頁(yè)請(qǐng)求的xhr資源都打印出來(lái)秋柄,如果這里沒(méi)有數(shù)據(jù)連接获枝,那就是在document里面了,比F12清晰一點(diǎn)骇笔。

9. 獲取瀏覽器依據(jù)加載的圖片內(nèi)容, Selenium與Pyppeteer相同


執(zhí)行JS, 返回圖片的二進(jìn)制的Base64編碼, 參照: https://www.w3ctech.com/topic/767
'''
() => {
var img = document.getElementById("%s");
var canvas = document.createElement("canvas");
canvas.width = %s;
canvas.height = %s;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");}''' % (id, width, height)

10. 切換瀏覽器的tag

# 在Pyppeteer中每一個(gè)標(biāo)簽頁(yè)就是一個(gè)page對(duì)象, 切換page對(duì)象就是切換標(biāo)簽頁(yè)
for _page in await browser.pages() :
   if _page != page:
      await _page.close()

三省店、案例

綜合應(yīng)用:爬取頭條和網(wǎng)易的新聞標(biāo)題

import asyncio
from pyppeteer import launch
from lxml import etree

async def main():
    browser = await launch(headless=False)                              # headless參數(shù)設(shè)為False,則變成有頭模式
    page1 = await browser.newPage()
    await page1.setViewport(viewport={'width': 1280, 'height': 800})    # 設(shè)置頁(yè)面視圖大小
    await page1.goto('https://www.toutiao.com/')
    await asyncio.sleep(2)    
    page_text = await page1.content()                                   # 打印頁(yè)面文本
    
    page2 = await browser.newPage()
    await page2.setViewport(viewport={'width': 1280, 'height': 800})
    await page2.goto('https://news.163.com/domestic/')
    await page2.evaluate('window.scrollTo(0,document.body.scrollHeight)')
    page_text1 = await page2.content()
    await browser.close()
    return {'wangyi':page_text1,'toutiao':page_text}
    
def parse(task):
    content_dic = task.result()
    wangyi = content_dic['wangyi']
    toutiao = content_dic['toutiao']
    tree = etree.HTML(toutiao)
    a_list = tree.xpath('//div[@class="title-box"]/a')
    for a in a_list:
        title = a.xpath('./text()')[0]
        print('toutiao:',title)
    tree = etree.HTML(wangyi)
    div_list = tree.xpath('//div[@class="data_row news_article clearfix "]')
    print(len(div_list))
    
    for div in div_list:
        title = div.xpath('.//div[@class="news_title"]/h3/a/text()')[0]
        print('wangyi:',title)
        
tasks = []
task1 = asyncio.ensure_future(main())
task1.add_done_callback(parse)
tasks.append(task1)
asyncio.get_event_loop().run_until_complete(asyncio.wait(tasks))

爬取結(jié)果:
toutiao: 「央視快評(píng)」堅(jiān)守初心 為國(guó)奉獻(xiàn)
toutiao: 南航一A380客機(jī)北京降落時(shí)遭冰雹風(fēng)擋現(xiàn)裂痕 已平安降落無(wú)人受傷
toutiao: 美國(guó)正開(kāi)啟第二戰(zhàn)場(chǎng):圍獵中國(guó)高科技企業(yè) |“雙線作戰(zhàn)”戰(zhàn)略意圖
toutiao: 云南省陸良縣:農(nóng)民給供銷(xiāo)社打“白條”
toutiao: 媒體:90后副縣長(zhǎng)若非靠拼爹上位 需拿出業(yè)績(jī)服眾
toutiao: 南航A380飛北京客機(jī)遭遇冰雹襲擊笨触,擋風(fēng)玻璃全碎
toutiao: 秘魯北部發(fā)生7.8級(jí)地震
toutiao: 1958年萨西,由捷克斯洛伐克援建的北京電影洗印廠曾為全國(guó)行業(yè)的老大
toutiao: 一箭60星,發(fā)射成功旭旭!馬斯克衛(wèi)星互聯(lián)網(wǎng)計(jì)劃啟動(dòng)
69
wangyi: 中美經(jīng)貿(mào)摩擦背后:有人在干谎脯,有人在騙
wangyi: 華為回應(yīng)個(gè)別標(biāo)準(zhǔn)組織撤銷(xiāo)資格:產(chǎn)品服務(wù)不受影響
wangyi: 隔空約架?中方主播劉欣23年前就贏得國(guó)際演講比賽
wangyi: 從錢(qián)學(xué)森到任正非 中國(guó)教育有多少底氣應(yīng)對(duì)全球化
wangyi: 2個(gè)月內(nèi)二度履新 35歲清華博士任安徽省直單位領(lǐng)導(dǎo)
wangyi: 南陽(yáng)“水氫發(fā)動(dòng)機(jī)汽車(chē)”引熱議 官方回應(yīng)四大疑問(wèn)
wangyi: 31歲北大博士躋身縣委常委 主筆6萬(wàn)字全縣發(fā)展規(guī)劃
wangyi: 干部退休15年后投案自首 省委巡視辦:頭一次碰到
wangyi: 臺(tái)灣被標(biāo)注"中國(guó)臺(tái)灣省" 臺(tái)外事部門(mén)要求更正被拒
wangyi: 190天3次現(xiàn)場(chǎng)辦公!南陽(yáng)領(lǐng)導(dǎo)為何鐘愛(ài)青年汽車(chē)項(xiàng)目

2. 淘寶登錄

import time
import random
import asyncio
import pyppeteer


class LoginTaoBao:
    """
    類(lèi)異步
    """
    pyppeteer.DEBUG = True
    page = None

    async def _injection_js(self):
        """注入js
        """
        await self.page.evaluate('''() =>{
                   Object.defineProperties(navigator,{
                     webdriver:{
                       get: () => false
                     }
                   })
                }''')

    async def _init(self):
        """初始化瀏覽器
        """
        browser = await pyppeteer.launch({'headless': False,
                                          'args': [
                                              '--window-size={1300},{600}'
                                              '--disable-extensions',
                                              '--hide-scrollbars',
                                              '--disable-bundled-ppapi-flash',
                                              '--mute-audio',
                                              '--no-sandbox',
                                              '--disable-setuid-sandbox',
                                              '--disable-gpu',
                                          ],
                                          'dumpio': True,
                                          })
        self.page = await browser.newPage()
        # 設(shè)置瀏覽器頭部
        await self.page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                                     '(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299')
        # 設(shè)置瀏覽器大小
        await self.page.setViewport({'width': 1200, 'height': 600})

    async def get_cookie(self):
        cookies_list = await self.page.cookies()
        cookies = ''
        for cookie in cookies_list:
            str_cookie = '{0}={1};'
            str_cookie = str_cookie.format(cookie.get('name'), cookie.get('value'))
            cookies += str_cookie
        print(cookies)
        return cookies

    async def mouse_slider(self):
        """滑動(dòng)滑塊
        """
        await asyncio.sleep(3)
        try:
            await self.page.hover('#nc_1_n1z')           
            await self.page.mouse.down()                        # 鼠標(biāo)按下按鈕            
            await self.page.mouse.move(2000, 0, {'steps': 30})  # 移動(dòng)鼠標(biāo)            
            await self.page.mouse.up()                          # 松開(kāi)鼠標(biāo)
            await asyncio.sleep(2)
        except Exception as e:
            print(e, '      :錯(cuò)誤')
            return None
        else:
            await asyncio.sleep(3)
            # 獲取元素內(nèi)容
            slider_again = await self.page.querySelectorEval('#nc_1__scale_text', 'node => node.textContent')
            if slider_again != '驗(yàn)證通過(guò)':
                return None
            else:
                print('驗(yàn)證通過(guò)')
                return True

    async def main(self, username_, pwd_):
        """登陸
        """        
        await self._init()                                      # 初始化瀏覽器       
        await self.page.goto('https://login.taobao.com')        # 打開(kāi)淘寶登陸頁(yè)面        
        await self._injection_js()                              # 注入js        
        await self.page.click('div.login-switch')               # 點(diǎn)擊密碼登陸按鈕
        time.sleep(random.random() * 2)
        
        await self.page.type('#TPL_username_1', username_, {'delay': random.randint(100, 151) - 50})    # 輸入用戶名        
        await self.page.type('#TPL_password_1', pwd_, {'delay': random.randint(100, 151)})              # 輸入密碼
        time.sleep(random.random() * 2)
        
        slider = await self.page.querySelector('#nc_1__scale_text')             # 獲取滑塊元素
        if slider:
            print('有滑塊')
            # 移動(dòng)滑塊
            flag = await self.mouse_slider()
            if not flag:
                print('滑動(dòng)滑塊失敗')
                return None
            time.sleep(random.random() + 1.5)
            # 點(diǎn)擊登陸
            print('點(diǎn)擊登陸')
            await self.page.click('#J_SubmitStatic')
            await asyncio.sleep(100)
        else:
            print('沒(méi)滑塊')
            # 按下回車(chē)
            await self.page.keyboard.press('Enter')


if __name__ == '__main__':
    username = input('淘寶用戶名')
    pwd = input('密碼')
    login = LoginTaoBao()
    loop = asyncio.get_event_loop()
    task = asyncio.ensure_future(login.main(username, pwd))
    loop.run_until_complete(task)

3. 某電商平臺(tái)模擬登陸

我曾經(jīng)用selenium + chrome 實(shí)現(xiàn)了模擬登陸這個(gè)電商平臺(tái)持寄,但是實(shí)在是有些麻煩源梭,繞過(guò)對(duì)webdriver的檢測(cè)不難,但是稍味,通過(guò)webdriver對(duì)瀏覽器的每一步操作都會(huì)留下特殊的痕跡废麻,會(huì)被平臺(tái)識(shí)別,這個(gè)必須通過(guò)重新編譯chrome的webdriver才能實(shí)現(xiàn)模庐,麻煩得讓人想哭烛愧。不說(shuō)了,都是淚,下面直接上用pyppeteer實(shí)現(xiàn)的代碼:

import os
os.environ['PYPPETEER_HOME'] = 'D:\Program Files'
import asyncio
from pyppeteer import launch
 
def screen_size():
    """使用tkinter獲取屏幕大小"""
    import tkinter
    tk = tkinter.Tk()
    width = tk.winfo_screenwidth()
    height = tk.winfo_screenheight()
    tk.quit()
    return width, height
 
 
async def main():
    js1 = '''() =>{
 
        Object.defineProperties(navigator,{
        webdriver:{
            get: () => false
            }
        })
    }'''
 
    js2 = '''() => {
        alert (
            window.navigator.webdriver
        )
    }'''
    browser = await launch({'headless':False, 'args':['--no-sandbox'],})
 
    page = await browser.newPage()
    width, height = screen_size()
    await page.setViewport({ # 最大化窗口
        "width": width,
        "height": height
    })
    await page.goto('https://h5.ele.me/login/')
    await page.evaluate(js1)
    await page.evaluate(js2)
    input_sjh = await page.xpath('//form/section[1]/input[1]')
    click_yzm = await page.xpath('//form/section[1]/button[1]')
    input_yzm = await page.xpath('//form/section[2]/input[1]')
    but = await page.xpath('//form/section[2]/input[1]')
    print(input_sjh)
    await input_sjh[0].type('*****手機(jī)號(hào)********')
    await click_yzm[0].click()
    ya = input('請(qǐng)輸入驗(yàn)證碼:')
    await input_yzm[0].type(str(ya))
    await but[0].click()
    await asyncio.sleep(3)
    await page.goto('https://www.ele.me/home/')
    await asyncio.sleep(100)
    await browser.close()
 
asyncio.get_event_loop().run_until_complete(main())

登錄時(shí)怜姿,由于等待時(shí)間過(guò)長(zhǎng)(我猜的)導(dǎo)致出現(xiàn)以下錯(cuò)誤:

pyppeteer.errors.NetworkError: Protocol Error (Runtime.callFunctionOn): Session closed. Most likely the page has been closed.

在github上找到了解決方法慎冤,似乎只能改源碼,找到pyppeteer包下的connection.py模塊沧卢,在其43行和44行改為下面這樣:

self._ws = websockets.client.connect(
# self._url, max_size=None, loop=self._loop)
self._url, max_size=None, loop=self._loop, ping_interval=None, ping_timeout=None)

再次運(yùn)行就沒(méi)問(wèn)題了蚁堤。可以成功繞過(guò)官方對(duì)webdriver的檢測(cè)但狭,登錄成功披诗,諸位可以自己嘗試一下。

四立磁、爬取京東商城

from pyppeteer import launch
import asyncio


def screen_size():
    """使用tkinter獲取屏幕大小"""
    import tkinter
    tk = tkinter.Tk()
    width = tk.winfo_screenwidth()
    height = tk.winfo_screenheight()
    tk.quit()
    return width, height


async def main(url):
    browser = await launch({'headless': False, 'args': ['--no-sandbox'], })
    # browser = await launch({'args': ['--no-sandbox'], })
    page = await browser.newPage()
    width, height = screen_size()
    await page.setViewport(viewport={"width": width, "height": height})
    await page.setJavaScriptEnabled(enabled=True)
    await page.setUserAgent(
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
        '(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
    )
    await page.goto(url)
    await asyncio.sleep(2*1000)
    await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')
    # await asyncio.sleep(1)

    # content = await page.content()
    li_list = await page.xpath('//*[@id="J_goodsList"]/ul/li')
    print(li_list)
    '''
    [
    <pyppeteer.element_handle.ElementHandle object at 0x000000000B994128>, 
    <pyppeteer.element_handle.ElementHandle object at 0x000000000B8CDD68>, 
    ...一個(gè)個(gè)pyppeteer對(duì)象]
    '''
    item_list = []
    for li in li_list:
        a = await li.xpath('.//div[@class="p-img"]/a')
        detail_url = await (await a[0].getProperty("href")).jsonValue()
        promo_words = await (await a[0].getProperty("title")).jsonValue()
        a_ = await li.xpath('.//div[@class="p-commit"]/strong/a')
        p_commit = await (await a_[0].getProperty("textContent")).jsonValue()
        i = await li.xpath('./div/div[3]/strong/i')
        price = await (await i[0].getProperty("textContent")).jsonValue()
        em = await li.xpath('./div/div[4]/a/em')
        title = await (await em[0].getProperty("textContent")).jsonValue()
        item = {
            "title": title,
            "detail_url": detail_url,
            "promo_words": promo_words,
            'p_commit': p_commit,
            'price': price
        }
        item_list.append(item)
        # print(item)
        # break
    # print(content)

    await page_close(browser)
    return item_list


async def page_close(browser):
    for _page in await browser.pages():
        await _page.close()
    await browser.close()


msg = "手機(jī)"
url = "https://search.jd.com/Search?keyword={}&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq={}&cid2=653&cid3=655&page={}"

task_list = []
for i in range(1, 2):
    page = i * 2 - 1
    url = url.format(msg, msg, page)
    task_list.append(main(url))

loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*task_list))
# print(results, len(results))
for i in results:
    print(i, len(i))

print('*' * 100)
# soup = BeautifulSoup(content, 'lxml')
# div = soup.find('div', id='J_goodsList')
# for i, li in enumerate(div.find_all('li', class_='gl-item')):
#     if li.select('.p-img a'):
#         print(li.select('.p-img a')[0]['href'], i)
#         print(li.select('.p-price i')[0].get_text(), i)
#         print(li.select('.p-name em')[0].text, i)
#     else:
#         print("#" * 200)
#         print(li)

image

detail_url = await (await a[0].getProperty("href")).jsonValue()    # 取屬性值
i = await li.xpath('./div/div[3]/strong/i')
price = await (await i[0].getProperty("textContent")).jsonValue()   # 取文本
em = await li.xpath('./div/div[4]/a/em')
title = await (await em[0].getProperty("textContent")).jsonValue()

REF
http://www.reibang.com/p/84f39941f3ea
https://blog.csdn.net/freeking101/article/details/93331204
https://www.cnblogs.com/baihuitestsoftware/p/10531462.html

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word;">pyppeteer github 地址:https://github.com/miyakogi/pyppeteer
pyppeteer 英文文檔地址:https://miyakogi.github.io/pyppeteer/
pyppeteer 官方文檔 API Reference :https://miyakogi.github.io/pyppeteer/reference.html
puppeteer( Nodejs 版 selenium )快速入門(mén):https://blog.csdn.net/freeking101/article/details/91542887
爬蟲(chóng)界又出神器|一款比selenium更高效的利器:https://blog.csdn.net/chen801090/article/details/93216278
python爬蟲(chóng)利器 pyppeteer(模擬瀏覽器) 實(shí)戰(zhàn):https://blog.csdn.net/xiaoming0018/article/details/89841728</pre>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呈队,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子唱歧,更是在濱河造成了極大的恐慌宪摧,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迈喉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡温圆,警方通過(guò)查閱死者的電腦和手機(jī)挨摸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)岁歉,“玉大人得运,你說(shuō)我怎么就攤上這事」疲” “怎么了熔掺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)非剃。 經(jīng)常有香客問(wèn)我置逻,道長(zhǎng),這世上最難降的妖魔是什么备绽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任券坞,我火速辦了婚禮,結(jié)果婚禮上肺素,老公的妹妹穿的比我還像新娘恨锚。我一直安慰自己,他們只是感情好倍靡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布猴伶。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪他挎。 梳的紋絲不亂的頭發(fā)上筝尾,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音雇盖,去河邊找鬼忿等。 笑死,一個(gè)胖子當(dāng)著我的面吹牛崔挖,可吹牛的內(nèi)容都是我干的贸街。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狸相,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼薛匪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起脓鹃,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逸尖,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瘸右,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體娇跟,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年太颤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苞俘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡龄章,死狀恐怖吃谣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情做裙,我是刑警寧澤岗憋,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站锚贱,受9級(jí)特大地震影響仔戈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拧廊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一杂穷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卦绣,春花似錦耐量、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)趴拧。三九已至,卻和暖如春山叮,著一層夾襖步出監(jiān)牢的瞬間著榴,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工屁倔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脑又,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓锐借,卻偏偏與公主長(zhǎng)得像问麸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钞翔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355