寫在前面
本篇博客只是為練習pyppeteer的用法,其中的實踐案例用其他的更簡單方法也可以實作,
最近也是看完了崔慶才爬蟲52講里面pyppeteer的部分,就想著實戰演練一遍(主要是里面的案例無法使用,哭唧唧),找了一下經常爬取的網站,例如淘寶,知網什么的,但是這些網站都需要登錄,難度偏大一點,就給否決掉了,最后選到了縱橫小說排行榜這個網站,因為這個網站沒有什么反爬,不需要登錄而且比較符合初始頁加詳情頁這樣常用的抓取資料的方法,
網站分析
初始串列頁的url

翻頁觀察

多翻幾個頁面之后,發現初始頁面的url規律十分明顯
http://www.zongheng.com/rank/details.html?rt=1&d=1&p={page}
詳情頁面的url

隨便多開幾個詳情頁,規律也都是一樣的
http://book.zongheng.com/book/{id}.html
實作思路
構造url串列請求初始頁面,獲取到詳情頁面的id,再請求詳情頁面,拿到想要的資料即可,
因為我們這邊用的是pyppeteer模擬瀏覽器進行請求的,沒有必要抓包分析了,直接分析elements拿到詳情頁面的url即可,

通過簡單分析過后,很容易就可以拿到詳情頁面的url,
之后我們請求詳情頁拿到想要的資料,因為這只是練習一下pyppeteer的使用,隨便抓點資料即可,

獲取這個些資料的方法也很簡單,直接分析elements就可以了,在這里我就不詳細的分析了,最后再把資料存盤到MySQL就可以了,
代碼實作
附帶上源代碼,注釋很清晰,和selenium做了比較,方便對比學習,
from pyppeteer import launch
import asyncio
import logging
from pyppeteer.errors import TimeoutError
import pymysql
logging.basicConfig(level=logging.INFO,format="%(asctime)s-%(levelname)s:%(message)s")
INDEX_URL = "http://www.zongheng.com/rank/details.html?rt=1&d=1&p={page}"
TIMEOUT = 10
TOTAL_PAGE = 10
WINDOW_WIDTH, WINDOW_HEIGHT = 1366,768
HEADLESS = False
browser, tab = None, None
# 初始化資料庫
db = pymysql.connect(host='localhost', user='root', database='zongheng_top100', autocommit=True)
# 使用 cursor() 方法創建一個游標物件 cursor
cursor = db.cursor()
# 使用 execute() 方法執行 SQL,如果表存在則洗掉
cursor.execute("DROP TABLE IF EXISTS DATA")
# 使用預處理陳述句創建表
sql = """CREATE TABLE DATA (
NAME CHAR(20) NOT NULL,
AU_NAME CHAR(20),
BOOK_LABEL CHAR(100),
NUM CHAR(20),
RECOMMEND CHAR(20),
TOTAL_HITS CHAR(20),
RECOMMEND_WEEK CHAR(20),
BRIEF_INTRODUCTION VARCHAR(2000),
URL CHAR(50)
)"""
cursor.execute(sql)
# 初始化pyppeteer
async def init():
global browser, tab
# browser物件是用來定義瀏覽器視窗 用法相當于selenium里面的option
browser = await launch(headless=HEADLESS,args=['--disable-infobars',f'--window-size={WINDOW_WIDTH},{WINDOW_HEIGHT}'])
# 開啟一個頁面
tab = await browser.newPage()
await tab.setViewport({'width':WINDOW_WIDTH,'height':WINDOW_HEIGHT})
# 定義通用的爬取方法
async def scrape_page(url,selector):
logging.info('scraping %s', url)
try:
# tab.goto(url)相當于selenium driver.get(url)
await tab.goto(url)
# waitForSelector相當于selenium里面顯性等待
await tab.waitForSelector(selector,option={
'timeout': TIMEOUT*10000
})
except TimeoutError:
logging.error('error occurred while scraping %s', url, exc_info=True)
# 定義爬取串列頁的方法
async def scrape_index(page):
url = INDEX_URL.format(page=page)
await scrape_page(url, '.rankpage_box')
# 定義決議串列頁的方法
async def parse_index():
"""
querySelectorAllEval方法接受兩個引數
第一個引數是selector,代表要選擇的節點對應的CSS選擇器
第二個引數pageFunction,代表要執行的JavaScript方法
"""
# querySelectorAllEval相當與find_elements_by_
# nodes=>nodes.map(node=>node.href)相當與selenium里面遍歷elements并執行.get_attribute('href')方法
return await tab.querySelectorAllEval('.rank_d_book_img a','nodes=>nodes.map(node=>node.href)')
# 定義訪問詳情頁的方法
async def scrape_detail(url):
await scrape_page(url, '.book-top')
# 決議詳情頁拿到資料
async def parse_detail():
url = tab.url
# node=>node.innerText相當于selenium里面的element.text
name = await tab.querySelectorEval('.book-name','node=>node.innerText')
au_name = await tab.querySelectorEval('.au-name a','node=>node.innerText')
# nodes=>nodes.map(node=>node.innerText)相當與selenium里面遍歷elements并執行.text方法
book_label = ",".join(await tab.querySelectorAllEval('.book-label a',"nodes=>nodes.map(node=>node.innerText)"))
num = await tab.querySelectorEval('.nums span:nth-of-type(1) i','node=>node.innerText')
recommend = await tab.querySelectorEval('.nums span:nth-of-type(2) i','node=>node.innerText')
total_hits = await tab.querySelectorEval('.nums span:nth-of-type(3) i','node=>node.innerText')
recommend_week = await tab.querySelectorEval('.nums span:nth-of-type(4) i','node=>node.innerText')
brief_introduction = await tab.querySelectorEval('.book-dec p','node=>node.innerText')
data = (name,au_name,book_label,num,recommend,total_hits,recommend_week,brief_introduction,url)
return data
# 存盤資料到mysql
async def save_data(detail_data):
sql = "INSERT INTO DATA" \
"(NAME, AU_NAME, BOOK_LABEL, NUM, RECOMMEND, TOTAL_HITS, RECOMMEND_WEEK, BRIEF_INTRODUCTION, URL) " \
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"
# 執行sql陳述句
cursor.execute(sql,detail_data)
# 執行sql陳述句
db.commit()
async def main():
await init()
try:
for page in range(1,TOTAL_PAGE+1):
await scrape_index(page)
detail_urls = await parse_index()
logging.info('detail_urls %s',detail_urls)
for detail_url in detail_urls:
await scrape_detail(detail_url)
detail_data = await parse_detail()
logging.info('data %s',detail_data)
await save_data(detail_data)
db.close()
finally:
await browser.close()
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
再添加一點pyppeteer常用的方法:
滑鼠點擊操作
# 第一個引數表示選擇器,第二個引數操作的方法
await page.click('.item .name',options={
'button':'right',
'clickCount':1, # 1or2 單擊或雙擊
'delay':3000, # 毫秒
})
文本輸入操作
# 第一個引數選擇器,第二個引數輸入的文字
await page.type('#q','iPad')
想要了解更多的操作直接去看官方檔案吧
總結
pyppeteer可以實作異步爬取,速度上比selenium要快上很多,而且使用pyppeteer沒有必要專門下載chrome瀏覽器和chromedriver驅動,在某些特定的網站確實要比selenium好用,但pyppeteer提取資料,滑鼠操作等方法過于復雜,不如selenium簡單,不便于記憶,而且pyppeteer可以實作的方法selenium基本都可以實作,總之pyppeteer可以學習,但沒必要死記硬背其中的方法,用到的時候直接翻閱相關檔案即可,
附上
運行程序

速度確實要快很多,和requests直接爬取的速度基本一樣了!
PS:第一次運行pyppeteer程式會下載一個Chromium瀏覽器,速度慢一點,之后運行就不需要了,速度會快很多,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/257806.html
標籤:python
